static void drop_all_privs(void) { struct mystr user_str = INIT_MYSTR; struct mystr dir_str = INIT_MYSTR; int option = VSF_SECUTIL_OPTION_CHROOT | VSF_SECUTIL_OPTION_NO_PROCS; if (!tunable_ssl_enable) { /* Unfortunately, can only enable this if we can be sure of not using SSL. * In the SSL case, we'll need to receive data transfer file descriptors. */ option |= VSF_SECUTIL_OPTION_NO_FDS; } str_alloc_text(&user_str, tunable_nopriv_user); str_alloc_text(&dir_str, tunable_secure_chroot_dir); /* Be kind: give good error message if the secure dir is missing */ { struct vsf_sysutil_statbuf* p_statbuf = 0; if (vsf_sysutil_retval_is_error(str_lstat(&dir_str, &p_statbuf))) { die2("vsftpd: not found: directory given in 'secure_chroot_dir':", tunable_secure_chroot_dir); } vsf_sysutil_free(p_statbuf); } vsf_secutil_change_credentials(&user_str, &dir_str, 0, 0, option); str_free(&user_str); str_free(&dir_str); }
static void common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str, int do_chroot, int anon) { int was_anon = anon; int newpid; vsf_sysutil_default_sig(kVSFSysUtilSigCHLD); /* Asks the pre-login child to go away (by exiting) */ priv_sock_send_result(p_sess, PRIV_SOCK_RESULT_OK); (void) vsf_sysutil_wait(); /* Handle loading per-user config options */ handle_per_user_config(p_user_str); vsf_sysutil_install_async_sighandler(kVSFSysUtilSigCHLD, handle_sigchld); newpid = vsf_sysutil_fork(); if (newpid == 0) { struct mystr guest_user_str = INIT_MYSTR; struct mystr chdir_str = INIT_MYSTR; unsigned int secutil_option = VSF_SECUTIL_OPTION_USE_GROUPS; calculate_chdir_dir(anon, &chdir_str, p_user_str); if (do_chroot) { secutil_option |= VSF_SECUTIL_OPTION_CHROOT; } /* Child - drop privs and start proper FTP! */ if (tunable_guest_enable && !anon) { /* Remap to the guest user */ str_alloc_text(&guest_user_str, tunable_guest_username); p_user_str = &guest_user_str; /* SECURITY: For now, apply the anonymous restrictions to * guest users */ anon = 1; } if (!anon) { secutil_option |= VSF_SECUTIL_OPTION_CHANGE_EUID; } vsf_secutil_change_credentials(p_user_str, 0, &chdir_str, 0, secutil_option); str_free(&guest_user_str); str_free(&chdir_str); /* Guard against the config error of having the anonymous ftp tree owned * by the user we are running as */ if (was_anon && vsf_sysutil_write_access("/")) { die("vsftpd: refusing to run with writable anonymous root"); } p_sess->is_anonymous = anon; process_post_login(p_sess); bug("should not get here: common_do_login"); } /* Parent */ vsf_priv_parent_postlogin(p_sess); bug("should not get here in common_do_login"); }
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 drop_all_privs(void) { 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); /* Be kind: give good error message if the secure dir is missing */ { struct vsf_sysutil_statbuf* p_statbuf = 0; if (vsf_sysutil_retval_is_error(str_lstat(&dir_str, &p_statbuf))) { die("vsftpd: not found: directory given in 'secure_chroot_dir'"); } vsf_sysutil_free(p_statbuf); } vsf_secutil_change_credentials(&user_str, &dir_str, 0, 0, VSF_SECUTIL_OPTION_CHROOT); str_free(&user_str); str_free(&dir_str); }
void vsf_one_process_start(struct vsf_session* p_sess) { unsigned int caps = 0; if (tunable_chown_uploads) { caps |= kCapabilityCAP_CHOWN; } if (tunable_connect_from_port_20) { caps |= kCapabilityCAP_NET_BIND_SERVICE; } { struct mystr user_name = INIT_MYSTR; struct mystr chdir_str = INIT_MYSTR; str_alloc_text(&user_name, tunable_ftp_username); if (tunable_anon_root) { str_alloc_text(&chdir_str, tunable_anon_root); } if (tunable_run_as_launching_user) { if (!str_isempty(&chdir_str)) { (void) str_chdir(&chdir_str); } } else { vsf_secutil_change_credentials(&user_name, 0, &chdir_str, caps, VSF_SECUTIL_OPTION_CHROOT | VSF_SECUTIL_OPTION_USE_GROUPS); } str_free(&user_name); str_free(&chdir_str); } init_connection(p_sess); }
static void common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str, int do_chroot, int anon) { int was_anon = anon; const struct mystr* p_orig_user_str = p_user_str; int newpid; vsf_sysutil_install_null_sighandler(kVSFSysUtilSigCHLD); /* Tells the pre-login child all is OK (it may exit in response) */ priv_sock_send_result(p_sess->parent_fd, PRIV_SOCK_RESULT_OK); if (!p_sess->control_use_ssl) { (void) vsf_sysutil_wait(); } else { p_sess->ssl_slave_active = 1; } /* Handle loading per-user config options */ handle_per_user_config(p_user_str); /* Set this before we fork */ p_sess->is_anonymous = anon; priv_sock_close(p_sess); priv_sock_init(p_sess); vsf_sysutil_install_sighandler(kVSFSysUtilSigCHLD, handle_sigchld, 0, 1); if (tunable_isolate_network && !tunable_port_promiscuous) { newpid = vsf_sysutil_fork_newnet(); } else { newpid = vsf_sysutil_fork(); } if (newpid == 0) { struct mystr guest_user_str = INIT_MYSTR; struct mystr chroot_str = INIT_MYSTR; struct mystr chdir_str = INIT_MYSTR; struct mystr userdir_str = INIT_MYSTR; unsigned int secutil_option = VSF_SECUTIL_OPTION_USE_GROUPS | VSF_SECUTIL_OPTION_NO_PROCS; /* Child - drop privs and start proper FTP! */ /* This PR_SET_PDEATHSIG doesn't work for all possible process tree setups. * The other cases are taken care of by a shutdown() of the command * connection in our SIGTERM handler. */ vsf_set_die_if_parent_dies(); priv_sock_set_child_context(p_sess); if (tunable_guest_enable && !anon) { p_sess->is_guest = 1; /* Remap to the guest user */ str_alloc_text(&guest_user_str, tunable_guest_username); p_user_str = &guest_user_str; if (!tunable_virtual_use_local_privs) { anon = 1; do_chroot = 1; } } if (do_chroot) { secutil_option |= VSF_SECUTIL_OPTION_CHROOT; } if (!anon) { secutil_option |= VSF_SECUTIL_OPTION_CHANGE_EUID; } calculate_chdir_dir(was_anon, &userdir_str, &chroot_str, &chdir_str, p_user_str, p_orig_user_str); vsf_secutil_change_credentials(p_user_str, &userdir_str, &chroot_str, 0, secutil_option); if (!str_isempty(&chdir_str)) { (void) str_chdir(&chdir_str); } str_free(&guest_user_str); str_free(&chroot_str); str_free(&chdir_str); str_free(&userdir_str); p_sess->is_anonymous = anon; process_post_login(p_sess); bug("should not get here: common_do_login"); } /* Parent */ priv_sock_set_parent_context(p_sess); if (tunable_ssl_enable) { ssl_comm_channel_set_producer_context(p_sess); } vsf_priv_parent_postlogin(p_sess); bug("should not get here in common_do_login"); }
static void common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str, int do_chroot, int anon) { int was_anon = anon; const struct mystr* p_orig_user_str = p_user_str; int newpid; vsf_sysutil_install_null_sighandler(kVSFSysUtilSigCHLD); /* Tells the pre-login child all is OK (it may exit in response) */ priv_sock_send_result(p_sess->parent_fd, PRIV_SOCK_RESULT_OK); if (!p_sess->control_use_ssl) { (void) vsf_sysutil_wait(); } else { p_sess->ssl_slave_active = 1; } /* Absorb the SIGCHLD */ vsf_sysutil_unblock_sig(kVSFSysUtilSigCHLD); /* Handle loading per-user config options */ handle_per_user_config(p_user_str); /* Set this before we fork */ p_sess->is_anonymous = anon; vsf_sysutil_install_async_sighandler(kVSFSysUtilSigCHLD, twoproc_handle_sigchld); newpid = vsf_sysutil_fork(); if (newpid == 0) { struct mystr guest_user_str = INIT_MYSTR; struct mystr chroot_str = INIT_MYSTR; struct mystr chdir_str = INIT_MYSTR; struct mystr userdir_str = INIT_MYSTR; unsigned int secutil_option = VSF_SECUTIL_OPTION_USE_GROUPS; /* Child - drop privs and start proper FTP! */ vsf_sysutil_close(p_sess->parent_fd); if (tunable_ssl_enable) { vsf_sysutil_close(p_sess->ssl_slave_fd); } if (tunable_guest_enable && !anon) { /* Remap to the guest user */ str_alloc_text(&guest_user_str, tunable_guest_username); p_user_str = &guest_user_str; if (!tunable_virtual_use_local_privs) { anon = 1; do_chroot = 1; } } if (do_chroot) { secutil_option |= VSF_SECUTIL_OPTION_CHROOT; } if (!anon) { secutil_option |= VSF_SECUTIL_OPTION_CHANGE_EUID; } calculate_chdir_dir(was_anon, &userdir_str, &chroot_str, &chdir_str, p_user_str, p_orig_user_str); vsf_secutil_change_credentials(p_user_str, &userdir_str, &chroot_str, 0, secutil_option); if (!str_isempty(&chdir_str)) { (void) str_chdir(&chdir_str); } str_free(&guest_user_str); str_free(&chroot_str); str_free(&chdir_str); str_free(&userdir_str); /* Guard against the config error of having the anonymous ftp tree owned * by the user we are running as */ if (was_anon && vsf_sysutil_write_access("/")) { die("vsftpd: refusing to run with writable anonymous root"); } p_sess->is_anonymous = anon; process_post_login(p_sess); bug("should not get here: common_do_login"); } /* Parent */ if (tunable_ssl_enable) { vsf_sysutil_close(p_sess->ssl_consumer_fd); /* Keep the SSL slave fd around so we can shutdown() upon exit */ } vsf_priv_parent_postlogin(p_sess); bug("should not get here in common_do_login"); }