static void emit_greeting(struct vsf_session* p_sess) { struct mystr str_log_line = INIT_MYSTR; /* Check for client limits (standalone mode only) */ if (tunable_max_clients > 0 && p_sess->num_clients > tunable_max_clients) { str_alloc_text(&str_log_line, "Connection refused: too many sessions."); vsf_log_line(p_sess, kVSFLogEntryConnection, &str_log_line); vsf_cmdio_write_noblock(p_sess, FTP_TOO_MANY_USERS, "There are too many connected users, please try later."); vsf_sysutil_exit(0); } if (tunable_max_per_ip > 0 && p_sess->num_this_ip > tunable_max_per_ip) { str_alloc_text(&str_log_line, "Connection refused: too many sessions for this address."); vsf_log_line(p_sess, kVSFLogEntryConnection, &str_log_line); vsf_cmdio_write_noblock(p_sess, FTP_IP_LIMIT, "There are too many connections from your internet address."); vsf_sysutil_exit(0); } if (!p_sess->tcp_wrapper_ok) { str_alloc_text(&str_log_line, "Connection refused: tcp_wrappers denial."); vsf_log_line(p_sess, kVSFLogEntryConnection, &str_log_line); vsf_cmdio_write_noblock(p_sess, FTP_IP_DENY, "Service not available."); vsf_sysutil_exit(0); } vsf_log_line(p_sess, kVSFLogEntryConnection, &str_log_line); if (!str_isempty(&p_sess->banner_str)) { vsf_banner_write(p_sess, &p_sess->banner_str, FTP_GREET); str_free(&p_sess->banner_str); vsf_cmdio_write(p_sess, FTP_GREET, ""); } else if (tunable_ftpd_banner == 0) { vsf_cmdio_write(p_sess, FTP_GREET, "(vsFTPd " VSF_VERSION ")"); } else { vsf_cmdio_write(p_sess, FTP_GREET, tunable_ftpd_banner); } }
static void handle_alarm_timeout(void* p_private) { struct vsf_session* p_sess = (struct vsf_session*) p_private; vsf_cmdio_write_noblock(p_sess, FTP_IDLE_TIMEOUT, "Timeout."); vsf_sysutil_exit(0); }
void vsf_two_process_login(struct vsf_session* p_sess, const struct mystr* p_pass_str) { char result; priv_sock_send_cmd(p_sess, PRIV_SOCK_LOGIN); priv_sock_send_str(p_sess, &p_sess->user_str); priv_sock_send_str(p_sess, p_pass_str); result = priv_sock_get_result(p_sess); if (result == PRIV_SOCK_RESULT_OK) { /* Miracle. We don't emit the success message here. That is left to * process_post_login(). * Exit normally, parent will wait for this and launch new child */ vsf_sysutil_exit(0); } else if (result == PRIV_SOCK_RESULT_BAD) { /* Continue the processing loop.. */ return; } else { die("priv_sock_get_result"); } }
void handle_auth(struct vsf_session* p_sess) { str_upper(&p_sess->ftp_arg_str); if (str_equal_text(&p_sess->ftp_arg_str, "TLS") || str_equal_text(&p_sess->ftp_arg_str, "TLS-C") || str_equal_text(&p_sess->ftp_arg_str, "SSL") || str_equal_text(&p_sess->ftp_arg_str, "TLS-P")) { vsf_cmdio_write(p_sess, FTP_AUTHOK, "Proceed with negotiation."); if (!ssl_session_init(p_sess)) { struct mystr err_str = INIT_MYSTR; str_alloc_text(&err_str, "Negotiation failed: "); str_append_text(&err_str, get_ssl_error()); vsf_cmdio_write_str(p_sess, FTP_TLS_FAIL, &err_str); vsf_sysutil_exit(0); } p_sess->control_use_ssl = 1; if (str_equal_text(&p_sess->ftp_arg_str, "SSL") || str_equal_text(&p_sess->ftp_arg_str, "TLS-P")) { p_sess->data_use_ssl = 1; } } else { vsf_cmdio_write(p_sess, FTP_BADAUTH, "Unknown AUTH type."); } }
static void parse_username_password(struct vsf_session* p_sess) { while (1) { /* Kitsune: update point */ kitsune_update("prelogin.c"); /**DSU updatepoint */ /* Kitsune */ vsf_sysutil_kitsune_set_update_point("prelogin.c"); vsf_cmdio_get_cmd_and_arg(p_sess, &p_sess->ftp_cmd_str, &p_sess->ftp_arg_str, 1); if (str_equal_text(&p_sess->ftp_cmd_str, "USER")) { handle_user_command(p_sess); } else if (str_equal_text(&p_sess->ftp_cmd_str, "PASS")) { handle_pass_command(p_sess); } else if (str_equal_text(&p_sess->ftp_cmd_str, "QUIT")) { vsf_cmdio_write(p_sess, FTP_GOODBYE, "Goodbye."); vsf_sysutil_exit(0); } else { vsf_cmdio_write(p_sess, FTP_LOGINERR, "Please login with USER and PASS."); } } }
void vsf_exit(const char* p_text) { (void) vsf_sysutil_write_loop(VSFTP_COMMAND_FD, p_text, vsf_sysutil_strlen(p_text)); vsf_sysutil_exit(0); }
void init_connection(struct vsf_session* p_sess) { if (tunable_setproctitle_enable) { vsf_sysutil_setproctitle("not logged in"); } #ifdef VSF_BUILD_SQLITE /* In stealth mode we wont answer the connection unless the remote host * is known to our database. */ if (tunable_sqlite_enable && tunable_stealth_mode) { int retval = vsf_db_check_remote_host(&p_sess->remote_ip_str); if (retval == 0) { /* The IP check failed we drop the connection silently. */ vsf_sysutil_shutdown_read_failok(VSFTP_COMMAND_FD); vsf_sysutil_exit(0); } } #endif /* Before we talk to the remote, make sure an alarm is set up in case * writing the initial greetings should block. */ vsf_cmdio_set_alarm(p_sess); emit_greeting(p_sess); parse_username_password(p_sess); }
static void handle_pass_command(struct vsf_session* p_sess) { if (str_isempty(&p_sess->user_str)) { vsf_cmdio_write(p_sess, FTP_NEEDUSER, "Login with USER first."); return; } /* These login calls never return if successful */ if (tunable_one_process_model) { vsf_one_process_login(p_sess, &p_sess->ftp_arg_str); } else { vsf_two_process_login(p_sess, &p_sess->ftp_arg_str); } vsf_cmdio_write(p_sess, FTP_LOGINERR, "Login incorrect."); if (++p_sess->login_fails >= tunable_max_login_fails) { vsf_sysutil_exit(0); } str_empty(&p_sess->user_str); /* FALLTHRU if login fails */ }
static void check_login_fails(struct vsf_session* p_sess) { if (++p_sess->login_fails >= tunable_max_login_fails) { vsf_sysutil_exit(0); } }
void die(const char* p_text) { #ifdef DIE_DEBUG bug(p_text); #endif vsf_sysutil_exit(2); }
static void handle_sigterm(void* duff) { (void) duff; /* Blow away the connection to make sure no process lingers. */ vsf_sysutil_shutdown_failok(VSFTP_COMMAND_FD); /* Will call the registered exit function to clean up u/wtmp if needed. */ vsf_sysutil_exit(1); }
void bug(const char* p_text) { /* Rats. Try and write the reason to the network for diagnostics */ vsf_sysutil_activate_noblock(VSFTP_COMMAND_FD); (void) vsf_sysutil_write_loop(VSFTP_COMMAND_FD, FTP_500_STRING, FTP_500_SIZE); (void) vsf_sysutil_write_loop(VSFTP_COMMAND_FD, p_text, vsf_sysutil_strlen(p_text)); (void) vsf_sysutil_write_loop(VSFTP_COMMAND_FD, "\r\n", 2); vsf_sysutil_exit(2); }
void vsf_cmdio_write_exit(struct vsf_session* p_sess, int status, const char* p_text) { /* Unblock any readers on the dying control channel. This is needed for SSL * connections, where the SSL control channel slave is in a separate * process. */ vsf_sysutil_shutdown_read_failok(VSFTP_COMMAND_FD); ftp_write_text_common(p_sess, status, p_text, 1, ' '); vsf_sysutil_shutdown_failok(VSFTP_COMMAND_FD); vsf_sysutil_exit(0); }
static void handle_sigalrm(void* p_private) { struct vsf_session* p_sess = (struct vsf_session*) p_private; if (!p_sess->data_progress) { vsf_cmdio_write_noblock(p_sess, FTP_DATA_TIMEOUT, "Data timeout. Reconnect. Sorry."); vsf_sysutil_exit(0); } p_sess->data_progress = 0; start_data_alarm(p_sess); }
void ssl_control_handshake(struct vsf_session* p_sess) { if (!ssl_session_init(p_sess)) { struct mystr err_str = INIT_MYSTR; str_alloc_text(&err_str, "Negotiation failed: "); /* Technically, we shouldn't leak such detailed error messages. */ str_append_text(&err_str, get_ssl_error()); vsf_cmdio_write_str(p_sess, FTP_TLS_FAIL, &err_str); vsf_sysutil_exit(0); } p_sess->control_use_ssl = 1; }
static void parse_username_password(struct vsf_session* p_sess) { while (1) { vsf_cmdio_get_cmd_and_arg(p_sess, &p_sess->ftp_cmd_str, &p_sess->ftp_arg_str, 1); if (str_equal_text(&p_sess->ftp_cmd_str, "USER")) { handle_user_command(p_sess); } else if (str_equal_text(&p_sess->ftp_cmd_str, "PASS")) { handle_pass_command(p_sess); } 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, "FEAT")) { handle_feat(p_sess); } else if (tunable_ssl_enable && str_equal_text(&p_sess->ftp_cmd_str, "AUTH")) { handle_auth(p_sess); } else if (tunable_ssl_enable && str_equal_text(&p_sess->ftp_cmd_str, "PBSZ")) { handle_pbsz(p_sess); } else if (tunable_ssl_enable && str_equal_text(&p_sess->ftp_cmd_str, "PROT")) { handle_prot(p_sess); } else { vsf_cmdio_write(p_sess, FTP_LOGINERR, "Please login with USER and PASS."); } } }
static void handle_sigchld(int duff) { struct vsf_sysutil_wait_retval wait_retval = vsf_sysutil_wait(); (void) duff; /* Child died, so we'll do the same! Report it as an error unless the child * exited normally with zero exit code */ if (vsf_sysutil_retval_is_error(vsf_sysutil_wait_get_retval(&wait_retval)) || !vsf_sysutil_wait_exited_normally(&wait_retval) || vsf_sysutil_wait_get_exitcode(&wait_retval) != 0) { die("child died"); } else { vsf_sysutil_exit(0); } }
void vsf_two_process_login(struct vsf_session* p_sess, const struct mystr* p_pass_str) { char result; priv_sock_send_cmd(p_sess->child_fd, PRIV_SOCK_LOGIN); priv_sock_send_str(p_sess->child_fd, &p_sess->user_str); priv_sock_send_str(p_sess->child_fd, p_pass_str); priv_sock_send_int(p_sess->child_fd, p_sess->control_use_ssl); priv_sock_send_int(p_sess->child_fd, p_sess->data_use_ssl); result = priv_sock_get_result(p_sess->child_fd); if (result == PRIV_SOCK_RESULT_OK) { /* Miracle. We don't emit the success message here. That is left to * process_post_login(). * Exit normally, unless we are remaining as the SSL read / write child. */ if (!p_sess->control_use_ssl) { vsf_sysutil_exit(0); } else { vsf_sysutil_clear_alarm(); vsf_sysutil_close(p_sess->child_fd); if (tunable_setproctitle_enable) { vsf_sysutil_setproctitle("SSL handler"); } process_ssl_slave_req(p_sess); } /* NOTREACHED */ } else if (result == PRIV_SOCK_RESULT_BAD) { /* Continue the processing loop.. */ return; } else { die("priv_sock_get_result"); } }
static void minimize_privilege(struct vsf_session* p_sess) { /* So, we logged in and forked a totally unprivileged child. Our job * now is to minimize the privilege we need in order to act as a helper * to the child. * * In some happy circumstances, we can exit and be done with root * altogether. */ if (!p_sess->is_anonymous && tunable_session_support) { /* Need to hang around to update logs, utmp, wtmp etc. on logout. * Need to keep privs to do this. */ return; } if (!tunable_chown_uploads && !tunable_connect_from_port_20 && !tunable_max_per_ip && !tunable_max_clients) { /* Cool. We're outta here. */ vsf_sysutil_exit(0); } { unsigned int caps = 0; struct mystr user_str = INIT_MYSTR; struct mystr dir_str = INIT_MYSTR; str_alloc_text(&user_str, tunable_nopriv_user); str_alloc_text(&dir_str, tunable_secure_chroot_dir); if (tunable_chown_uploads) { caps |= kCapabilityCAP_CHOWN; } if (tunable_connect_from_port_20) { caps |= kCapabilityCAP_NET_BIND_SERVICE; } vsf_secutil_change_credentials(&user_str, &dir_str, 0, caps, VSF_SECUTIL_OPTION_CHROOT); str_free(&user_str); str_free(&dir_str); } }
static void handle_sigchld(int duff) { /* WARNING - async handler. Must not call anything which might have * re-entrancy issues */ struct vsf_sysutil_wait_retval wait_retval = vsf_sysutil_wait(); (void) duff; /* Child died, so we'll do the same! Report it as an error unless the child * exited normally with zero exit code */ if (vsf_sysutil_retval_is_error(vsf_sysutil_wait_get_retval(&wait_retval)) || !vsf_sysutil_wait_exited_normally(&wait_retval) || vsf_sysutil_wait_get_exitcode(&wait_retval) != 0) { die("child died"); } else { vsf_sysutil_exit(0); } }
void process_post_login(struct vsf_session* p_sess) { int retval; 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); } /* Kitsune */ vsf_sysutil_kitsune_set_update_point("postlogin.c"); if(!kitsune_is_updating()) { /* Handle any login message */ vsf_banner_dir_changed(p_sess, FTP_LOGINOK); vsf_cmdio_write(p_sess, FTP_LOGINOK, "Login successful. Have fun."); } else { /* Set sigchld function pointer (normally done in twoprocess.c) */ vsf_sysutil_default_sig(kVSFSysUtilSigCHLD); vsf_sysutil_install_async_sighandler(kVSFSysUtilSigCHLD, twoproc_handle_sigchld); } /* End Kitsune */ while(1) { if (tunable_setproctitle_enable) { vsf_sysutil_setproctitle("IDLE"); } /* Kitsune update point */ kitsune_update("postlogin.c"); /**DSU updatepoint */ /* 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); } 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 && str_equal_text(&p_sess->ftp_cmd_str, "PASV")) { handle_pasv(p_sess); } else if (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")) { vsf_cmdio_write(p_sess, FTP_BADHELP, "Sorry, I don't have help."); } else if (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 && 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 (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); } else if (str_equal_text(&p_sess->ftp_cmd_str, "ABOR")) { 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 (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")) { vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied."); } else { vsf_cmdio_write(p_sess, FTP_BADCMD, "Unknown command."); } } }
struct vsf_client_launch vsf_standalone_main(void) { struct vsf_sysutil_sockaddr* p_accept_addr = 0; int listen_sock = -1; int retval; s_ipaddr_size = vsf_sysutil_get_ipaddr_size(); if (tunable_listen && tunable_listen_ipv6) { die("run two copies of vsftpd for IPv4 and IPv6"); } if (tunable_background) { int forkret = vsf_sysutil_fork(); if (forkret > 0) { /* Parent, just exit */ vsf_sysutil_exit(0); } vsf_sysutil_make_session_leader(); } if (tunable_listen) { listen_sock = vsf_sysutil_get_ipv4_sock(); } else { listen_sock = vsf_sysutil_get_ipv6_sock(); } vsf_sysutil_activate_reuseaddr(listen_sock); s_p_ip_count_hash = hash_alloc(256, s_ipaddr_size, sizeof(unsigned int), hash_ip); s_p_pid_ip_hash = hash_alloc(256, sizeof(int), s_ipaddr_size, hash_pid); if (tunable_setproctitle_enable) { vsf_sysutil_setproctitle("LISTENER"); } vsf_sysutil_install_async_sighandler(kVSFSysUtilSigCHLD, handle_sigchld); vsf_sysutil_install_async_sighandler(kVSFSysUtilSigHUP, handle_sighup); if (tunable_listen) { struct vsf_sysutil_sockaddr* p_sockaddr = 0; vsf_sysutil_sockaddr_alloc_ipv4(&p_sockaddr); vsf_sysutil_sockaddr_set_port(p_sockaddr, tunable_listen_port); if (!tunable_listen_address) { vsf_sysutil_sockaddr_set_any(p_sockaddr); } else { if (!vsf_sysutil_inet_aton(tunable_listen_address, p_sockaddr)) { die2("bad listen_address: ", tunable_listen_address); } } retval = vsf_sysutil_bind(listen_sock, p_sockaddr); vsf_sysutil_free(p_sockaddr); if (vsf_sysutil_retval_is_error(retval)) { die("could not bind listening IPv4 socket"); } } else { struct vsf_sysutil_sockaddr* p_sockaddr = 0; vsf_sysutil_sockaddr_alloc_ipv6(&p_sockaddr); vsf_sysutil_sockaddr_set_port(p_sockaddr, tunable_listen_port); if (!tunable_listen_address6) { vsf_sysutil_sockaddr_set_any(p_sockaddr); } else { struct mystr addr_str = INIT_MYSTR; const unsigned char* p_raw_addr; str_alloc_text(&addr_str, tunable_listen_address6); p_raw_addr = vsf_sysutil_parse_ipv6(&addr_str); str_free(&addr_str); if (!p_raw_addr) { die2("bad listen_address6: ", tunable_listen_address6); } vsf_sysutil_sockaddr_set_ipv6addr(p_sockaddr, p_raw_addr); } retval = vsf_sysutil_bind(listen_sock, p_sockaddr); vsf_sysutil_free(p_sockaddr); if (vsf_sysutil_retval_is_error(retval)) { die("could not bind listening IPv6 socket"); } } vsf_sysutil_listen(listen_sock, VSFTP_LISTEN_BACKLOG); vsf_sysutil_sockaddr_alloc(&p_accept_addr); while (1) { struct vsf_client_launch child_info; void* p_raw_addr; int new_child; int new_client_sock; vsf_sysutil_unblock_sig(kVSFSysUtilSigCHLD); vsf_sysutil_unblock_sig(kVSFSysUtilSigHUP); new_client_sock = vsf_sysutil_accept_timeout( listen_sock, p_accept_addr, 0); vsf_sysutil_block_sig(kVSFSysUtilSigCHLD); vsf_sysutil_block_sig(kVSFSysUtilSigHUP); if (vsf_sysutil_retval_is_error(new_client_sock)) { continue; } ++s_children; child_info.num_children = s_children; child_info.num_this_ip = 0; p_raw_addr = vsf_sysutil_sockaddr_get_raw_addr(p_accept_addr); child_info.num_this_ip = handle_ip_count(p_raw_addr); new_child = vsf_sysutil_fork_failok(); if (new_child != 0) { /* Parent context */ vsf_sysutil_close(new_client_sock); if (new_child > 0) { hash_add_entry(s_p_pid_ip_hash, (void*)&new_child, p_raw_addr); } else { /* fork() failed, clear up! */ --s_children; drop_ip_count(p_raw_addr); } /* Fall through to while() loop and accept() again */ } else { /* Child context */ vsf_sysutil_close(listen_sock); prepare_child(new_client_sock); /* By returning here we "launch" the child process with the same * contract as xinetd would provide. */ return child_info; } } }
static void handle_user_command(struct vsf_session* p_sess) { /* SECURITY: If we're in anonymous only-mode, immediately reject * non-anonymous usernames in the hope we save passwords going plaintext * over the network */ int is_anon = 1; str_copy(&p_sess->user_str, &p_sess->ftp_arg_str); str_upper(&p_sess->ftp_arg_str); if (!str_equal_text(&p_sess->ftp_arg_str, "FTP") && !str_equal_text(&p_sess->ftp_arg_str, "ANONYMOUS")) { is_anon = 0; } if (!tunable_local_enable && !is_anon) { vsf_cmdio_write( p_sess, FTP_LOGINERR, "This FTP server is anonymous only."); str_empty(&p_sess->user_str); return; } if (is_anon && p_sess->control_use_ssl && !tunable_allow_anon_ssl && !tunable_force_anon_logins_ssl) { vsf_cmdio_write( p_sess, FTP_LOGINERR, "Anonymous sessions may not use encryption."); str_empty(&p_sess->user_str); return; } if (tunable_ssl_enable && !is_anon && !p_sess->control_use_ssl && tunable_force_local_logins_ssl) { vsf_cmdio_write( p_sess, FTP_LOGINERR, "Non-anonymous sessions must use encryption."); vsf_sysutil_exit(0); } if (tunable_ssl_enable && is_anon && !p_sess->control_use_ssl && tunable_force_anon_logins_ssl) { vsf_cmdio_write( p_sess, FTP_LOGINERR, "Anonymous sessions must use encryption."); vsf_sysutil_exit(0); } if (tunable_userlist_enable) { int located = str_contains_line(&p_sess->userlist_str, &p_sess->user_str); if ((located && tunable_userlist_deny) || (!located && !tunable_userlist_deny)) { check_login_delay(); vsf_cmdio_write(p_sess, FTP_LOGINERR, "Permission denied."); check_login_fails(p_sess); str_empty(&p_sess->user_str); return; } } if (is_anon && tunable_no_anon_password) { /* Fake a password */ str_alloc_text(&p_sess->ftp_arg_str, "<no password>"); handle_pass_command(p_sess); } else { vsf_cmdio_write(p_sess, FTP_GIVEPWORD, "Please specify the password."); } }
static void parse_username_password(struct vsf_session* p_sess) { while (1) { vsf_cmdio_get_cmd_and_arg(p_sess, &p_sess->ftp_cmd_str, &p_sess->ftp_arg_str, 1); if (tunable_ftp_enable) { if (str_equal_text(&p_sess->ftp_cmd_str, "USER")) { handle_user_command(p_sess); } else if (str_equal_text(&p_sess->ftp_cmd_str, "PASS")) { handle_pass_command(p_sess); } else if (str_equal_text(&p_sess->ftp_cmd_str, "QUIT")) { vsf_cmdio_write_exit(p_sess, FTP_GOODBYE, "Goodbye."); } else if (str_equal_text(&p_sess->ftp_cmd_str, "FEAT")) { handle_feat(p_sess); } else if (str_equal_text(&p_sess->ftp_cmd_str, "OPTS")) { handle_opts(p_sess); } else if (tunable_ssl_enable && str_equal_text(&p_sess->ftp_cmd_str, "AUTH") && !p_sess->control_use_ssl) { handle_auth(p_sess); } else if (tunable_ssl_enable && str_equal_text(&p_sess->ftp_cmd_str, "PBSZ")) { handle_pbsz(p_sess); } else if (tunable_ssl_enable && str_equal_text(&p_sess->ftp_cmd_str, "PROT")) { handle_prot(p_sess); } else if (str_isempty(&p_sess->ftp_cmd_str) && str_isempty(&p_sess->ftp_arg_str)) { /* Deliberately ignore to avoid NAT device bugs, as per ProFTPd. */ } else { vsf_cmdio_write(p_sess, FTP_LOGINERR, "Please login with USER and PASS."); } } else if (tunable_http_enable) { if (str_equal_text(&p_sess->ftp_cmd_str, "GET")) { handle_get(p_sess); } else { vsf_cmdio_write(p_sess, FTP_LOGINERR, "Bad HTTP verb."); } vsf_sysutil_exit(0); } } }
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); } }
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."); } } }