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

Updated to v2.0.6

This commit is contained in:
Dag Wieers 2008-02-13 00:00:00 +01:00
parent c4e45fe8ac
commit 2b30cb98d0
24 changed files with 446 additions and 49 deletions

View File

@ -935,3 +935,43 @@ fix.
- So, timezone and daylight are not available on BSD, so redo the whole TZ
thing again. Should use only very portable constructs now.
At this point: v2.0.5 released!
===============================
- Fix delay_failed_login typo. Oops.
- Patch the getcwd and readlink sysutil helpers to reflect that they wouldn't
like a 0-sized buf. No caller is affected. Thanks Ilja van Sprundel
<ilja@suresec.org>.
- Allow a (fake) reauth as the same user as the logged in user. Should resolve
.NET related report from Sabo Jim <Jim.Sabo@thomson.net>.
- Tweak from Lucian Adrian Grijincu <lucian.grijincu@gmail.com> to take
unnecessary port calculations out of a loop.
- Fix byte I/O accounting in the error path of do_file_send_rwloop, thanks to
<echen@siac.com>.
- Don't log FireFox's attempts to RETR directories! Reported by
Nixdorf, Tim <tnixdorf@dnps.com>.
- Fix STOU sending the same 150 status line twice - oops! Reported by
<yamazaki@iij.ad.jp>.
- Fix xferlog format for virtual (guest) users, reported by Andy Fletcher
<andy@withnail.org>.
- Fix bug with empty user list file and userlist_deny=NO. Reported by
Marcin Zawadzki/GlobalVanet.com <marcin.zawadzki@globalvanet.com>.
- Pretend we have proper UTF8 support and respond positively to OPTS UTF8 ON.
Thanks Stanislav Maslovski <stanislav.maslovski@gmail.com>.
- Add control over the file permissions used in the chown()ing of anonymous
uploads: chown_upload_mode (default 0600 as before). Suggestion from
An Pham <apham@medforcetech.com>.
- Do a retry getting the active ftp socket in vsf_privop_get_ftp_port_sock();
should help buggy Solaris systems. Reported by Michael Masterson
<mjmasterson@xo.com>.
- Add debug_ssl option to dump out some SSL connection details.
- Use code 522, not 521, to indicate that the server requires an encrypted
data connection. Still does not seem to coax lftp to retry :(
- Recognize OPTS pre-login.
- A whole ton of SSL improvements, including ability to force requirement of
a client cert; data and control channel client cert cross checking. Ability
to require fully valid / authentic client certs. No cert-based auth yet.
- Change my e-mail to my GMail account.
At this point: v2.0.6 released!
===============================

View File

@ -13,7 +13,7 @@ OBJS = main.o utility.o prelogin.o ftpcmdio.o postlogin.o privsock.o \
postprivparent.o logging.o str.o netstr.o sysstr.o strlist.o \
banner.o filestr.o parseconf.o secutil.o \
ascii.o oneprocess.o twoprocess.o privops.o standalone.o hash.o \
tcpwrap.o ipaddrparse.o access.o features.o readwrite.o \
tcpwrap.o ipaddrparse.o access.o features.o readwrite.o opts.o \
ssl.o sysutil.o sysdeputil.o

4
README
View File

@ -1,6 +1,6 @@
This is vsftpd, version 2.0.5
This is vsftpd, version 2.0.6
Author: Chris Evans
Contact: chris@scary.beasts.org
Contact: scarybeasts@gmail.com
Website: http://vsftpd.beasts.org/
- All options are documented in the vsftpd.conf.5 manual page.
- See the FAQ file for solutions to frequently asked questions.

1
TODO
View File

@ -13,7 +13,6 @@ NOT SO CRITICAL
- "add_group" support.
- Implict SSL support (port 990?)
- Do something with client certs.
- Add CHLD handler to priv parent process to take care of session crashes?
- Still reports FlashFXP broken when trying to do FXP.
- Add negation, other support to regex handler.

View File

@ -42,6 +42,7 @@ handle_feat(struct vsf_session* p_sess)
vsf_cmdio_write_raw(p_sess, " REST STREAM\r\n");
vsf_cmdio_write_raw(p_sess, " SIZE\r\n");
vsf_cmdio_write_raw(p_sess, " TVFS\r\n");
vsf_cmdio_write_raw(p_sess, " UTF8\r\n");
vsf_cmdio_write(p_sess, FTP_FEAT, "End");
}

View File

@ -14,6 +14,7 @@
#define FTP_MODEOK 200
#define FTP_PBSZOK 200
#define FTP_PROTOK 200
#define FTP_OPTSOK 200
#define FTP_ALLOOK 202
#define FTP_FEAT 211
#define FTP_STATOK 211
@ -64,7 +65,7 @@
#define FTP_BADMODE 504
#define FTP_BADAUTH 504
#define FTP_NOSUCHPROT 504
#define FTP_NEEDENCRYPT 521
#define FTP_NEEDENCRYPT 522
#define FTP_EPSVBAD 522
#define FTP_DATATLSBAD 522
#define FTP_LOGINERR 530

View File

@ -478,13 +478,16 @@ do_file_send_rwloop(struct vsf_session* p_sess, int file_fd, int is_ascii)
num_to_write = (unsigned int) retval;
}
retval = ftp_write_data(p_sess, p_writefrom_buf, num_to_write);
if (!vsf_sysutil_retval_is_error(retval))
{
ret_struct.transferred += (unsigned int) retval;
}
if (vsf_sysutil_retval_is_error(retval) ||
(unsigned int) retval != num_to_write)
{
ret_struct.retval = -2;
return ret_struct;
}
ret_struct.transferred += (unsigned int) retval;
}
}

View File

@ -105,6 +105,12 @@ vsf_log_entry_pending(struct vsf_session* p_sess)
return 1;
}
void
vsf_log_clear_entry(struct vsf_session* p_sess)
{
p_sess->log_type = 0;
}
void
vsf_log_do_log(struct vsf_session* p_sess, int succeeded)
{
@ -211,14 +217,21 @@ vsf_log_do_log_wuftpd_format(struct vsf_session* p_sess, struct mystr* p_str,
str_append_text(p_str, "o ");
}
/* Access mode: anonymous/real user, and identity */
if (p_sess->is_anonymous)
if (p_sess->is_anonymous && !p_sess->is_guest)
{
str_append_text(p_str, "a ");
str_append_str(p_str, &p_sess->anon_pass_str);
}
else
{
str_append_text(p_str, "r ");
if (p_sess->is_guest)
{
str_append_text(p_str, "g ");
}
else
{
str_append_text(p_str, "r ");
}
str_append_str(p_str, &p_sess->user_str);
}
str_append_char(p_str, ' ');
@ -255,7 +268,7 @@ vsf_log_do_log_vsftpd_format(struct vsf_session* p_sess, struct mystr* p_str,
}
/* And the action */
if (what != kVSFLogEntryFTPInput && what != kVSFLogEntryFTPOutput &&
what != kVSFLogEntryConnection)
what != kVSFLogEntryConnection && what != kVSFLogEntryDebug)
{
if (succeeded)
{
@ -301,6 +314,9 @@ vsf_log_do_log_vsftpd_format(struct vsf_session* p_sess, struct mystr* p_str,
case kVSFLogEntryChmod:
str_append_text(p_str, "CHMOD");
break;
case kVSFLogEntryDebug:
str_append_text(p_str, "DEBUG");
break;
default:
bug("bad entry_type in vsf_log_do_log");
break;
@ -320,7 +336,8 @@ vsf_log_do_log_vsftpd_format(struct vsf_session* p_sess, struct mystr* p_str,
str_append_str(p_str, p_log_str);
str_append_char(p_str, '"');
}
if (what != kVSFLogEntryFTPInput && what != kVSFLogEntryFTPOutput)
if (what != kVSFLogEntryFTPInput && what != kVSFLogEntryFTPOutput &&
what != kVSFLogEntryDebug)
{
if (p_sess->transfer_size)
{

View File

@ -18,7 +18,8 @@ enum EVSFLogEntryType
kVSFLogEntryDelete,
kVSFLogEntryRename,
kVSFLogEntryRmdir,
kVSFLogEntryChmod
kVSFLogEntryChmod,
kVSFLogEntryDebug,
};
/* vsf_log_init()
@ -49,6 +50,12 @@ void vsf_log_start_entry(struct vsf_session* p_sess,
*/
int vsf_log_entry_pending(struct vsf_session* p_sess);
/* vsf_log_clear_entry()
* PURPOSE
* Clears any pending log entry.
*/
void vsf_log_clear_entry(struct vsf_session* p_sess);
/* vsf_log_do_log()
* PURPOSE
* Denote the end of a logged operation, specifying whether the operation

10
main.c
View File

@ -41,7 +41,7 @@ main(int argc, const char* argv[])
/* Data connection */
-1, 0, -1, 0, 0, 0, 0,
/* Login */
1, INIT_MYSTR, INIT_MYSTR,
1, 0, INIT_MYSTR, INIT_MYSTR,
/* Protocol state */
0, 1, INIT_MYSTR, 0, 0,
/* Session state */
@ -61,7 +61,7 @@ main(int argc, const char* argv[])
/* Home directory */
INIT_MYSTR,
/* Secure connection state */
0, 0, 0, 0, 0, 0, -1, -1,
0, 0, 0, 0, 0, INIT_MYSTR, 0, -1, -1,
/* Login fails */
0
};
@ -128,7 +128,7 @@ main(int argc, const char* argv[])
vsf_sysutil_setproctitle_init(argc, argv);
}
/* Initialize the SSL system here if needed - saves the overhead of each
* child doing this itself.
* child doing this itself.
*/
if (tunable_ssl_enable)
{
@ -270,6 +270,10 @@ do_sanity_checks(void)
{
die("vsftpd: security: 'one_process_model' needs a better OS");
}
if (tunable_ssl_enable)
{
die("vsftpd: SSL mode not compatible with 'one_process_model'");
}
}
if (!tunable_local_enable && !tunable_anonymous_enable)
{

27
opts.c Normal file
View File

@ -0,0 +1,27 @@
/*
* Part of Very Secure FTPd
* Licence: GPL v2
* Author: Chris Evans
* opts.c
*
* Routines to handle OPTS.
*/
#include "ftpcodes.h"
#include "ftpcmdio.h"
#include "session.h"
void
handle_opts(struct vsf_session* p_sess)
{
str_upper(&p_sess->ftp_arg_str);
if (str_equal_text(&p_sess->ftp_arg_str, "UTF8 ON"))
{
vsf_cmdio_write(p_sess, FTP_OPTSOK, "Always in UTF8 mode.");
}
else
{
vsf_cmdio_write(p_sess, FTP_BADOPTS, "Option not understood.");
}
}

9
opts.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef VSF_OPTS_H
#define VSF_OPTS_H
struct vsf_session;
void handle_opts(struct vsf_session* p_sess);
#endif /* VSF_OPTS_H */

View File

@ -99,6 +99,9 @@ parseconf_bool_array[] =
{ "mdtm_write", &tunable_mdtm_write },
{ "lock_upload_files", &tunable_lock_upload_files },
{ "pasv_addr_resolve", &tunable_pasv_addr_resolve },
{ "debug_ssl", &tunable_debug_ssl },
{ "require_cert", &tunable_require_cert },
{ "validate_cert", &tunable_validate_cert },
{ 0, 0 }
};
@ -128,6 +131,7 @@ parseconf_uint_array[] =
{ "delay_failed_login", &tunable_delay_failed_login },
{ "delay_successful_login", &tunable_delay_successful_login },
{ "max_login_fails", &tunable_max_login_fails },
{ "chown_upload_mode", &tunable_chown_upload_mode },
{ 0, 0 }
};
@ -168,6 +172,7 @@ parseconf_str_array[] =
{ "ssl_ciphers", &tunable_ssl_ciphers },
{ "rsa_private_key_file", &tunable_rsa_private_key_file },
{ "dsa_private_key_file", &tunable_dsa_private_key_file },
{ "ca_certs_file", &tunable_ca_certs_file },
{ 0, 0 }
};

View File

@ -26,6 +26,7 @@
#include "features.h"
#include "ssl.h"
#include "vsftpver.h"
#include "opts.h"
/* Private local functions */
static void handle_pwd(struct vsf_session* p_sess);
@ -57,6 +58,8 @@ static void handle_help(struct vsf_session* p_sess);
static void handle_stou(struct vsf_session* p_sess);
static void handle_stat(struct vsf_session* p_sess);
static void handle_stat_file(struct vsf_session* p_sess);
static void handle_logged_in_user(struct vsf_session* p_sess);
static void handle_logged_in_pass(struct vsf_session* p_sess);
static int pasv_active(struct vsf_session* p_sess);
static int port_active(struct vsf_session* p_sess);
@ -340,7 +343,7 @@ process_post_login(struct vsf_session* p_sess)
}
else if (str_equal_text(&p_sess->ftp_cmd_str, "OPTS"))
{
vsf_cmdio_write(p_sess, FTP_BADOPTS, "Option not understood.");
handle_opts(p_sess);
}
else if (str_equal_text(&p_sess->ftp_cmd_str, "STAT") &&
str_isempty(&p_sess->ftp_arg_str))
@ -360,6 +363,14 @@ process_post_login(struct vsf_session* p_sess)
{
handle_prot(p_sess);
}
else if (str_equal_text(&p_sess->ftp_cmd_str, "USER"))
{
handle_logged_in_user(p_sess);
}
else if (str_equal_text(&p_sess->ftp_cmd_str, "PASS"))
{
handle_logged_in_pass(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") ||
@ -499,6 +510,9 @@ handle_pasv(struct vsf_session* p_sess, int is_epsv)
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))
{
@ -528,21 +542,20 @@ handle_pasv(struct vsf_session* p_sess, int is_epsv)
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;
/* IPPORT_RESERVED */
unsigned short min_port = 1024;
unsigned short max_port = 65535;
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;
}
the_port = vsf_sysutil_get_random_byte();
the_port <<= 8;
the_port |= vsf_sysutil_get_random_byte();
@ -657,6 +670,13 @@ handle_retr(struct vsf_session* p_sess)
{
/* Note - pretend open failed */
vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Failed to open file.");
/* Irritating FireFox does RETR on directories, so avoid logging this
* very common and noisy case.
*/
if (vsf_sysutil_statbuf_is_dir(s_p_statbuf))
{
vsf_log_clear_entry(p_sess);
}
goto file_close_out;
}
/* Now deactive O_NONBLOCK, otherwise we have a problem on DMAPI filesystems
@ -1009,7 +1029,7 @@ handle_upload_common(struct vsf_session* p_sess, int is_append, int is_unique)
/* Are we required to chown() this file for security? */
if (p_sess->is_anonymous && tunable_chown_uploads)
{
vsf_sysutil_fchmod(new_file_fd, 0600);
vsf_sysutil_fchmod(new_file_fd, tunable_chown_upload_mode);
if (tunable_one_process_model)
{
vsf_one_process_chown_upload(p_sess, new_file_fd);
@ -1034,7 +1054,6 @@ handle_upload_common(struct vsf_session* p_sess, int is_append, int is_unique)
struct mystr resp_str = INIT_MYSTR;
str_alloc_text(&resp_str, "FILE: ");
str_append_str(&resp_str, p_filename);
vsf_cmdio_write_str(p_sess, FTP_DATACONN, &resp_str);
remote_fd = get_remote_transfer_fd(p_sess, str_getbuf(&resp_str));
str_free(&resp_str);
}
@ -1684,7 +1703,9 @@ handle_stou(struct vsf_session* p_sess)
static void
get_unique_filename(struct mystr* p_outstr, const struct mystr* p_base_str)
{
/* Use silly wu-ftpd algorithm for compatibility */
/* Use silly wu-ftpd algorithm for compatibility. It has races of course, if
* two sessions are using the same file prefix at the same time.
*/
static struct vsf_sysutil_statbuf* s_p_statbuf;
unsigned int suffix = 1;
int retval;
@ -1828,3 +1849,23 @@ resolve_tilde(struct mystr* p_str, struct vsf_session* p_sess)
}
}
static void handle_logged_in_user(struct vsf_session* p_sess)
{
if (p_sess->is_anonymous)
{
vsf_cmdio_write(p_sess, FTP_LOGINERR, "Can't change from guest user.");
}
else if (str_equal(&p_sess->user_str, &p_sess->ftp_arg_str))
{
vsf_cmdio_write(p_sess, FTP_GIVEPWORD, "Any password will do.");
}
else
{
vsf_cmdio_write(p_sess, FTP_LOGINERR, "Can't change to another user.");
}
}
static void handle_logged_in_pass(struct vsf_session* p_sess)
{
vsf_cmdio_write(p_sess, FTP_LOGINOK, "Already logged in.");
}

View File

@ -23,6 +23,7 @@
#include "ssl.h"
#include "features.h"
#include "defs.h"
#include "opts.h"
/* Functions used */
static void emit_greeting(struct vsf_session* p_sess);
@ -116,6 +117,10 @@ parse_username_password(struct vsf_session* p_sess)
{
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"))
{
handle_auth(p_sess);
@ -182,7 +187,7 @@ handle_user_command(struct vsf_session* p_sess)
str_empty(&p_sess->user_str);
return;
}
if (!str_isempty(&p_sess->userlist_str))
if (tunable_userlist_enable)
{
int located = str_contains_line(&p_sess->userlist_str, &p_sess->user_str);
if ((located && tunable_userlist_deny) ||

View File

@ -36,14 +36,29 @@ vsf_privop_get_ftp_port_sock(struct vsf_session* p_sess)
{
static struct vsf_sysutil_sockaddr* p_sockaddr;
int retval;
int i;
int s = vsf_sysutil_get_ipsock(p_sess->p_local_addr);
vsf_sysutil_activate_reuseaddr(s);
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);
if (retval != 0)
/* A report of failure here on Solaris, presumably buggy address reuse
* support? We'll retry.
*/
for (i = 0; i < 2; ++i)
{
die("vsf_sysutil_bind");
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);
if (retval == 0)
{
return s;
}
if (vsf_sysutil_get_error() != kVSFSysUtilErrADDRINUSE || i == 1)
{
die("vsf_sysutil_bind");
}
double sleep_for = vsf_sysutil_get_random_byte();
sleep_for /= 256.0;
sleep_for += 1.0;
vsf_sysutil_sleep(sleep_for);
}
return s;
}

View File

@ -33,6 +33,7 @@ struct vsf_session
/* Details of the login */
int is_anonymous;
int is_guest;
struct mystr user_str;
struct mystr anon_pass_str;
@ -87,6 +88,7 @@ struct vsf_session
void* p_ssl_ctx;
void* p_control_ssl;
void* p_data_ssl;
struct mystr control_cert_digest;
int ssl_slave_active;
int ssl_slave_fd;
int ssl_consumer_fd;

181
ssl.c
View File

@ -20,6 +20,7 @@
#include "tunables.h"
#include "utility.h"
#include "builddefs.h"
#include "logging.h"
#ifdef VSF_BUILD_SSL
@ -34,8 +35,12 @@ static int ssl_session_init(struct vsf_session* p_sess);
static void setup_bio_callbacks();
static long bio_callback(
BIO* p_bio, int oper, const char* p_arg, int argi, long argl, long retval);
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 int ssl_inited;
static struct mystr debug_str;
void
ssl_init(struct vsf_session* p_sess)
@ -44,6 +49,7 @@ ssl_init(struct vsf_session* p_sess)
{
SSL_CTX* p_ctx;
long options;
int verify_option = 0;
SSL_library_init();
p_ctx = SSL_CTX_new(SSLv23_server_method());
if (p_ctx == NULL)
@ -105,6 +111,19 @@ ssl_init(struct vsf_session* p_sess)
{
die("SSL: RNG is not seeded");
}
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 (!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;
ssl_inited = 1;
}
@ -124,6 +143,7 @@ handle_auth(struct vsf_session* 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);
@ -187,17 +207,26 @@ void
ssl_getline(const struct vsf_session* p_sess, struct mystr* p_str,
char end_char, char* p_buf, unsigned int buflen)
{
if (buflen == 0)
{
return;
}
char* p_buf_start = p_buf;
p_buf[buflen - 1] = '\0';
p_buf[buflen - 1] = end_char;
buflen--;
while (1)
{
/* Should I use SSL_peek here? Won't lack of it break pipelining? (SSL
* clients seem to work just fine, mind you, so maybe pipelining is banned
* over SSL connections. Also note that OpenSSL didn't always have
* SSL_peek).
*/
int retval = SSL_read(p_sess->p_control_ssl, p_buf, buflen);
if (retval <= 0)
{
die("SSL_read");
}
p_buf[retval] = '\0';
p_buf[retval] = end_char;
buflen -= retval;
if (p_buf[retval - 1] == end_char || buflen == 0)
{
@ -250,9 +279,27 @@ ssl_write_str(void* p_ssl, const struct mystr* p_str)
return 0;
}
void
ssl_data_close(struct vsf_session* p_sess)
{
if (p_sess->p_data_ssl)
{
SSL_free(p_sess->p_data_ssl);
p_sess->p_data_ssl = NULL;
}
}
int
ssl_accept(struct vsf_session* p_sess, int fd)
{
/* SECURITY: data SSL connections don't have any auth on them as part of the
* protocol. If a client sends an unfortunately optional client cert then
* we can check for a match between the control and data connections.
*/
if (p_sess->p_data_ssl != NULL)
{
die("p_data_ssl should be NULL.");
}
SSL* p_ssl = get_ssl(p_sess, fd);
if (p_ssl == NULL)
{
@ -260,15 +307,38 @@ ssl_accept(struct vsf_session* p_sess, int fd)
}
p_sess->p_data_ssl = p_ssl;
setup_bio_callbacks(p_ssl);
if (str_getlen(&p_sess->control_cert_digest) > 0)
{
static struct mystr data_cert_digest;
if (!ssl_cert_digest(p_ssl, p_sess, &data_cert_digest))
{
if (tunable_debug_ssl)
{
str_alloc_text(&debug_str, "Missing cert on data channel.");
vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
}
ssl_data_close(p_sess);
return 0;
}
if (str_strcmp(&p_sess->control_cert_digest, &data_cert_digest))
{
if (tunable_debug_ssl)
{
str_alloc_text(&debug_str, "DIFFERENT cert on data channel.");
vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
}
ssl_data_close(p_sess);
return 0;
}
if (tunable_debug_ssl)
{
str_alloc_text(&debug_str, "Matching cert on data channel.");
vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
}
}
return 1;
}
void
ssl_data_close(struct vsf_session* p_sess)
{
SSL_free(p_sess->p_data_ssl);
}
void
ssl_comm_channel_init(struct vsf_session* p_sess)
{
@ -284,19 +354,66 @@ get_ssl(struct vsf_session* p_sess, int fd)
SSL* p_ssl = SSL_new(p_sess->p_ssl_ctx);
if (p_ssl == NULL)
{
if (tunable_debug_ssl)
{
str_alloc_text(&debug_str, "SSL_new failed");
vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
}
return NULL;
}
if (!SSL_set_fd(p_ssl, fd))
{
if (tunable_debug_ssl)
{
str_alloc_text(&debug_str, "SSL_set_fd failed");
vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
}
SSL_free(p_ssl);
return NULL;
}
if (SSL_accept(p_ssl) != 1)
{
die(get_ssl_error());
const char* p_err = get_ssl_error();
if (tunable_debug_ssl)
{
str_alloc_text(&debug_str, "SSL_accept failed: ");
str_append_text(&debug_str, p_err);
vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
}
die(p_err);
SSL_free(p_ssl);
return NULL;
}
if (tunable_debug_ssl)
{
const char* p_ssl_version = SSL_get_cipher_version(p_ssl);
SSL_CIPHER* p_ssl_cipher = SSL_get_current_cipher(p_ssl);
const char* p_cipher_name = SSL_CIPHER_get_name(p_ssl_cipher);
int reused = SSL_session_reused(p_ssl);
X509* p_ssl_cert = SSL_get_peer_certificate(p_ssl);
str_alloc_text(&debug_str, "SSL version: ");
str_append_text(&debug_str, p_ssl_version);
str_append_text(&debug_str, ", SSL cipher: ");
str_append_text(&debug_str, p_cipher_name);
if (reused)
{
str_append_text(&debug_str, ", reused");
}
else
{
str_append_text(&debug_str, ", not reused");
}
if (p_ssl_cert != NULL)
{
str_append_text(&debug_str, ", CERT PRESENTED");
X509_free(p_ssl_cert);
}
else
{
str_append_text(&debug_str, ", no cert");
}
vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
}
return p_ssl;
}
@ -309,10 +426,45 @@ ssl_session_init(struct vsf_session* p_sess)
return 0;
}
p_sess->p_control_ssl = p_ssl;
(void) ssl_cert_digest(p_ssl, p_sess, &p_sess->control_cert_digest);
setup_bio_callbacks(p_ssl);
return 1;
}
static int
ssl_cert_digest(SSL* p_ssl, struct vsf_session* p_sess, struct mystr* p_str)
{
X509* p_cert = SSL_get_peer_certificate(p_ssl);
unsigned int num_bytes = 0;
if (p_cert == NULL)
{
return 0;
}
str_reserve(p_str, EVP_MAX_MD_SIZE);
str_empty(p_str);
str_rpad(p_str, EVP_MAX_MD_SIZE);
if (!X509_digest(p_cert, EVP_sha256(), (unsigned char*) str_getbuf(p_str),
&num_bytes))
{
die("X509_digest failed");
}
X509_free(p_cert);
if (tunable_debug_ssl)
{
unsigned int i;
str_alloc_text(&debug_str, "Cert digest:");
for (i = 0; i < num_bytes; ++i)
{
str_append_char(&debug_str, ' ');
str_append_ulong(
&debug_str, (unsigned long) (unsigned char) str_get_char_at(p_str, i));
}
vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
}
str_trunc(p_str, num_bytes);
return 1;
}
static char*
get_ssl_error()
{
@ -347,6 +499,17 @@ bio_callback(
return ret;
}
static int
ssl_verify_callback(int verify_ok, X509_STORE_CTX* p_ctx)
{
(void) p_ctx;
if (tunable_validate_cert)
{
return verify_ok;
}
return 1;
}
#else /* VSF_BUILD_SSL */
void

View File

@ -917,6 +917,9 @@ vsf_sysutil_isdigit(int the_char)
char*
vsf_sysutil_getcwd(char* p_dest, const unsigned int buf_size)
{
if (buf_size == 0) {
return p_dest;
}
char* p_retval = getcwd(p_dest, buf_size);
p_dest[buf_size - 1] = '\0';
return p_retval;
@ -1512,6 +1515,9 @@ vsf_sysutil_unlock_file(int fd)
int
vsf_sysutil_readlink(const char* p_filename, char* p_dest, unsigned int bufsiz)
{
if (bufsiz == 0) {
return -1;
}
int retval = readlink(p_filename, p_dest, bufsiz - 1);
if (retval < 0)
{

View File

@ -71,6 +71,9 @@ int tunable_force_anon_data_ssl = 0;
int tunable_mdtm_write = 1;
int tunable_lock_upload_files = 1;
int tunable_pasv_addr_resolve = 0;
int tunable_debug_ssl = 0;
int tunable_require_cert = 0;
int tunable_validate_cert = 0;
unsigned int tunable_accept_timeout = 60;
unsigned int tunable_connect_timeout = 60;
@ -94,6 +97,8 @@ unsigned int tunable_trans_chunk_size = 0;
unsigned int tunable_delay_failed_login = 1;
unsigned int tunable_delay_successful_login = 0;
unsigned int tunable_max_login_fails = 3;
/* -rw------- */
unsigned int tunable_chown_upload_mode = 0600;
const char* tunable_secure_chroot_dir = "/usr/share/empty";
const char* tunable_ftp_username = "ftp";
@ -125,4 +130,5 @@ const char* tunable_dsa_cert_file = 0;
const char* tunable_ssl_ciphers = "DES-CBC3-SHA";
const char* tunable_rsa_private_key_file = 0;
const char* tunable_dsa_private_key_file = 0;
const char* tunable_ca_certs_file = 0;

View File

@ -67,6 +67,9 @@ extern int tunable_force_anon_data_ssl; /* Require anon data uses SSL */
extern int tunable_mdtm_write; /* Allow MDTM to set timestamps */
extern int tunable_lock_upload_files; /* Lock uploading files */
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 */
/* Integer/numeric defines */
extern unsigned int tunable_accept_timeout;
@ -88,6 +91,7 @@ extern unsigned int tunable_trans_chunk_size;
extern unsigned int tunable_delay_failed_login;
extern unsigned int tunable_delay_successful_login;
extern unsigned int tunable_max_login_fails;
extern unsigned int tunable_chown_upload_mode;
/* String defines */
extern const char* tunable_secure_chroot_dir;
@ -120,6 +124,7 @@ extern const char* tunable_dsa_cert_file;
extern const char* tunable_ssl_ciphers;
extern const char* tunable_rsa_private_key_file;
extern const char* tunable_dsa_private_key_file;
extern const char* tunable_ca_certs_file;
#endif /* VSF_TUNABLES_H */

View File

@ -282,6 +282,7 @@ process_login_req(struct vsf_session* p_sess)
static void
process_ssl_slave_req(struct vsf_session* p_sess)
{
priv_sock_send_str(p_sess->ssl_slave_fd, &p_sess->control_cert_digest);
while (1)
{
char cmd = priv_sock_get_cmd(p_sess->ssl_slave_fd);
@ -342,9 +343,15 @@ common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
if (tunable_ssl_enable)
{
vsf_sysutil_close(p_sess->ssl_slave_fd);
if (p_sess->ssl_slave_active)
{
priv_sock_get_str(p_sess->ssl_consumer_fd,
&p_sess->control_cert_digest);
}
}
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;

View File

@ -163,6 +163,12 @@ slightly less privilege.
Default: NO (but the sample config file enables it)
.TP
.B debug_ssl
If true, OpenSSL connection diagnostics are dumped to the vsftpd log file.
(Added in v2.0.6).
Default: NO
.TP
.B deny_email_enable
If activated, you may provide a list of anonymous password e-mail responses
which cause login to be denied. By default, the file containing this list is
@ -282,7 +288,7 @@ downloads proceed with a shared read lock on the download file. WARNING!
Before enabling this, be aware that malicious readers could starve a writer
wanting to e.g. append a file.
Default: NO
Default: YES
.TP
.B log_ftp_protocol
When enabled, all FTP requests and responses are logged, providing the option
@ -369,6 +375,15 @@ Set to YES if you want to disable the PORT security check that ensures that
outgoing data connections can only connect to the client. Only enable if
you know what you are doing!
Default: NO
.TP
.B require_cert
If set to yes, all SSL client connections are required to present a client
certificate. The degree of validation applied to this certificate is
controlled by
.BR validate_cert
(Added in v2.0.6).
Default: NO
.TP
.B run_as_launching_user
@ -518,6 +533,12 @@ before they are asked for a password. This may be useful in preventing
cleartext passwords being transmitted. See also
.BR userlist_deny .
Default: NO
.TP
.B validate_cert
If set to yes, all SSL client certificates received must validate OK.
Self-signed certs do not constitute OK validation. (New in v2.0.6).
Default: NO
.TP
.B virtual_use_local_privs
@ -575,6 +596,11 @@ value will be treated as a base 10 integer!
Default: 077
.TP
.B chown_upload_mode
The file mode to force for chown()ed anonymous uploads. (Added in v2.0.6).
Default: 0600
.TP
.B connect_timeout
The timeout, in seconds, for a remote client to respond to our PORT style
data connection.
@ -588,12 +614,12 @@ client is kicked off.
Default: 300
.TP
.B delay_failed_logins
.B delay_failed_login
The number of seconds to pause prior to reporting a failed login.
Default: 1
.TP
.B delay_successful_logins
.B delay_successful_login
The number of seconds to pause prior to allowing a successful login.
Default: 0
@ -700,6 +726,14 @@ the
.BR ftpd_banner
option.
Default: (none)
.TP
.B ca_certs_file
This option is the name of a file to load Certificate Authority certs from, for
the purpose of validating client certs. Regrettably, the default SSL CA cert
paths are not used, because of vsftpd's use of restricted filesystem spaces
(chroot). (Added in v2.0.6).
Default: (none)
.TP
.B chown_username
@ -962,5 +996,5 @@ Alternatively, it is written if you have set the option
Default: /var/log/xferlog
.SH AUTHOR
chris@scary.beasts.org
scarybeasts@gmail.com

View File

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