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

Updated to v2.0.7

This commit is contained in:
Dag Wieers 2008-07-30 00:00:00 +02:00
parent 2b30cb98d0
commit 19940a28a1
19 changed files with 238 additions and 42 deletions

View File

@ -975,3 +975,20 @@ to require fully valid / authentic client certs. No cert-based auth yet.
At this point: v2.0.6 released!
===============================
- Fix finding libcap for the link on Slackware systems, thanks to Roman
Kravchenko <roman@atech.lv>.
- Fix build on Solaris 2.8 due to non-standard C, thanks to IIDA Yosiaki
<y-iida@secom.co.jp>.
- Fix man page typo, thanks Matt Selsky <selsky@columbia.edu>.
- Bring the PASV listen() into the bind() retry loop to resolve a race under
extreme load. Thanks to Curtis Taylor <cjt@us.ibm.com>.
- Enhance logging for debug_ssl.
- Shutdown the SSL data connections properly. This prevents clients such as
recent FileZilla from complaining. Reported by various people.
- Add option to enforce proper SSL shutdown on uploads. Left it off after much
agonizing because clients are so broken in this area.
- Add option to delete failed uploads.
At this point: v2.0.7 released!
===============================

2
README
View File

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

View File

@ -51,9 +51,10 @@ static int write_dir_list(struct vsf_session* p_sess,
enum EVSFRWTarget target);
static unsigned int get_chunk_size();
void
int
vsf_ftpdataio_dispose_transfer_fd(struct vsf_session* p_sess)
{
int dispose_ret = 1;
int retval;
if (p_sess->data_fd == -1)
{
@ -64,7 +65,7 @@ vsf_ftpdataio_dispose_transfer_fd(struct vsf_session* p_sess)
vsf_sysutil_uninstall_io_handler();
if (p_sess->p_data_ssl != 0)
{
ssl_data_close(p_sess);
dispose_ret = ssl_data_close(p_sess);
}
/* This close() blocks because we set SO_LINGER */
retval = vsf_sysutil_close_failok(p_sess->data_fd);
@ -76,6 +77,7 @@ vsf_ftpdataio_dispose_transfer_fd(struct vsf_session* p_sess)
}
vsf_sysutil_clear_alarm();
p_sess->data_fd = -1;
return dispose_ret;
}
int

View File

@ -15,8 +15,11 @@ struct vsf_session;
* timeout goes off, or the connection is severed).
* PARAMETERS
* p_sess - the current FTP session object
* RETURNS
* 1 on success, 0 otherwise.
*
*/
void vsf_ftpdataio_dispose_transfer_fd(struct vsf_session* p_sess);
int vsf_ftpdataio_dispose_transfer_fd(struct vsf_session* p_sess);
/* vsf_ftpdataio_get_pasv_fd()
* PURPOSE

View File

@ -102,6 +102,10 @@ parseconf_bool_array[] =
{ "debug_ssl", &tunable_debug_ssl },
{ "require_cert", &tunable_require_cert },
{ "validate_cert", &tunable_validate_cert },
{ "strict_ssl_read_eof", &tunable_strict_ssl_read_eof },
{ "strict_ssl_write_shutdown", &tunable_strict_ssl_write_shutdown },
{ "ssl_request_cert", &tunable_ssl_request_cert },
{ "delete_failed_uploads", &tunable_delete_failed_uploads },
{ 0, 0 }
};

View File

@ -568,19 +568,22 @@ handle_pasv(struct vsf_session* p_sess, int is_epsv)
retval = vsf_sysutil_bind(p_sess->pasv_listen_fd, s_p_sockaddr);
if (!vsf_sysutil_retval_is_error(retval))
{
break;
retval = vsf_sysutil_listen(p_sess->pasv_listen_fd, 1);
if (!vsf_sysutil_retval_is_error(retval))
{
break;
}
}
if (vsf_sysutil_get_error() == kVSFSysUtilErrADDRINUSE)
{
continue;
}
die("vsf_sysutil_bind");
die("vsf_sysutil_bind / listen");
}
if (!bind_retries)
{
die("vsf_sysutil_bind");
}
vsf_sysutil_listen(p_sess->pasv_listen_fd, 1);
if (is_epsv)
{
str_alloc_text(&s_pasv_res_str, "Entering Extended Passive Mode (|||");
@ -718,7 +721,10 @@ handle_retr(struct vsf_session* p_sess)
}
trans_ret = vsf_ftpdataio_transfer_file(p_sess, remote_fd,
opened_file, 0, is_ascii);
vsf_ftpdataio_dispose_transfer_fd(p_sess);
if (vsf_ftpdataio_dispose_transfer_fd(p_sess) != 1 && trans_ret.retval == 0)
{
trans_ret.retval = -2;
}
p_sess->transfer_size = trans_ret.transferred;
/* Log _after_ the blocking dispose call, so we get transfer times right */
if (trans_ret.retval == 0)
@ -863,7 +869,10 @@ handle_dir_common(struct vsf_session* p_sess, int full_details, int stat_cmd)
}
if (!stat_cmd)
{
vsf_ftpdataio_dispose_transfer_fd(p_sess);
if (vsf_ftpdataio_dispose_transfer_fd(p_sess) != 1 && retval == 0)
{
retval = -1;
}
}
if (stat_cmd)
{
@ -972,6 +981,8 @@ handle_upload_common(struct vsf_session* p_sess, int is_append, int is_unique)
struct vsf_transfer_ret trans_ret;
int new_file_fd;
int remote_fd;
int success = 0;
int created = 0;
filesize_t offset = p_sess->restart_pos;
p_sess->restart_pos = 0;
if (!data_transfer_checks_ok(p_sess))
@ -1018,6 +1029,7 @@ handle_upload_common(struct vsf_session* p_sess, int is_append, int is_unique)
vsf_cmdio_write(p_sess, FTP_UPLOADFAIL, "Could not create file.");
return;
}
created = 1;
vsf_sysutil_fstat(new_file_fd, &s_p_statbuf);
if (vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
{
@ -1075,11 +1087,14 @@ handle_upload_common(struct vsf_session* p_sess, int is_append, int is_unique)
trans_ret = vsf_ftpdataio_transfer_file(p_sess, remote_fd,
new_file_fd, 1, 0);
}
vsf_ftpdataio_dispose_transfer_fd(p_sess);
if (vsf_ftpdataio_dispose_transfer_fd(p_sess) != 1 && trans_ret.retval == 0)
{
trans_ret.retval = -2;
}
p_sess->transfer_size = trans_ret.transferred;
/* XXX - handle failure, delete file? */
if (trans_ret.retval == 0)
{
success = 1;
vsf_log_do_log(p_sess, 1);
}
if (trans_ret.retval == -1)
@ -1098,6 +1113,10 @@ handle_upload_common(struct vsf_session* p_sess, int is_append, int is_unique)
port_pasv_cleanup_out:
port_cleanup(p_sess);
pasv_cleanup(p_sess);
if (tunable_delete_failed_uploads && created && !success)
{
str_unlink(p_filename);
}
vsf_sysutil_close(new_file_fd);
}

View File

@ -44,6 +44,7 @@ vsf_privop_get_ftp_port_sock(struct vsf_session* p_sess)
*/
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, tunable_ftp_data_port);
retval = vsf_sysutil_bind(s, p_sockaddr);
@ -55,7 +56,7 @@ vsf_privop_get_ftp_port_sock(struct vsf_session* p_sess)
{
die("vsf_sysutil_bind");
}
double sleep_for = vsf_sysutil_get_random_byte();
sleep_for = vsf_sysutil_get_random_byte();
sleep_for /= 256.0;
sleep_for += 1.0;
vsf_sysutil_sleep(sleep_for);

View File

@ -51,11 +51,11 @@ ftp_write_str(const struct vsf_session* p_sess, const struct mystr* p_str,
}
int
ftp_read_data(const struct vsf_session* p_sess, char* p_buf, unsigned int len)
ftp_read_data(struct vsf_session* p_sess, char* p_buf, unsigned int len)
{
if (p_sess->data_use_ssl)
{
return ssl_read(p_sess->p_data_ssl, p_buf, len);
return ssl_read(p_sess, p_buf, len);
}
else
{

View File

@ -12,8 +12,7 @@ enum EVSFRWTarget
int ftp_write_str(const struct vsf_session* p_sess, const struct mystr* p_str,
enum EVSFRWTarget target);
int ftp_read_data(const struct vsf_session* p_sess, char* p_buf,
unsigned int len);
int ftp_read_data(struct vsf_session* p_sess, char* p_buf, unsigned int len);
int ftp_write_data(const struct vsf_session* p_sess, const char* p_buf,
unsigned int len);
void ftp_getline(const struct vsf_session* p_sess, struct mystr* p_str,

136
ssl.c
View File

@ -28,6 +28,7 @@
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/bio.h>
#include <errno.h>
static char* get_ssl_error();
static SSL* get_ssl(struct vsf_session* p_sess, int fd);
@ -38,6 +39,8 @@ static long bio_callback(
static int ssl_verify_callback(int verify_ok, X509_STORE_CTX* p_ctx);
static int ssl_cert_digest(
SSL* p_ssl, struct vsf_session* p_sess, struct mystr* p_str);
static void maybe_log_shutdown_state(struct vsf_session* p_sess);
static void maybe_log_ssl_error_state(struct vsf_session* p_sess, int ret);
static int ssl_inited;
static struct mystr debug_str;
@ -111,17 +114,23 @@ ssl_init(struct vsf_session* p_sess)
{
die("SSL: RNG is not seeded");
}
verify_option = SSL_VERIFY_PEER;
if (tunable_ssl_request_cert)
{
verify_option |= SSL_VERIFY_PEER;
}
if (tunable_require_cert)
{
verify_option |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
}
SSL_CTX_set_verify(p_ctx, verify_option, ssl_verify_callback);
if (tunable_ca_certs_file)
if (verify_option)
{
if (!SSL_CTX_load_verify_locations(p_ctx, tunable_ca_certs_file, NULL))
SSL_CTX_set_verify(p_ctx, verify_option, ssl_verify_callback);
if (tunable_ca_certs_file)
{
die("SSL: could not load verify file");
if (!SSL_CTX_load_verify_locations(p_ctx, tunable_ca_certs_file, NULL))
{
die("SSL: could not load verify file");
}
}
}
p_sess->p_ssl_ctx = p_ctx;
@ -207,11 +216,12 @@ void
ssl_getline(const struct vsf_session* p_sess, struct mystr* p_str,
char end_char, char* p_buf, unsigned int buflen)
{
char* p_buf_start;
if (buflen == 0)
{
return;
}
char* p_buf_start = p_buf;
p_buf_start = p_buf;
p_buf[buflen - 1] = end_char;
buflen--;
while (1)
@ -238,17 +248,30 @@ ssl_getline(const struct vsf_session* p_sess, struct mystr* p_str,
}
int
ssl_read(void* p_ssl, char* p_buf, unsigned int len)
ssl_read(struct vsf_session* p_sess, char* p_buf, unsigned int len)
{
int retval;
int err;
SSL* p_ssl = p_sess->p_data_ssl;
do
{
retval = SSL_read((SSL*) p_ssl, p_buf, len);
err = SSL_get_error((SSL*) p_ssl, retval);
retval = SSL_read(p_ssl, p_buf, len);
err = SSL_get_error(p_ssl, retval);
}
while (retval < 0 && (err == SSL_ERROR_WANT_READ ||
err == SSL_ERROR_WANT_WRITE));
// If we hit an EOF, make sure it was from the peer, not injected by the
// attacker.
if (retval == 0 && SSL_get_shutdown(p_ssl) != SSL_RECEIVED_SHUTDOWN)
{
str_alloc_text(&debug_str, "Connection terminated without SSL shutdown "
"- buggy client?");
vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
if (tunable_strict_ssl_read_eof)
{
return -1;
}
}
return retval;
}
@ -279,14 +302,93 @@ ssl_write_str(void* p_ssl, const struct mystr* p_str)
return 0;
}
void
static void
maybe_log_shutdown_state(struct vsf_session* p_sess)
{
if (tunable_debug_ssl)
{
int ret = SSL_get_shutdown(p_sess->p_data_ssl);
str_alloc_text(&debug_str, "SSL shutdown state is: ");
if (ret == 0)
{
str_append_text(&debug_str, "NONE");
}
else if (ret == SSL_SENT_SHUTDOWN)
{
str_append_text(&debug_str, "SSL_SENT_SHUTDOWN");
}
else if (ret == SSL_RECEIVED_SHUTDOWN)
{
str_append_text(&debug_str, "SSL_RECEIVED_SHUTDOWN");
}
else
{
str_append_ulong(&debug_str, ret);
}
vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
}
}
static void
maybe_log_ssl_error_state(struct vsf_session* p_sess, int ret)
{
if (tunable_debug_ssl)
{
str_alloc_text(&debug_str, "SSL ret: ");
str_append_ulong(&debug_str, ret);
str_append_text(&debug_str, ", SSL error: ");
str_append_text(&debug_str, get_ssl_error());
str_append_text(&debug_str, ", errno: ");
str_append_ulong(&debug_str, errno);
vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
}
}
int
ssl_data_close(struct vsf_session* p_sess)
{
if (p_sess->p_data_ssl)
int success = 1;
SSL* p_ssl = p_sess->p_data_ssl;
if (p_ssl)
{
SSL_free(p_sess->p_data_ssl);
int ret;
maybe_log_shutdown_state(p_sess);
// This is a mess. Ideally, when we're the sender, we'd like to get to the
// SSL_RECEIVED_SHUTDOWN state to get a cryptographic guarantee that the
// peer received all the data and shut the connection down cleanly. It
// doesn't matter hugely apart from logging, but it's a nagging detail.
// Unfortunately, no FTP client I found was able to get sends into that
// state, so the best we can do is issue SSL_shutdown but not check the
// errors / returns. At least this enables the receiver to be sure of the
// integrity of the send in terms of unwanted truncation.
ret = SSL_shutdown(p_ssl);
maybe_log_shutdown_state(p_sess);
if (ret == 0)
{
ret = SSL_shutdown(p_ssl);
maybe_log_shutdown_state(p_sess);
if (ret != 1)
{
if (tunable_strict_ssl_write_shutdown)
{
success = 0;
}
maybe_log_shutdown_state(p_sess);
maybe_log_ssl_error_state(p_sess, ret);
}
}
else if (ret < 0)
{
if (tunable_strict_ssl_write_shutdown)
{
success = 0;
}
maybe_log_ssl_error_state(p_sess, ret);
}
SSL_free(p_ssl);
p_sess->p_data_ssl = NULL;
}
return success;
}
int
@ -296,11 +398,12 @@ ssl_accept(struct vsf_session* p_sess, int fd)
* protocol. If a client sends an unfortunately optional client cert then
* we can check for a match between the control and data connections.
*/
SSL* p_ssl;
if (p_sess->p_data_ssl != NULL)
{
die("p_data_ssl should be NULL.");
}
SSL* p_ssl = get_ssl(p_sess, fd);
p_ssl = get_ssl(p_sess, fd);
if (p_ssl == NULL)
{
return 0;
@ -549,9 +652,9 @@ ssl_getline(const struct vsf_session* p_sess, struct mystr* p_str,
}
int
ssl_read(void* p_ssl, char* p_buf, unsigned int len)
ssl_read(struct vsf_session* p_sess, char* p_buf, unsigned int len)
{
(void) p_ssl;
(void) p_sess;
(void) p_buf;
(void) len;
return -1;
@ -582,10 +685,11 @@ ssl_accept(struct vsf_session* p_sess, int fd)
return -1;
}
void
int
ssl_data_close(struct vsf_session* p_sess)
{
(void) p_sess;
return 1;
}
void

4
ssl.h
View File

@ -6,12 +6,12 @@ struct mystr;
void ssl_getline(const struct vsf_session* p_sess, struct mystr* p_str,
char end_char, char* p_buf, unsigned int buflen);
int ssl_read(void* p_ssl, char* p_buf, unsigned int len);
int ssl_read(struct vsf_session* p_sess, char* p_buf, unsigned int len);
int ssl_write(void* p_ssl, const char* p_buf, unsigned int len);
int ssl_write_str(void* p_ssl, const struct mystr* p_str);
void ssl_init(struct vsf_session* p_sess);
int ssl_accept(struct vsf_session* p_sess, int fd);
void ssl_data_close(struct vsf_session* p_sess);
int ssl_data_close(struct vsf_session* p_sess);
void ssl_comm_channel_init(struct vsf_session* p_sess);
void handle_auth(struct vsf_session* p_sess);
void handle_pbsz(struct vsf_session* p_sess);

View File

@ -130,7 +130,11 @@ vsf_standalone_main(void)
die("could not bind listening IPv6 socket");
}
}
vsf_sysutil_listen(listen_sock, VSFTP_LISTEN_BACKLOG);
retval = vsf_sysutil_listen(listen_sock, VSFTP_LISTEN_BACKLOG);
if (vsf_sysutil_retval_is_error(retval))
{
die("could not listen");
}
vsf_sysutil_sockaddr_alloc(&p_accept_addr);
while (1)
{

View File

@ -917,10 +917,11 @@ vsf_sysutil_isdigit(int the_char)
char*
vsf_sysutil_getcwd(char* p_dest, const unsigned int buf_size)
{
char* p_retval;
if (buf_size == 0) {
return p_dest;
}
char* p_retval = getcwd(p_dest, buf_size);
p_retval = getcwd(p_dest, buf_size);
p_dest[buf_size - 1] = '\0';
return p_retval;
}
@ -1515,10 +1516,11 @@ vsf_sysutil_unlock_file(int fd)
int
vsf_sysutil_readlink(const char* p_filename, char* p_dest, unsigned int bufsiz)
{
int retval;
if (bufsiz == 0) {
return -1;
}
int retval = readlink(p_filename, p_dest, bufsiz - 1);
retval = readlink(p_filename, p_dest, bufsiz - 1);
if (retval < 0)
{
return retval;
@ -1620,14 +1622,16 @@ vsf_sysutil_bind(int fd, const struct vsf_sysutil_sockaddr* p_sockptr)
return bind(fd, p_sockaddr, len);
}
void
int
vsf_sysutil_listen(int fd, const unsigned int backlog)
{
int retval = listen(fd, backlog);
if (retval != 0)
if (vsf_sysutil_retval_is_error(retval) &&
vsf_sysutil_get_error() != kVSFSysUtilErrADDRINUSE)
{
die("listen");
}
return retval;
}
/* Warning: callers of this function assume it does NOT make use of any

View File

@ -239,7 +239,7 @@ int vsf_sysutil_get_ipv6_sock(void);
struct vsf_sysutil_socketpair_retval
vsf_sysutil_unix_stream_socketpair(void);
int vsf_sysutil_bind(int fd, const struct vsf_sysutil_sockaddr* p_sockptr);
void vsf_sysutil_listen(int fd, const unsigned int backlog);
int vsf_sysutil_listen(int fd, const unsigned int backlog);
void vsf_sysutil_getsockname(int fd, struct vsf_sysutil_sockaddr** p_sockptr);
void vsf_sysutil_getpeername(int fd, struct vsf_sysutil_sockaddr** p_sockptr);
int vsf_sysutil_accept_timeout(int fd, struct vsf_sysutil_sockaddr* p_sockaddr,

View File

@ -74,6 +74,10 @@ int tunable_pasv_addr_resolve = 0;
int tunable_debug_ssl = 0;
int tunable_require_cert = 0;
int tunable_validate_cert = 0;
int tunable_strict_ssl_read_eof = 0;
int tunable_strict_ssl_write_shutdown = 0;
int tunable_ssl_request_cert = 1;
int tunable_delete_failed_uploads = 0;
unsigned int tunable_accept_timeout = 60;
unsigned int tunable_connect_timeout = 60;

View File

@ -70,6 +70,10 @@ extern int tunable_pasv_addr_resolve; /* DNS resolve pasv_addr */
extern int tunable_debug_ssl; /* Verbose SSL logging */
extern int tunable_require_cert; /* SSL client cert required */
extern int tunable_validate_cert; /* SSL certs must be valid */
extern int tunable_strict_ssl_read_eof; /* Need SSL_shutdown() on read */
extern int tunable_strict_ssl_write_shutdown; /* Need SSL_shutdown() on write */
extern int tunable_ssl_request_cert; /* Ask client for cert */
extern int tunable_delete_failed_uploads; /* Delete an upload that failed */
/* Integer/numeric defines */
extern unsigned int tunable_accept_timeout;

View File

@ -45,6 +45,7 @@ locate_library /usr/lib/libsec.sl && echo "-lsec";
# Look for libcap (capabilities)
locate_library /lib/libcap.so.1 && echo "/lib/libcap.so.1";
locate_library /usr/lib/libcap.so && echo "-lcap";
locate_library /lib/libcap.so && echo "-lcap";
# Solaris needs this for nanosleep()..
locate_library /lib/libposix4.so && echo "-lposix4";

View File

@ -167,6 +167,11 @@ Default: NO (but the sample config file enables it)
If true, OpenSSL connection diagnostics are dumped to the vsftpd log file.
(Added in v2.0.6).
Default: NO
.TP
.B delete_failed_uploads
If true, any failed upload files are deleted. (Added in v2.0.7).
Default: NO
.TP
.B deny_email_enable
@ -445,6 +450,14 @@ OpenSSL library.
Default: NO
.TP
.B ssl_request_cert
If enabled, vsftpd will request (but not necessarily require; see
.BR require_cert) a certificate on incoming SSL connections. Normally this
should not cause any trouble at all, but IBM zOS seems to have issues.
(New in v2.0.7).
Default: YES
.TP
.B ssl_sslv2
Only applies if
.BR ssl_enable
@ -469,6 +482,23 @@ TLS v1 connections are preferred.
Default: YES
.TP
.B strict_ssl_read_eof
If enabled, SSL data uploads are required to terminate via SSL, not an
EOF on the socket. This option is required to be sure that an attacker did
not terminate an upload prematurely with a faked TCP FIN. Unfortunately, it
is not enabled by default because so few clients get it right. (New in v2.0.7).
Default: NO
.TP
.B strict_ssl_write_shutdown
If enabled, SSL data downloads are required to terminate via SSL, not an
EOF on the socket. This is off by default as I was unable to find a single
FTP client that does this. It is minor. All it affects is our ability to tell
whether the client confirmed full receipt of the file. Even without this option,
the client is able to check the integrity of the download. (New in v2.0.7).
Default: NO
.TP
.B syslog_enable
If enabled, then any log output which would have gone to /var/log/vsftpd.log
goes to the system log instead. Logging is done under the FTPD facility.
@ -915,7 +945,7 @@ Default: /usr/share/empty
.TP
.B ssl_ciphers
This option can be used to select which SSL ciphers vsftpd will allow for
encrpyted SSL connections. See the
encrypted SSL connections. See the
.BR ciphers
man page for further details. Note that restricting ciphers can be a useful
security precaution as it prevents malicious remote parties forcing a cipher

View File

@ -1,7 +1,7 @@
#ifndef VSF_VERSION_H
#define VSF_VERSION_H
#define VSF_VERSION "2.0.6"
#define VSF_VERSION "2.0.7"
#endif /* VSF_VERSION_H */