1
0
mirror of https://github.com/InfrastructureServices/vsftpd.git synced 2025-04-19 01:24:02 +03:00

Updated to v2.2.0pre1

This commit is contained in:
Dag Wieers 2009-07-07 00:00:00 +02:00
parent 3f1020a66e
commit b93ee04314
26 changed files with 553 additions and 142 deletions

View File

@ -1,2 +1,3 @@
Everything within this tar archive is Copyright (c) Chris Evans.
Everything within this tar archive is Copyright (c) Chris Evans, except
where otherwise noted in individual files.

View File

@ -1132,3 +1132,21 @@ At this point: v2.1.1 released!
At this point: v2.1.2 released!
===============================
- Fix compile on systems with no RLIMIT_NPROC, oops.
- Change some unsigned int to socklen_t's to avoid warnings on various
platforms, e.g. AIX.
- Add some syscall constants to ptracesandbox.c to fix the build on systems
with 2.4 kernel headers.
- Look for libs in /lib64 and /usr/lib64 too (Fixes Fedora 11 x86_64 compile).
- Fix EACCES mapping, thanks Solar Designer <solar@openwall.com>.
- Dont emit a bogus "OOPS: " message upon a QUIT, report from Solar Designer
<solar@openwall.com>.
- Tweak example vsftpd.conf to add commented out chroot_local_user=YES,
from Ivan I. Grushin <mcgru@intrice.ru>.
- Where available, use CLONE_NEWNET to isolate the untrusted processes so that
they can't do arbitrary connect() and instead have to ask the privileged
process for sockets. Moderate code disturbance - hope for no breakage :-/
- Disable implicit activation of one_process_model so that an anonymous setup
can benefit from the no-network isolation of the unprivileged process (where
available).
(vsftpd-2.2.0pre1)

2
README
View File

@ -1,4 +1,4 @@
This is vsftpd, version 2.1.2
This is vsftpd, version 2.2.0
Author: Chris Evans
Contact: scarybeasts@gmail.com
Website: http://vsftpd.beasts.org/

View File

@ -83,6 +83,7 @@ vsf_cmdio_write_exit(struct vsf_session* p_sess, int status, const char* p_text)
*/
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);
}

View File

@ -98,32 +98,27 @@ int
vsf_ftpdataio_get_pasv_fd(struct vsf_session* p_sess)
{
int remote_fd;
struct vsf_sysutil_sockaddr* p_accept_addr = 0;
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))
if (tunable_one_process_model)
{
remote_fd = vsf_one_process_get_pasv_fd(p_sess);
}
else
{
remote_fd = vsf_two_process_get_pasv_fd(p_sess);
}
/* Yes, yes, hardcoded bad I know. */
if (remote_fd == -1)
{
vsf_cmdio_write(p_sess, FTP_BADSENDCONN,
"Failed to establish connection.");
vsf_sysutil_sockaddr_clear(&p_accept_addr);
return remote_fd;
}
/* SECURITY:
* Reject the connection if it wasn't from the same IP as the
* control connection.
*/
if (!tunable_pasv_promiscuous)
else if (remote_fd == -2)
{
if (!vsf_sysutil_sockaddr_addr_equal(p_sess->p_remote_addr, p_accept_addr))
{
vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Security: Bad IP connecting.");
vsf_sysutil_close(remote_fd);
vsf_sysutil_sockaddr_clear(&p_accept_addr);
return -1;
}
vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Security: Bad IP connecting.");
vsf_sysutil_close(remote_fd);
return -1;
}
vsf_sysutil_sockaddr_clear(&p_accept_addr);
init_data_sock_params(p_sess, remote_fd);
return remote_fd;
}
@ -131,38 +126,19 @@ vsf_ftpdataio_get_pasv_fd(struct vsf_session* p_sess)
int
vsf_ftpdataio_get_port_fd(struct vsf_session* p_sess)
{
int retval;
int remote_fd;
if (tunable_connect_from_port_20)
if (tunable_one_process_model || tunable_port_promiscuous)
{
if (tunable_one_process_model)
{
remote_fd = vsf_one_process_get_priv_data_sock(p_sess);
}
else
{
remote_fd = vsf_two_process_get_priv_data_sock(p_sess);
}
remote_fd = vsf_one_process_get_priv_data_sock(p_sess);
}
else
{
static struct vsf_sysutil_sockaddr* s_p_addr;
remote_fd = vsf_sysutil_get_ipsock(p_sess->p_local_addr);
vsf_sysutil_sockaddr_clone(&s_p_addr, p_sess->p_local_addr);
vsf_sysutil_sockaddr_set_port(s_p_addr, 0);
retval = vsf_sysutil_bind(remote_fd, s_p_addr);
if (retval != 0)
{
die("vsf_sysutil_bind");
}
remote_fd = vsf_two_process_get_priv_data_sock(p_sess);
}
retval = vsf_sysutil_connect_timeout(remote_fd, p_sess->p_port_sockaddr,
tunable_connect_timeout);
if (vsf_sysutil_retval_is_error(retval))
if (vsf_sysutil_retval_is_error(remote_fd))
{
vsf_cmdio_write(p_sess, FTP_BADSENDCONN,
"Failed to establish connection.");
vsf_sysutil_close(remote_fd);
return -1;
}
init_data_sock_params(p_sess, remote_fd);

8
main.c
View File

@ -214,14 +214,6 @@ main(int argc, const char* argv[])
die2("cannot open email passwords file:", tunable_email_password_file);
}
}
/* Special case - can force one process model if we've got a setup
* needing _no_ privs
*/
if (!tunable_local_enable && !tunable_connect_from_port_20 &&
!tunable_chown_uploads)
{
tunable_one_process_model = 1;
}
if (tunable_run_as_launching_user)
{
tunable_one_process_model = 1;

View File

@ -128,7 +128,32 @@ vsf_one_process_login(struct vsf_session* p_sess,
int
vsf_one_process_get_priv_data_sock(struct vsf_session* p_sess)
{
return vsf_privop_get_ftp_port_sock(p_sess);
unsigned short port = vsf_sysutil_sockaddr_get_port(p_sess->p_port_sockaddr);
return vsf_privop_get_ftp_port_sock(p_sess, port);
}
void
vsf_one_process_pasv_cleanup(struct vsf_session* p_sess)
{
vsf_privop_pasv_cleanup(p_sess);
}
int
vsf_one_process_pasv_active(struct vsf_session* p_sess)
{
return vsf_privop_pasv_active(p_sess);
}
unsigned short
vsf_one_process_listen(struct vsf_session* p_sess)
{
return vsf_privop_pasv_listen(p_sess);
}
int
vsf_one_process_get_pasv_fd(struct vsf_session* p_sess)
{
return vsf_privop_accept_pasv(p_sess);
}
void

View File

@ -34,6 +34,44 @@ void vsf_one_process_login(struct vsf_session* p_sess,
*/
int vsf_one_process_get_priv_data_sock(struct vsf_session* p_sess);
/* vsf_one_process_pasv_cleanup()
* PURPOSE
* Clean up any listening passive socket.
* PARAMETERS
* p_sess - the current session object
*/
void vsf_one_process_pasv_cleanup(struct vsf_session* p_sess);
/* vsf_one_process_pasv_active()
* PURPOSE
* Determine whether a listening pasv socket is active.
* PARAMETERS
* p_sess - the current session object
* RETURNS
* 1 if active, 0 if not.
*/
int vsf_one_process_pasv_active(struct vsf_session* p_sess);
/* vsf_one_process_listen()
* PURPOSE
* Start listening for an incoming connection.
* PARAMETERS
* p_sess - the current session object
* RETURNS
* The port we listened on.
*/
unsigned short vsf_one_process_listen(struct vsf_session* p_sess);
/* vsf_one_process_get_pasv_fd()
* PURPOSE
* Accept an incoming connection.
* PARAMETERS
* p_sess - the current session object
* RETURNS
* The file descriptor for the incoming connection.
*/
int vsf_one_process_get_pasv_fd(struct vsf_session* p_sess);
/* vsf_one_process_chown_upload()
* PURPOSE
* Change ownership of an uploaded file using the one process model.

View File

@ -172,8 +172,7 @@ process_post_login(struct vsf_session* 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);
vsf_cmdio_write_exit(p_sess, FTP_GOODBYE, "Goodbye.");
}
else if (str_equal_text(&p_sess->ftp_cmd_str, "PWD") ||
str_equal_text(&p_sess->ftp_cmd_str, "XPWD"))
@ -503,9 +502,16 @@ static int
pasv_active(struct vsf_session* p_sess)
{
int ret = 0;
if (p_sess->pasv_listen_fd != -1)
if (tunable_one_process_model)
{
ret = vsf_one_process_pasv_active(p_sess);
}
else
{
ret = vsf_two_process_pasv_active(p_sess);
}
if (ret)
{
ret = 1;
if (port_active(p_sess))
{
bug("pasv and port both active");
@ -523,23 +529,22 @@ port_cleanup(struct vsf_session* p_sess)
static void
pasv_cleanup(struct vsf_session* p_sess)
{
if (p_sess->pasv_listen_fd != -1)
if (tunable_one_process_model)
{
vsf_sysutil_close(p_sess->pasv_listen_fd);
p_sess->pasv_listen_fd = -1;
vsf_one_process_pasv_cleanup(p_sess);
}
else
{
vsf_two_process_pasv_cleanup(p_sess);
}
}
static void
handle_pasv(struct vsf_session* p_sess, int is_epsv)
{
unsigned short the_port;
static struct mystr s_pasv_res_str;
static struct vsf_sysutil_sockaddr* s_p_sockaddr;
int bind_retries = 10;
unsigned short the_port = 0;
/* IPPORT_RESERVED */
unsigned short min_port = 1024;
unsigned short max_port = 65535;
int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr);
if (is_epsv && !str_isempty(&p_sess->ftp_arg_str))
{
@ -560,58 +565,13 @@ handle_pasv(struct vsf_session* p_sess, int is_epsv)
}
pasv_cleanup(p_sess);
port_cleanup(p_sess);
if (is_ipv6)
if (tunable_one_process_model)
{
p_sess->pasv_listen_fd = vsf_sysutil_get_ipv6_sock();
the_port = vsf_one_process_listen(p_sess);
}
else
{
p_sess->pasv_listen_fd = vsf_sysutil_get_ipv4_sock();
}
vsf_sysutil_activate_reuseaddr(p_sess->pasv_listen_fd);
if (tunable_pasv_min_port > min_port && tunable_pasv_min_port <= max_port)
{
min_port = tunable_pasv_min_port;
}
if (tunable_pasv_max_port >= min_port && tunable_pasv_max_port < max_port)
{
max_port = tunable_pasv_max_port;
}
while (--bind_retries)
{
int retval;
double scaled_port;
the_port = vsf_sysutil_get_random_byte();
the_port <<= 8;
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;
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;
}
}
/* SELinux systems can give you an inopportune EACCES, it seems. */
if (vsf_sysutil_get_error() == kVSFSysUtilErrADDRINUSE ||
vsf_sysutil_get_error() == kVSFSysUtilErrACCES)
{
continue;
}
die("vsf_sysutil_bind / listen");
}
if (!bind_retries)
{
die("vsf_sysutil_bind");
the_port = vsf_two_process_listen(p_sess);
}
if (is_epsv)
{
@ -629,6 +589,10 @@ handle_pasv(struct vsf_session* p_sess, int is_epsv)
die("invalid pasv_address");
}
}
else
{
vsf_sysutil_sockaddr_clone(&s_p_sockaddr, p_sess->p_local_addr);
}
str_alloc_text(&s_pasv_res_str, "Entering Passive Mode (");
if (!is_ipv6)
{

View File

@ -26,6 +26,10 @@ static void minimize_privilege(struct vsf_session* p_sess);
static void process_post_login_req(struct vsf_session* p_sess);
static void cmd_process_chown(struct vsf_session* p_sess);
static void cmd_process_get_data_sock(struct vsf_session* p_sess);
static void cmd_process_pasv_cleanup(struct vsf_session* p_sess);
static void cmd_process_pasv_active(struct vsf_session* p_sess);
static void cmd_process_pasv_listen(struct vsf_session* p_sess);
static void cmd_process_pasv_accept(struct vsf_session* p_sess);
void
vsf_priv_parent_postlogin(struct vsf_session* p_sess)
@ -48,10 +52,26 @@ process_post_login_req(struct vsf_session* p_sess)
{
cmd_process_chown(p_sess);
}
else if (tunable_connect_from_port_20 && cmd == PRIV_SOCK_GET_DATA_SOCK)
else if (cmd == PRIV_SOCK_GET_DATA_SOCK)
{
cmd_process_get_data_sock(p_sess);
}
else if (cmd == PRIV_SOCK_PASV_CLEANUP)
{
cmd_process_pasv_cleanup(p_sess);
}
else if (cmd == PRIV_SOCK_PASV_ACTIVE)
{
cmd_process_pasv_active(p_sess);
}
else if (cmd == PRIV_SOCK_PASV_LISTEN)
{
cmd_process_pasv_listen(p_sess);
}
else if (cmd == PRIV_SOCK_PASV_ACCEPT)
{
cmd_process_pasv_accept(p_sess);
}
else
{
die("bad request in process_post_login_req");
@ -104,9 +124,44 @@ cmd_process_chown(struct vsf_session* p_sess)
static void
cmd_process_get_data_sock(struct vsf_session* p_sess)
{
int sock_fd = vsf_privop_get_ftp_port_sock(p_sess);
unsigned short port = (unsigned short) priv_sock_get_int(p_sess->parent_fd);
int sock_fd = vsf_privop_get_ftp_port_sock(p_sess, port);
if (sock_fd == -1)
{
priv_sock_send_result(p_sess->parent_fd, PRIV_SOCK_RESULT_BAD);
return;
}
priv_sock_send_result(p_sess->parent_fd, PRIV_SOCK_RESULT_OK);
priv_sock_send_fd(p_sess->parent_fd, sock_fd);
vsf_sysutil_close(sock_fd);
}
static void
cmd_process_pasv_cleanup(struct vsf_session* p_sess)
{
vsf_privop_pasv_cleanup(p_sess);
priv_sock_send_result(p_sess->parent_fd, PRIV_SOCK_RESULT_OK);
}
static void
cmd_process_pasv_active(struct vsf_session* p_sess)
{
int active = vsf_privop_pasv_active(p_sess);
priv_sock_send_int(p_sess->parent_fd, active);
}
static void
cmd_process_pasv_listen(struct vsf_session* p_sess)
{
unsigned short port = vsf_privop_pasv_listen(p_sess);
priv_sock_send_int(p_sess->parent_fd, port);
}
static void
cmd_process_pasv_accept(struct vsf_session* p_sess)
{
int fd = vsf_privop_accept_pasv(p_sess);
priv_sock_send_fd(p_sess->parent_fd, fd);
vsf_sysutil_close(fd);
}

View File

@ -127,8 +127,7 @@ parse_username_password(struct vsf_session* 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);
vsf_cmdio_write_exit(p_sess, FTP_GOODBYE, "Goodbye.");
}
else if (str_equal_text(&p_sess->ftp_cmd_str, "FEAT"))
{

140
privops.c
View File

@ -32,12 +32,22 @@ static enum EVSFPrivopLoginResult handle_login(
const struct mystr* p_pass_str);
int
vsf_privop_get_ftp_port_sock(struct vsf_session* p_sess)
vsf_privop_get_ftp_port_sock(struct vsf_session* p_sess,
unsigned short remote_port)
{
static struct vsf_sysutil_sockaddr* p_sockaddr;
int retval;
int i;
int s = vsf_sysutil_get_ipsock(p_sess->p_local_addr);
int port = 0;
if (vsf_sysutil_is_port_reserved(remote_port))
{
die("Illegal port request");
}
if (tunable_connect_from_port_20)
{
port = tunable_ftp_data_port;
}
vsf_sysutil_activate_reuseaddr(s);
/* A report of failure here on Solaris, presumably buggy address reuse
* support? We'll retry.
@ -46,11 +56,11 @@ vsf_privop_get_ftp_port_sock(struct vsf_session* p_sess)
{
double sleep_for;
vsf_sysutil_sockaddr_clone(&p_sockaddr, p_sess->p_local_addr);
vsf_sysutil_sockaddr_set_port(p_sockaddr, tunable_ftp_data_port);
vsf_sysutil_sockaddr_set_port(p_sockaddr, port);
retval = vsf_sysutil_bind(s, p_sockaddr);
if (retval == 0)
{
return s;
break;
}
if (vsf_sysutil_get_error() != kVSFSysUtilErrADDRINUSE || i == 1)
{
@ -61,9 +71,133 @@ vsf_privop_get_ftp_port_sock(struct vsf_session* p_sess)
sleep_for += 1.0;
vsf_sysutil_sleep(sleep_for);
}
vsf_sysutil_sockaddr_set_port(p_sess->p_remote_addr, remote_port);
retval = vsf_sysutil_connect_timeout(s, p_sess->p_remote_addr,
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 = 0;
/* IPPORT_RESERVED */
unsigned short min_port = 1024;
unsigned short max_port = 65535;
int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr);
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);
if (tunable_pasv_min_port > min_port && tunable_pasv_min_port <= max_port)
{
min_port = tunable_pasv_min_port;
}
if (tunable_pasv_max_port >= min_port && tunable_pasv_max_port < max_port)
{
max_port = tunable_pasv_max_port;
}
while (--bind_retries)
{
int retval;
double scaled_port;
the_port = vsf_sysutil_get_random_byte();
the_port <<= 8;
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;
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;
}
}
/* SELinux systems can give you an inopportune EACCES, it seems. */
if (vsf_sysutil_get_error() == kVSFSysUtilErrADDRINUSE ||
vsf_sysutil_get_error() == kVSFSysUtilErrACCES)
{
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;
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;
}
/* SECURITY:
* Reject the connection if it wasn't from the same IP as the
* control connection.
*/
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)
{

View File

@ -6,13 +6,59 @@ struct vsf_session;
/* vsf_privop_get_ftp_port_sock()
* PURPOSE
* Return a network socket bound to a privileged port (less than 1024).
* Return a network socket potentially bound to a privileged port (less than
* 1024) and connected to the remote.
* PARAMETERS
* p_sess - the current session object
* remote_port - the remote port to connect to
* RETURNS
* A file descriptor which is a socket bound to the privileged port, and
* connected to the remote on the specified port.
* Kills the process / session if the bind() fails.
* Returns -1 if the bind() worked but the connect() was not possible.
*/
int vsf_privop_get_ftp_port_sock(struct vsf_session* p_sess,
unsigned short remote_port);
/* vsf_privop_pasv_cleanup()
* PURPOSE
* Makes sure any listening passive socket is closed.
* PARAMETERS
* p_sess - the current session object
*/
void vsf_privop_pasv_cleanup(struct vsf_session* p_sess);
/* vsf_privop_pasv_listen()
* PURPOSE
* Start listening for an FTP data connection.
* PARAMETERS
* p_sess - the current session object
* RETURNS
* A file descriptor which is a socket bound to the privileged port.
* The port we ended up listening on.
*/
int vsf_privop_get_ftp_port_sock(struct vsf_session* p_sess);
unsigned short vsf_privop_pasv_listen(struct vsf_session* p_sess);
/* vsf_privop_pasv_active()
* PURPOSE
* Determine whether there is a passive listening socket active.
* PARAMETERS
* p_sess - the current session object
* RETURNS
* 1 if active, 0 if not.
*/
int vsf_privop_pasv_active(struct vsf_session* p_sess);
/* vsf_privop_accept_pasv()
* PURPOSE
* Accept a connection on the listening data socket.
* PARAMETERS
* p_sess - the current session object
* RETURNS
* The file descriptor of the accepted incoming connection; or -1 if a
* network error occurred or -2 if the incoming connection was from the
* wrong IP (security issue).
*/
int vsf_privop_accept_pasv(struct vsf_session* p_sess);
/* vsf_privop_do_file_chown()
* PURPOSE

View File

@ -165,6 +165,10 @@ int priv_sock_get_int(int fd);
#define PRIV_SOCK_DO_SSL_CLOSE 7
#define PRIV_SOCK_DO_SSL_READ 8
#define PRIV_SOCK_DO_SSL_WRITE 9
#define PRIV_SOCK_PASV_CLEANUP 10
#define PRIV_SOCK_PASV_ACTIVE 11
#define PRIV_SOCK_PASV_LISTEN 12
#define PRIV_SOCK_PASV_ACCEPT 13
#define PRIV_SOCK_RESULT_OK 1
#define PRIV_SOCK_RESULT_BAD 2

View File

@ -35,6 +35,14 @@
#include <asm/unistd.h>
#ifndef __NR_sendfile64
#define __NR_sendfile64 239
#endif
#ifndef __NR_exit_group
#define __NR_exit_group 252
#endif
#ifndef __NR_utimes
#define __NR_utimes 271
#endif

View File

@ -73,6 +73,9 @@
#ifndef CLONE_NEWIPC
#define CLONE_NEWIPC 0x08000000
#endif
#ifndef CLONE_NEWNET
#define CLONE_NEWNET 0x40000000
#endif
#include <linux/unistd.h>
#include <errno.h>
#include <syscall.h>
@ -1255,6 +1258,25 @@ vsf_sysutil_fork_isolate_failok()
return vsf_sysutil_fork_failok();
}
int
vsf_sysutil_fork_newnet()
{
#ifdef VSF_SYSDEP_HAVE_LINUX_CLONE
static int cloneflags_work = 1;
if (cloneflags_work)
{
int ret = syscall(__NR_clone, CLONE_NEWNET | SIGCHLD, NULL);
if (ret != -1 || errno != EINVAL)
{
vsf_sysutil_clear_pid_cache();
return ret;
}
cloneflags_work = 0;
}
#endif
return vsf_sysutil_fork();
}
int
vsf_sysutil_getpid_nocache(void)
{

View File

@ -60,8 +60,12 @@ void vsf_set_die_if_parent_dies();
/* Or a softer version delivering SIGTERM. */
void vsf_set_term_if_parent_dies();
/* If supported, the ability to fork into different secure namespaces. */
/* If supported, the ability to fork into different secure namespaces (PID
* and IPC. Fails back to normal fork() */
int vsf_sysutil_fork_isolate_failok();
/* If supported, the ability to fork into an empty network namespace.
* Fails back to normal fork() */
int vsf_sysutil_fork_newnet();
int vsf_sysutil_getpid_nocache();
#endif /* VSF_SYSDEPUTIL_H */

View File

@ -542,15 +542,7 @@ vsf_sysutil_getpid(void)
int
vsf_sysutil_fork(void)
{
/* Child does NOT inherit exit function */
exitfunc_t curr_func = s_exit_func;
int retval;
s_exit_func = 0;
retval = vsf_sysutil_fork_failok();
if (retval != 0)
{
s_exit_func = curr_func;
}
int retval = vsf_sysutil_fork_failok();
if (retval < 0)
{
die("fork");
@ -561,7 +553,15 @@ vsf_sysutil_fork(void)
int
vsf_sysutil_fork_failok(void)
{
int retval = fork();
/* Child does NOT inherit exit function */
exitfunc_t curr_func = s_exit_func;
int retval;
s_exit_func = 0;
retval = fork();
if (retval != 0)
{
s_exit_func = curr_func;
}
if (retval == 0)
{
vsf_sysutil_clear_pid_cache();
@ -1597,7 +1597,7 @@ vsf_sysutil_get_error(void)
retval = kVSFSysUtilErrOPNOTSUPP;
break;
case EACCES:
retval = kVSFSysUtilErrOPNOTSUPP;
retval = kVSFSysUtilErrACCES;
break;
}
return retval;
@ -1684,7 +1684,7 @@ vsf_sysutil_accept_timeout(int fd, struct vsf_sysutil_sockaddr* p_sockaddr,
int saved_errno;
fd_set accept_fdset;
struct timeval timeout;
unsigned int socklen = sizeof(remote_addr);
socklen_t socklen = sizeof(remote_addr);
if (p_sockaddr)
{
vsf_sysutil_memclr(p_sockaddr, sizeof(*p_sockaddr));
@ -1808,7 +1808,7 @@ vsf_sysutil_getsockname(int fd, struct vsf_sysutil_sockaddr** p_sockptr)
{
struct vsf_sysutil_sockaddr the_addr;
int retval;
unsigned int socklen = sizeof(the_addr);
socklen_t socklen = sizeof(the_addr);
vsf_sysutil_sockaddr_clear(p_sockptr);
retval = getsockname(fd, &the_addr.u.u_sockaddr, &socklen);
if (retval != 0)
@ -1833,7 +1833,7 @@ vsf_sysutil_getpeername(int fd, struct vsf_sysutil_sockaddr** p_sockptr)
{
struct vsf_sysutil_sockaddr the_addr;
int retval;
unsigned int socklen = sizeof(the_addr);
socklen_t socklen = sizeof(the_addr);
vsf_sysutil_sockaddr_clear(p_sockptr);
retval = getpeername(fd, &the_addr.u.u_sockaddr, &socklen);
if (retval != 0)
@ -2130,6 +2130,25 @@ vsf_sysutil_sockaddr_set_any(struct vsf_sysutil_sockaddr* p_sockaddr)
}
}
unsigned short
vsf_sysutil_sockaddr_get_port(const struct vsf_sysutil_sockaddr* p_sockptr)
{
if (p_sockptr->u.u_sockaddr.sa_family == AF_INET)
{
return ntohs(p_sockptr->u.u_sockaddr_in.sin_port);
}
else if (p_sockptr->u.u_sockaddr.sa_family == AF_INET6)
{
return ntohs(p_sockptr->u.u_sockaddr_in6.sin6_port);
}
else
{
bug("bad family");
}
/* NOTREACHED */
return 0;
}
void
vsf_sysutil_sockaddr_set_port(struct vsf_sysutil_sockaddr* p_sockptr,
unsigned short the_port)
@ -2779,6 +2798,7 @@ vsf_sysutil_set_no_fds()
void
vsf_sysutil_set_no_procs()
{
#ifdef RLIMIT_NPROC
int ret;
struct rlimit rlim;
rlim.rlim_cur = 0;
@ -2788,6 +2808,7 @@ vsf_sysutil_set_no_procs()
{
die("setrlimit NPROC");
}
#endif
}
void

View File

@ -228,6 +228,8 @@ void vsf_sysutil_sockaddr_set_ipv4addr(struct vsf_sysutil_sockaddr* p_sockptr,
void vsf_sysutil_sockaddr_set_ipv6addr(struct vsf_sysutil_sockaddr* p_sockptr,
const unsigned char* p_raw);
void vsf_sysutil_sockaddr_set_any(struct vsf_sysutil_sockaddr* p_sockaddr);
unsigned short vsf_sysutil_sockaddr_get_port(
const struct vsf_sysutil_sockaddr* p_sockptr);
void vsf_sysutil_sockaddr_set_port(struct vsf_sysutil_sockaddr* p_sockptr,
unsigned short the_port);
int vsf_sysutil_is_port_reserved(unsigned short port);

View File

@ -83,6 +83,7 @@ int tunable_implicit_ssl;
int tunable_sandbox;
int tunable_require_ssl_reuse;
int tunable_isolate;
int tunable_isolate_network;
unsigned int tunable_accept_timeout;
unsigned int tunable_connect_timeout;
@ -218,6 +219,7 @@ tunables_load_defaults()
tunable_sandbox = 0;
tunable_require_ssl_reuse = 1;
tunable_isolate = 1;
tunable_isolate_network = 1;
tunable_accept_timeout = 60;
tunable_connect_timeout = 60;

View File

@ -84,6 +84,7 @@ extern int tunable_implicit_ssl; /* Use implicit SSL protocol */
extern int tunable_sandbox; /* Deploy ptrace sandbox */
extern int tunable_require_ssl_reuse; /* Require re-used data conn */
extern int tunable_isolate; /* Use container clone() flags */
extern int tunable_isolate_network; /* Use CLONE_NEWNET */
/* Integer/numeric defines */
extern unsigned int tunable_accept_timeout;

View File

@ -90,7 +90,15 @@ vsf_two_process_start(struct vsf_session* p_sess)
}
vsf_sysutil_install_sighandler(kVSFSysUtilSigCHLD, handle_sigchld, 0, 1);
{
int newpid = vsf_sysutil_fork();
int newpid;
if (tunable_isolate_network)
{
newpid = vsf_sysutil_fork_newnet();
}
else
{
newpid = vsf_sysutil_fork();
}
if (newpid != 0)
{
priv_sock_set_parent_context(p_sess);
@ -200,15 +208,54 @@ int
vsf_two_process_get_priv_data_sock(struct vsf_session* p_sess)
{
char res;
unsigned short port = vsf_sysutil_sockaddr_get_port(p_sess->p_port_sockaddr);
priv_sock_send_cmd(p_sess->child_fd, PRIV_SOCK_GET_DATA_SOCK);
priv_sock_send_int(p_sess->child_fd, port);
res = priv_sock_get_result(p_sess->child_fd);
if (res != PRIV_SOCK_RESULT_OK)
if (res == PRIV_SOCK_RESULT_BAD)
{
return -1;
}
else if (res != PRIV_SOCK_RESULT_OK)
{
die("could not get privileged socket");
}
return priv_sock_recv_fd(p_sess->child_fd);
}
void
vsf_two_process_pasv_cleanup(struct vsf_session* p_sess)
{
char res;
priv_sock_send_cmd(p_sess->child_fd, PRIV_SOCK_PASV_CLEANUP);
res = priv_sock_get_result(p_sess->child_fd);
if (res != PRIV_SOCK_RESULT_OK)
{
die("could not clean up socket");
}
}
int
vsf_two_process_pasv_active(struct vsf_session* p_sess)
{
priv_sock_send_cmd(p_sess->child_fd, PRIV_SOCK_PASV_ACTIVE);
return priv_sock_get_int(p_sess->child_fd);
}
unsigned short
vsf_two_process_listen(struct vsf_session* p_sess)
{
priv_sock_send_cmd(p_sess->child_fd, PRIV_SOCK_PASV_LISTEN);
return (unsigned short) priv_sock_get_int(p_sess->child_fd);
}
int
vsf_two_process_get_pasv_fd(struct vsf_session* p_sess)
{
priv_sock_send_cmd(p_sess->child_fd, PRIV_SOCK_PASV_ACCEPT);
return priv_sock_recv_fd(p_sess->child_fd);
}
void
vsf_two_process_chown_upload(struct vsf_session* p_sess, int fd)
{
@ -324,7 +371,14 @@ common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
priv_sock_close(p_sess);
priv_sock_init(p_sess);
vsf_sysutil_install_sighandler(kVSFSysUtilSigCHLD, handle_sigchld, 0, 1);
newpid = vsf_sysutil_fork();
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;

View File

@ -33,6 +33,46 @@ void vsf_two_process_login(struct vsf_session* p_sess,
*/
int vsf_two_process_get_priv_data_sock(struct vsf_session* p_sess);
/* vsf_two_process_pasv_cleanup()
* PURPOSE
* Clean up any listening passive socket in the privileged side.
* PARAMETERS
* p_sess - the current session object
*/
void vsf_two_process_pasv_cleanup(struct vsf_session* p_sess);
/* vsf_two_process_pasv_active()
* PURPOSE
* Determine if the passive socket is listening on the privileged side.
* PARAMETERS
* p_sess - the current session object
* RETURNS
* 1 if active, 0 if not.
*/
int vsf_two_process_pasv_active(struct vsf_session* p_sess);
/* vsf_two_process_listen()
* PURPOSE
* Start listening for an incoming connection on the passive socket in the
* privileged side.
* PARAMETERS
* p_sess - the current session object
* RETURNS
* The port we listened on.
*/
unsigned short vsf_two_process_listen(struct vsf_session* p_sess);
/* vsf_two_process_get_pasv_fd()
* PURPOSE
* Accept an incoming connection on the passive socket in the privileged
* side.
* PARAMETERS
* p_sess - the current session object
* RETURNS
* The file descriptor for the incoming connection.
*/
int vsf_two_process_get_pasv_fd(struct vsf_session* p_sess);
/* vsf_two_process_chown_upload()
* PURPOSE
* Change ownership of an uploaded file using the two process model.

View File

@ -14,6 +14,7 @@ fi
if find_func pam_start sysdeputil.o; then
locate_library /lib/libpam.so.0 && echo "/lib/libpam.so.0";
locate_library /usr/lib/libpam.so && echo "-lpam";
locate_library /usr/lib64/libpam.so && echo "-lpam";
# HP-UX ends shared libraries with .sl
locate_library /usr/lib/libpam.sl && echo "-lpam";
# AIX ends shared libraries with .a
@ -21,6 +22,7 @@ if find_func pam_start sysdeputil.o; then
else
locate_library /lib/libcrypt.so && echo "-lcrypt";
locate_library /usr/lib/libcrypt.so && echo "-lcrypt";
locate_library /usr/lib64/libcrypt.so && echo "-lcrypt";
fi
# Look for the dynamic linker library. Needed by older RedHat when
@ -50,6 +52,7 @@ elif locate_library /lib/libcap.so.2; then
else
locate_library /usr/lib/libcap.so && echo "-lcap";
locate_library /lib/libcap.so && echo "-lcap";
locate_library /lib64/libcap.so && echo "-lcap";
fi
# Solaris needs this for nanosleep()..

View File

@ -92,6 +92,7 @@ connect_from_port_20=YES
# You may specify an explicit list of local users to chroot() to their home
# directory. If chroot_local_user is YES, then this list becomes a list of
# users to NOT chroot().
#chroot_local_user=YES
#chroot_list_enable=YES
# (default follows)
#chroot_list_file=/etc/vsftpd.chroot_list

View File

@ -1,7 +1,7 @@
#ifndef VSF_VERSION_H
#define VSF_VERSION_H
#define VSF_VERSION "2.1.2"
#define VSF_VERSION "2.2.0"
#endif /* VSF_VERSION_H */