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

Updated to v2.1.0

This commit is contained in:
Dag Wieers 2009-02-18 00:00:00 +01:00
parent 19940a28a1
commit 57529485ed
50 changed files with 3715 additions and 449 deletions

105
Changelog
View File

@ -992,3 +992,108 @@ agonizing because clients are so broken in this area.
At this point: v2.0.7 released!
===============================
- Remove .postlogin.c.swp (thanks Kaibin Li <kbli@fortinet.com>)!
- findlibs repairs for libcap; builds on my Ubuntu 6.06 again.
- Apply patch to fix "error: assignment of read-only member '__in'" build
error on broken systems where the WIFEXITED() etc. macros write to their
argument. Thanks Ingo Terpelle <it@exanto.de>.
- Replace spaces in xferlog with underscores, report from
Michael Wittauer <michael.wittauer@sonydadc.com>.
- Reload default config values before re-parsing config file on SIGHUP. This
makes the values correct in the case a setting was removed from the file.
- Do not issue an FTP response for a blank line on the control channel. Fixes
issues with some broken NAT devices. ProFTPd does the same, so hopefully
nothing will break. Report from Frank Bulk <fbulk@mypremieronline.com>.
- Replace usage of broken _syscall() with syscall(). Fixes build errors for
those without libcap-devel installed.
- Add implicit SSL support with implicit_ssl option.
- Remove arbitrary restriction on one process model + SSL.
- Set a session ID on the SSL context.
- Add the skeleton of a built-in ptrace sandbox. Not yet useful for anything
other than catching compile errors. Yes, I'm crazy :P
- Use PR_SET_PDEATHSIG all over the place so that when the listener is killed,
existing sessions are booted too.
- Use SSL_peek; makes SSL pipelining work. Note that I never found any SSL
client that need it, but still a nice code clean-up.
- Change ASCII download behaviour so \r\n does not become \r\r\n. This mirrors
proftpd behaviour instead of wu-ftpd. Thanks Paul Abel <pabel@fexcodcc.com>.
- Switch all sighandlers to the synchronous ones. Prevents us having to
block and unblock signals all the time.
- Add a "use alarm" option to synchronous signal handlers, to ensure the race
condition against a blocking call does not result in a permanent
non-delivery.
- Use SIGTERM for privileged parent process shutdown, so they can still update
u/wtmp properly.
- Do RAND_load_file from /dev/urandom in the child context because I don't
trust the OpenSSL API vs. fork(). Different children do have different RNG
state; this is defense in depth.
- More thoroughly close the remote ends of the priv_sock, ensuring that child
death results in no blocking in the parent. This is a matter of tidyness; the
SIGCHLD handler will reliably tear down the parent.
- Do the same for the SSL slave / consumer channels.
- Fix OpenBSD build.
- Move SSL data handling into the SSL slave process. Incurs some extra
overhead in terms of context switches and copies, but it enables this next
item:
- By default, require SSL data connections to exhibit SSL session re-use of
the control channel. Unlike the cert thing, this _is_ something we can turn
on by default as most clients seem to do reuse. Yay.
- Change 522 response for SSL connection fail to note when session reuse is
required.
(v2.1.0pre1 here)
- More work on the inbuilt ptrace()-sandbox support.
- Clear the idle alarm when starting data transfer if there is no data alarm.
- Fix syslog format; don't embed 2nd copy of date, pid. Thanks to
René Berber <r.berber@computer.org>.
- Lock file before truncating it for upload. Fixes various simultaneous
upload corruption issues.
- Make sure to give 426 error code on uploads if ABOR was received.
- Add cmds_denied option to complement cmds_allowed.
- Ignore lines in config file containing only white space.
- Require write_enable / anon_upload_enable / etc. to process STOU.
- FC10 patch (vsftpd-1.0.1-missingok.patch): tweak to logrotate file.
- FC10 patch (vsftpd-1.2.1-nonrootconf.patch): bail if the config file is not
owned by the currently running user.
- FC10 patch (vsftpd-2.0.1-tcp_wrappers.patch): explicitly call openlog() to
avoid syslog() bug where some settings are not initialized.
- FC10 patch (vsftpd-2.0.3-daemonize_fds.patch): when backgrounding, replace
fd 0,1,2 with /dev/null fd.
- FC10 patch (vsftpd-2.0.5-correct_comments.patch): comment tweaks in the
sample config file.
- FC10 patch (vsftpd-2.0.5-fix_unique.patch): use the default filename given
by STOU if it is available.
- FC10 patch (vsftpd-2.0.5-pam_end.patch): call pam_end() properly so modules
can act on errors if they want.
- FC10 patch (vsftpd-2.0.5-pasv_dot.patch): Strict RFC compliance for PASV
command; add a trailing period.
- FC10 patch (vsftpd-2.0.5-uname_size.patch): allow longer usernames. It's not
1990 any more, so trust PAM etc. to not stack-buffer-overflow.
- FC10 patch (vsftpd-2.0.5-underscore_uname.patch): permit username to start
with underscore or period.
- FC10 patch (vsftpd-2.0.6-listen.patch): default listen to YES.
- Fix crash on SIGHUP introduced in 2.1.0pre1. Oops.
- FC10 patch (vsftpd-2.0.5-bind_denied.patch): retry PASV bind() on EACCES
too, which can happen on SELinux systems.
- Default resource limit for child processes: 100MB address space.
- Finishing touches to the initial sandbox policy; only permit connect() to
the host on the control channel being the nicest touch.
(v2.1.0pre2 here)
- Fix 64-bit build (oops)! Thanks Martin Nagy <mnagy@redhat.com>.
- Fix config of SSL built in; not enabled; two process model. Report from
Martin.
- Shutdown the command connection in the priv parent's SIGTERM handler; kills
of children where the PR_SET_PDEATHSIG cannot due to different user ids.
(v2.1.0pre3 here)
- Fix build on FC10.
- Some FAQ tweaks.
- Permit fcntl(F_GETFD) in sandbox policy. Needed for FC10. Not sure where it
comes from but it is harmless. (My guess would be glibc-2.9's new support for
using O_CLOEXEC more).
- Fix build warning on 64-bit.
- Fix build on OpenBSD again.
(v2.1.0pre4 here)
- Bring userlist_deny handling inside the max_login_fail accounting.
At this point: v2.1.0 released!
===============================

14
FAQ
View File

@ -151,6 +151,7 @@ Q) Help! vsftpd doesn't build, it fails with an error about being unable to
find -lcap.
A) Install the libcap package and retry the build. Seems to affect Debian
users a lot.
A) Install the libcap-devel. This certainly affects Fedora.
Q) Help! I've put settings in /etc/vsftpd.conf, but they are not taking
effect!
@ -238,6 +239,19 @@ Q) Help! When trying SSL transfers, users log in and are no longer restricted
to their home directory! They can browse the entire filesystem!
A) Most likely, your FTP client is in fact using the SSH protocol rather than
the FTP protocol - so sshd is in control and not vsftpd!
A) Of course, make sure you turn on the chroot_local_user option!!
Q) Help! I'm getting connections dropped whilst using gFTP for an SSL
connection.
A) The version of gFTP on my Fedora Core 10 installation appears to send the
"SIZE" command plain text during an SSL connection, which obviously breaks the
SSL connection.
Q) Help! SSL data connections are not working.
A) As of v2.1.0, vsftpd only accepts data connections that are reused sessions
of the control connection. This is a security measure. Unfortunately, not all
FTP clients reuse sessions (e.g. curl). You can disable this requirement by
changing require_ssl_reuse to NO.
Q) Blah.. blah..
A) For a good idea of what vsftpd can do, read the vsftpd.conf.5 man page

View File

@ -14,7 +14,7 @@ OBJS = main.o utility.o prelogin.o ftpcmdio.o postlogin.o privsock.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 opts.o \
ssl.o sysutil.o sysdeputil.o
ssl.o sslslave.o ptracesandbox.o ftppolicy.o sysutil.o sysdeputil.o
.c.o:

2
README
View File

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

View File

@ -1,4 +1,5 @@
/var/log/vsftpd.log {
# ftpd doesn't handle SIGHUP properly
nocompress
missingok
}

38
TODO
View File

@ -1,60 +1,48 @@
CRITICAL
========
- Improve FAQ, docs (ongoing..)
- Integrated test suite (I'm so lazy..)
- Sweedish, Russian etc. characters showing as ? in the log - many complaints.
- Check re-entrancy of usage of vsf_sysutil_exit_func.
- Fix SSL session timeout more gracefully.
- Use SSL_peek (surely pipelining won't work with current SSL code?)
NOT SO CRITICAL
===============
- Don't leak SSL private key to compromised process? May be impossible. We'll
see, would be an interesting security story.
- Better reporting of failed uploads due to out of device space or quota all
used.
- Upload file size limits.
- Improve FAQ, docs (ongoing..)
- Sweedish, Russian etc. characters showing as ? in the log - many complaints.
- "add_group" support.
- Implict SSL support (port 990?)
- 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.
- Fix SIGHUP config file reload to correctly reset _removed_ settings to
their default values.
- Upload file size limits.
- Allow groups to be listed in user lists.
- Allow space in username.
- Minor: background should happen after listen has completed so that failure
can result in a non-zero exit code.
- Better reporting of failed uploads due to out of device space or quota all
used.
- option to chroot to home dir and THEN apply init_dir
- separate upload/download max rates
- select() is assuming Linux behaviour (not threatening stability)
- add example global bandwidth limiting.
- switch to new signal model
- have a chown_uploads for non-anon users too; also more control over
permissions of uploaded file
- Fix SSL session timeout more gracefully. Currently, clients report an SSL read
error rather than an FTP status message saying timeout.
ON THE BACK BURNER
==================
- MLST, MLSD
- LPRT, LPSV
- Small race: signal might come in just before we start a blocking call
- log logout (pam session support provides this for locals)
- Limits on GIDs allowed to authenticate?
- Dynamic login info e.g. you are user XXX of YYY.
- Handle SIGINT.
- Session byte transfer counts in STAT output.
- Mac OS X setgroups() issue (report of success with 10.3.4, maybe OS X bug
has been fixed?)
- Fix for systems with no IPv6 (e.g. Solaris 7) (I'm only getting limited
numbers of these reports)
- Look into using GnuTLS in place of OpenSSL (more compatible license).
- put anon FTP users in wtmp too?
- Test vsftpd with pam_opie (issues with challenge/response vs. FTP protocol?)
- SITE GROUP support.
- SITE UTIME?
- Allow listener to listen on multiple IPs; bonus points if the different
IPs can have different configs.
- Allow listener to listen on multiple IPs, protocols; bonus points if the
different IPs can have different configs.
NOT PLANNED
===========
@ -62,4 +50,6 @@ NOT PLANNED
- telnet strings (no demand)
- "Minimal" build support
- transparent tar / compression support (no demand)
- put anon FTP users in wtmp too?
- Integrated test suite (I'm so lazy..) SORRY.

33
ascii.c
View File

@ -53,18 +53,29 @@ vsf_ascii_ascii_to_bin(char* p_buf, unsigned int in_len, int prev_cr)
return ret;
}
unsigned int
vsf_ascii_bin_to_ascii(const char* p_in, char* p_out, unsigned int in_len)
struct bin_to_ascii_ret
vsf_ascii_bin_to_ascii(const char* p_in,
char* p_out,
unsigned int in_len,
int prev_cr)
{
/* Task: translate all \n into \r\n. Note that \r\n becomes \r\r\n. That's
* what wu-ftpd does, and it's easier :-)
/* Task: translate all \n not preceeded by \r into \r\n.
* Note that \r\n stays as \r\n. We used to map it to \r\r\n like wu-ftpd
* but have switched to leaving it, like the more popular proftpd.
*/
struct bin_to_ascii_ret ret = { 0, 0 };
unsigned int indexx = 0;
unsigned int written = 0;
char last_char = 0;
if (prev_cr)
{
last_char = '\r';
ret.last_was_cr = 1;
}
while (indexx < in_len)
{
char the_char = p_in[indexx];
if (the_char == '\n')
if (the_char == '\n' && last_char != '\r')
{
*p_out++ = '\r';
written++;
@ -72,7 +83,17 @@ vsf_ascii_bin_to_ascii(const char* p_in, char* p_out, unsigned int in_len)
*p_out++ = the_char;
written++;
indexx++;
last_char = the_char;
if (the_char == '\r')
{
ret.last_was_cr = 1;
}
else
{
ret.last_was_cr = 0;
}
}
return written;
ret.stored = written;
return ret;
}

16
ascii.h
View File

@ -38,11 +38,21 @@ struct ascii_to_bin_ret vsf_ascii_ascii_to_bin(
* p_out - the output buffer, which MUST BE at least TWICE as big as
* "in_len"
* in_len - the length in bytes of the input buffer
* prev_cr - set to non-zero if this buffer fragment was immediately
* preceeded by a '\r'.
* RETURNS
* The number of characters stored in the output buffer
* The number of characters stored in the output buffer, and whether the last
* character stored was '\r'.
*/
unsigned int vsf_ascii_bin_to_ascii(const char* p_in, char* p_out,
unsigned int in_len);
struct bin_to_ascii_ret
{
unsigned int stored;
int last_was_cr;
};
struct bin_to_ascii_ret vsf_ascii_bin_to_ascii(const char* p_in,
char* p_out,
unsigned int in_len,
int prev_cr);
#endif /* VSFTP_ASCII_H */

5
defs.h
View File

@ -6,7 +6,7 @@
#define VSFTP_COMMAND_FD 0
#define VSFTP_PASSWORD_MAX 128
#define VSFTP_USERNAME_MAX 32
#define VSFTP_USERNAME_MAX 128
#define VSFTP_MAX_COMMAND_LINE 4096
#define VSFTP_DATA_BUFSIZE 65536
#define VSFTP_DIR_BUFSIZE 16384
@ -16,7 +16,8 @@
#define VSFTP_SECURE_UMASK 077
#define VSFTP_ROOT_UID 0
/* Must be greater than both VSFTP_MAX_COMMAND_LINE and VSFTP_DIR_BUFSIZE */
#define VSFTP_PRIVSOCK_MAXSTR VSFTP_DIR_BUFSIZE
#define VSFTP_PRIVSOCK_MAXSTR VSFTP_DATA_BUFSIZE
#define VSFTP_AS_LIMIT 100 * 1024 * 1024
#endif /* VSF_DEFS_H */

7
dummyinc/sys/prctl.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef VSF_DUMMYINC_PRCTL_H
#define VSF_DUMMYINC_PRCTL_H
/* Deliberate nothing; we're just avoiding a compile error. */
#endif /* VSF_DUMMYINC_PRCTL_H */

View File

@ -155,8 +155,10 @@ vsf_cmdio_set_alarm(struct vsf_session* p_sess)
{
if (tunable_idle_session_timeout > 0)
{
vsf_sysutil_install_sighandler(kVSFSysUtilSigALRM, handle_alarm_timeout,
p_sess);
vsf_sysutil_install_sighandler(kVSFSysUtilSigALRM,
handle_alarm_timeout,
p_sess,
1);
vsf_sysutil_set_alarm(tunable_idle_session_timeout);
}
}
@ -172,7 +174,17 @@ vsf_cmdio_get_cmd_and_arg(struct vsf_session* p_sess, struct mystr* p_cmd_str,
}
/* Blocks */
control_getline(p_cmd_str, p_sess);
str_split_char(p_cmd_str, p_arg_str, ' ');
/* 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 (tunable_log_ftp_protocol)
{

View File

@ -29,6 +29,7 @@
#include "ls.h"
#include "ssl.h"
#include "readwrite.h"
#include "privsock.h"
static void init_data_sock_params(struct vsf_session* p_sess, int sock_fd);
static filesize_t calc_num_send(int file_fd, filesize_t init_offset);
@ -63,7 +64,17 @@ vsf_ftpdataio_dispose_transfer_fd(struct vsf_session* p_sess)
/* Reset the data connection alarm so it runs anew with the blocking close */
start_data_alarm(p_sess);
vsf_sysutil_uninstall_io_handler();
if (p_sess->p_data_ssl != 0)
if (p_sess->data_use_ssl && p_sess->ssl_slave_active)
{
char result;
priv_sock_send_cmd(p_sess->ssl_consumer_fd, PRIV_SOCK_DO_SSL_CLOSE);
result = priv_sock_get_result(p_sess->ssl_consumer_fd);
if (result != PRIV_SOCK_RESULT_OK)
{
dispose_ret = 0;
}
}
else if (p_sess->p_data_ssl)
{
dispose_ret = ssl_data_close(p_sess);
}
@ -75,7 +86,10 @@ vsf_ftpdataio_dispose_transfer_fd(struct vsf_session* p_sess)
vsf_sysutil_deactivate_linger_failok(p_sess->data_fd);
(void) vsf_sysutil_close_failok(p_sess->data_fd);
}
vsf_sysutil_clear_alarm();
if (tunable_data_connection_timeout > 0)
{
vsf_sysutil_clear_alarm();
}
p_sess->data_fd = -1;
return dispose_ret;
}
@ -158,16 +172,37 @@ vsf_ftpdataio_get_port_fd(struct vsf_session* p_sess)
int
vsf_ftpdataio_post_mark_connect(struct vsf_session* p_sess)
{
if (p_sess->data_use_ssl)
int ret = 0;
if (!p_sess->data_use_ssl)
{
if (!ssl_accept(p_sess, p_sess->data_fd))
return 1;
}
if (!p_sess->ssl_slave_active)
{
ret = ssl_accept(p_sess, p_sess->data_fd);
}
else
{
int sock_ret;
priv_sock_send_cmd(p_sess->ssl_consumer_fd, PRIV_SOCK_DO_SSL_HANDSHAKE);
priv_sock_send_fd(p_sess->ssl_consumer_fd, p_sess->data_fd);
sock_ret = priv_sock_get_result(p_sess->ssl_consumer_fd);
if (sock_ret == PRIV_SOCK_RESULT_OK)
{
vsf_cmdio_write(
p_sess, FTP_DATATLSBAD, "Secure connection negotiation failed.");
return 0;
ret = 1;
}
}
return 1;
if (ret != 1)
{
static struct mystr s_err_msg;
str_alloc_text(&s_err_msg, "SSL connection failed");
if (tunable_require_ssl_reuse)
{
str_append_text(&s_err_msg, "; session reuse required");
}
vsf_cmdio_write_str(p_sess, FTP_DATATLSBAD, &s_err_msg);
}
return ret;
}
static void
@ -188,9 +223,16 @@ start_data_alarm(struct vsf_session* p_sess)
{
if (tunable_data_connection_timeout > 0)
{
vsf_sysutil_install_sighandler(kVSFSysUtilSigALRM, handle_sigalrm, p_sess);
vsf_sysutil_install_sighandler(kVSFSysUtilSigALRM,
handle_sigalrm,
p_sess,
1);
vsf_sysutil_set_alarm(tunable_data_connection_timeout);
}
else if (tunable_idle_session_timeout > 0)
{
vsf_sysutil_clear_alarm();
}
}
static void
@ -440,16 +482,20 @@ do_file_send_rwloop(struct vsf_session* p_sess, int file_fd, int is_ascii)
struct vsf_transfer_ret ret_struct = { 0, 0 };
unsigned int chunk_size = get_chunk_size();
char* p_writefrom_buf;
int prev_cr = 0;
if (p_readbuf == 0)
{
/* NOTE!! * 2 factor because we can double the data by doing our ASCII
* linefeed mangling
*/
vsf_secbuf_alloc(&p_asciibuf, VSFTP_DATA_BUFSIZE * 2);
vsf_secbuf_alloc(&p_readbuf, VSFTP_DATA_BUFSIZE);
}
if (is_ascii)
{
if (p_asciibuf == 0)
{
/* NOTE!! * 2 factor because we can double the data by doing our ASCII
* linefeed mangling
*/
vsf_secbuf_alloc(&p_asciibuf, VSFTP_DATA_BUFSIZE * 2);
}
p_writefrom_buf = p_asciibuf;
}
else
@ -472,8 +518,13 @@ do_file_send_rwloop(struct vsf_session* p_sess, int file_fd, int is_ascii)
}
if (is_ascii)
{
num_to_write = vsf_ascii_bin_to_ascii(p_readbuf, p_asciibuf,
(unsigned int) retval);
struct bin_to_ascii_ret ret =
vsf_ascii_bin_to_ascii(p_readbuf,
p_asciibuf,
(unsigned int) retval,
prev_cr);
num_to_write = ret.stored;
prev_cr = ret.last_was_cr;
}
else
{

326
ftppolicy.c Normal file
View File

@ -0,0 +1,326 @@
/*
* Part of Very Secure FTPd
* Licence: GPL v2
* Author: Chris Evans
* ftppolicy.c
*
* Code to build a sandbox policy based on current session options.
*/
#include "ftppolicy.h"
#include "ptracesandbox.h"
#include "tunables.h"
#include "session.h"
#include "sysutil.h"
/* For AF_INET etc. network constants. */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
static int socket_validator(struct pt_sandbox* p_sandbox, void* p_arg);
static int connect_validator(struct pt_sandbox* p_sandbox, void* p_arg);
static int getsockopt_validator(struct pt_sandbox* p_sandbox, void* p_arg);
static int setsockopt_validator(struct pt_sandbox* p_sandbox, void* p_arg);
void
policy_setup(struct pt_sandbox* p_sandbox, const struct vsf_session* p_sess)
{
int is_anon = p_sess->is_anonymous;
/* Always need to be able to exit! */
ptrace_sandbox_permit_exit(p_sandbox);
/* Needed for memory management. */
ptrace_sandbox_permit_mmap(p_sandbox);
ptrace_sandbox_permit_mprotect(p_sandbox);
ptrace_sandbox_permit_brk(p_sandbox);
/* Simple reads and writes are required. Permitting write does not imply
* filesystem write access because access control is done at open time.
*/
ptrace_sandbox_permit_read(p_sandbox);
ptrace_sandbox_permit_write(p_sandbox);
/* Querying time is harmless; used for log timestamps and internally to
* OpenSSL
*/
ptrace_sandbox_permit_query_time(p_sandbox);
/* Stat'ing is needed for downloading and directory listings. Can blanket
* enable for any filename thanks to the chroot().
*/
ptrace_sandbox_permit_file_stats(p_sandbox);
ptrace_sandbox_permit_fd_stats(p_sandbox);
/* Querying and changing directory. */
ptrace_sandbox_permit_getcwd(p_sandbox);
ptrace_sandbox_permit_chdir(p_sandbox);
/* Setting umask. */
ptrace_sandbox_permit_umask(p_sandbox);
/* Since we're in a chroot(), we can just blanket allow filesystem readonly
* open.
*/
ptrace_sandbox_permit_open(p_sandbox, 0);
ptrace_sandbox_permit_close(p_sandbox);
/* High-speed transfers... */
ptrace_sandbox_permit_sendfile(p_sandbox);
/* Reading directories. */
ptrace_sandbox_permit_getdents(p_sandbox);
/* Reading symlink targets. */
ptrace_sandbox_permit_readlink(p_sandbox);
/* File locking. */
ptrace_sandbox_permit_fcntl(p_sandbox);
/* Seeking for REST. */
ptrace_sandbox_permit_seek(p_sandbox);
/* Select for data connection readyness. */
ptrace_sandbox_permit_select(p_sandbox);
/* Always need ability to take signals (SIGPIPE) */
ptrace_sandbox_permit_sigreturn(p_sandbox);
/* Sleeping (bandwidth limit, connect retires, anon login fails) */
ptrace_sandbox_permit_sleep(p_sandbox);
/* May need ability to install signal handlers. */
if (tunable_async_abor_enable ||
tunable_idle_session_timeout > 0 ||
tunable_data_connection_timeout > 0)
{
ptrace_sandbox_permit_sigaction(p_sandbox);
}
/* May need ability to set up timeout alarms. */
if (tunable_idle_session_timeout > 0 || tunable_data_connection_timeout > 0)
{
ptrace_sandbox_permit_alarm(p_sandbox);
}
/* TODO - Grrrr! nscd cache access is leaking into child. Need to find out
* out how to disable that. Also means that text_userdb_names loads values
* from the real system data.
*/
if (tunable_text_userdb_names)
{
ptrace_sandbox_permit_mremap(p_sandbox);
}
/* Set up network permissions according to config and session. */
ptrace_sandbox_permit_recv(p_sandbox);
ptrace_sandbox_permit_shutdown(p_sandbox);
ptrace_sandbox_permit_socket(p_sandbox);
ptrace_sandbox_set_socket_validator(p_sandbox,
socket_validator,
(void*) p_sess);
ptrace_sandbox_permit_bind(p_sandbox);
/* Yes, reuse of the connect validator is intentional. */
ptrace_sandbox_set_bind_validator(p_sandbox,
connect_validator,
(void*) p_sess);
ptrace_sandbox_permit_setsockopt(p_sandbox);
ptrace_sandbox_set_setsockopt_validator(p_sandbox, setsockopt_validator, 0);
ptrace_sandbox_permit_shutdown(p_sandbox);
if (tunable_port_enable)
{
ptrace_sandbox_permit_connect(p_sandbox);
ptrace_sandbox_set_connect_validator(p_sandbox,
connect_validator,
(void*) p_sess);
ptrace_sandbox_permit_getsockopt(p_sandbox);
ptrace_sandbox_set_getsockopt_validator(p_sandbox, getsockopt_validator, 0);
}
if (tunable_pasv_enable)
{
ptrace_sandbox_permit_listen(p_sandbox);
ptrace_sandbox_permit_accept(p_sandbox);
}
/* Set up write permissions according to config and session. */
if (tunable_write_enable)
{
if (!is_anon || tunable_anon_upload_enable)
{
ptrace_sandbox_permit_open(p_sandbox, 1);
}
if (!is_anon || tunable_anon_mkdir_write_enable)
{
ptrace_sandbox_permit_mkdir(p_sandbox);
}
if (!is_anon || tunable_anon_other_write_enable)
{
ptrace_sandbox_permit_unlink(p_sandbox);
ptrace_sandbox_permit_rmdir(p_sandbox);
ptrace_sandbox_permit_rename(p_sandbox);
ptrace_sandbox_permit_ftruncate(p_sandbox);
if (tunable_mdtm_write)
{
ptrace_sandbox_permit_utime(p_sandbox);
}
}
if (!is_anon && tunable_chmod_enable)
{
ptrace_sandbox_permit_chmod(p_sandbox);
}
if (is_anon && tunable_chown_uploads)
{
ptrace_sandbox_permit_fchmod(p_sandbox);
ptrace_sandbox_permit_fchown(p_sandbox);
}
}
}
static int
socket_validator(struct pt_sandbox* p_sandbox, void* p_arg)
{
struct vsf_session* p_sess = (struct vsf_session*) p_arg;
unsigned long arg1;
unsigned long arg2;
unsigned long expected_family = AF_INET;
if (vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr))
{
expected_family = AF_INET6;
}
int ret = ptrace_sandbox_get_socketcall_arg(p_sandbox, 0, &arg1);
if (ret != 0)
{
return ret;
}
ret = ptrace_sandbox_get_socketcall_arg(p_sandbox, 1, &arg2);
if (ret != 0)
{
return ret;
}
if (arg1 != expected_family || arg2 != SOCK_STREAM)
{
return -1;
}
return 0;
}
static int
connect_validator(struct pt_sandbox* p_sandbox, void* p_arg)
{
struct vsf_session* p_sess = (struct vsf_session*) p_arg;
unsigned long arg2;
unsigned long arg3;
unsigned long expected_family = AF_INET;
unsigned long expected_len = sizeof(struct sockaddr_in);
void* p_buf = 0;
struct sockaddr* p_sockaddr;
static struct vsf_sysutil_sockaddr* p_sockptr;
if (vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr))
{
expected_family = AF_INET6;
expected_len = sizeof(struct sockaddr_in6);
}
int ret = ptrace_sandbox_get_socketcall_arg(p_sandbox, 1, &arg2);
if (ret != 0)
{
return ret;
}
ret = ptrace_sandbox_get_socketcall_arg(p_sandbox, 2, &arg3);
if (ret != 0)
{
return ret;
}
if (arg3 != expected_len)
{
return -1;
}
p_buf = vsf_sysutil_malloc((int) expected_len);
ret = ptrace_sandbox_get_buf(p_sandbox, arg2, expected_len, p_buf);
if (ret != 0)
{
vsf_sysutil_free(p_buf);
return -2;
}
p_sockaddr = (struct sockaddr*) p_buf;
if (p_sockaddr->sa_family != expected_family)
{
vsf_sysutil_free(p_buf);
return -3;
}
if (expected_family == AF_INET)
{
struct sockaddr_in* p_sockaddr_in = (struct sockaddr_in*) p_sockaddr;
vsf_sysutil_sockaddr_alloc_ipv4(&p_sockptr);
vsf_sysutil_sockaddr_set_ipv4addr(p_sockptr,
(const unsigned char*)
&p_sockaddr_in->sin_addr);
}
else
{
struct sockaddr_in6* p_sockaddr_in6 = (struct sockaddr_in6*) p_sockaddr;
vsf_sysutil_sockaddr_alloc_ipv6(&p_sockptr);
vsf_sysutil_sockaddr_set_ipv6addr(p_sockptr,
(const unsigned char*)
&p_sockaddr_in6->sin6_addr);
}
if (!vsf_sysutil_sockaddr_addr_equal(p_sess->p_remote_addr, p_sockptr))
{
vsf_sysutil_free(p_buf);
return -4;
}
vsf_sysutil_free(p_buf);
return 0;
}
static int
getsockopt_validator(struct pt_sandbox* p_sandbox, void* p_arg)
{
unsigned long arg2;
unsigned long arg3;
(void) p_arg;
int ret = ptrace_sandbox_get_socketcall_arg(p_sandbox, 1, &arg2);
if (ret != 0)
{
return ret;
}
ret = ptrace_sandbox_get_socketcall_arg(p_sandbox, 2, &arg3);
if (ret != 0)
{
return ret;
}
if (arg2 != SOL_SOCKET || arg3 != SO_ERROR)
{
return -1;
}
return 0;
}
static int
setsockopt_validator(struct pt_sandbox* p_sandbox, void* p_arg)
{
unsigned long arg2;
unsigned long arg3;
(void) p_arg;
int ret = ptrace_sandbox_get_socketcall_arg(p_sandbox, 1, &arg2);
if (ret != 0)
{
return ret;
}
ret = ptrace_sandbox_get_socketcall_arg(p_sandbox, 2, &arg3);
if (ret != 0)
{
return ret;
}
if (arg2 == SOL_SOCKET)
{
if (arg3 != SO_KEEPALIVE &&
arg3 != SO_REUSEADDR &&
arg3 != SO_OOBINLINE &&
arg3 != SO_LINGER)
{
return -1;
}
}
else if (arg2 == IPPROTO_TCP)
{
if (arg3 != TCP_NODELAY)
{
return -2;
}
}
else if (arg2 == IPPROTO_IP)
{
if (arg3 != IP_TOS)
{
return -3;
}
}
else
{
return -4;
}
return 0;
}

18
ftppolicy.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef VSF_FTPPOLICY_H
#define VSF_FTPPOLICY_H
/* Forward delcarations */
struct pt_sandbox;
struct vsf_session;
/* policy_setup()
* PURPOSE
* Sets up a sandbox policy according to the currently enabled options.
* PARAMETERS
* p_sandbox - the sandbox to set the policy on
* p_sess - current vsftpd session object
*/
void policy_setup(struct pt_sandbox* p_sandbox,
const struct vsf_session* p_sess);
#endif /* VSF_FTPPOLICY_H */

View File

@ -33,7 +33,7 @@ vsf_log_init(struct vsf_session* p_sess)
int retval;
if (tunable_syslog_enable || tunable_tcp_wrappers)
{
vsf_sysutil_openlog();
vsf_sysutil_openlog(1);
}
if (!tunable_xferlog_enable && !tunable_dual_log_enable)
{
@ -174,6 +174,7 @@ static void
vsf_log_do_log_wuftpd_format(struct vsf_session* p_sess, struct mystr* p_str,
int succeeded)
{
static struct mystr s_filename_str;
long delta_sec;
enum EVSFLogEntryType what = (enum EVSFLogEntryType) p_sess->log_type;
/* Date - vsf_sysutil_get_current_date updates cached time */
@ -194,7 +195,9 @@ vsf_log_do_log_wuftpd_format(struct vsf_session* p_sess, struct mystr* p_str,
str_append_filesize_t(p_str, p_sess->transfer_size);
str_append_char(p_str, ' ');
/* Filename */
str_append_str(p_str, &p_sess->log_str);
str_copy(&s_filename_str, &p_sess->log_str);
str_replace_char(&s_filename_str, ' ', '_');
str_append_str(p_str, &s_filename_str);
str_append_char(p_str, ' ');
/* Transfer type (ascii/binary) */
if (p_sess->is_ascii)
@ -253,12 +256,16 @@ vsf_log_do_log_vsftpd_format(struct vsf_session* p_sess, struct mystr* p_str,
int succeeded, enum EVSFLogEntryType what,
const struct mystr* p_log_str)
{
/* Date - vsf_sysutil_get_current_date updates cached time */
str_alloc_text(p_str, vsf_sysutil_get_current_date());
/* Pid */
str_append_text(p_str, " [pid ");
str_append_ulong(p_str, vsf_sysutil_getpid());
str_append_text(p_str, "] ");
str_empty(p_str);
if (!tunable_syslog_enable)
{
/* Date - vsf_sysutil_get_current_date updates cached time */
str_append_text(p_str, vsf_sysutil_get_current_date());
/* Pid */
str_append_text(p_str, " [pid ");
str_append_ulong(p_str, vsf_sysutil_getpid());
str_append_text(p_str, "] ");
}
/* User */
if (!str_isempty(&p_sess->user_str))
{

15
main.c
View File

@ -30,10 +30,12 @@ static void die_unless_privileged(void);
static void do_sanity_checks(void);
static void session_init(struct vsf_session* p_sess);
static void env_init(void);
static void limits_init(void);
int
main(int argc, const char* argv[])
{
tunables_load_defaults();
struct vsf_session the_session =
{
/* Control connection */
@ -160,6 +162,8 @@ main(int argc, const char* argv[])
session_init(&the_session);
/* Set up "environment", e.g. process group etc. */
env_init();
/* Set up resource limits. */
limits_init();
/* Set up logging - must come after global init because we need the remote
* address to convert into text
*/
@ -180,6 +184,7 @@ main(int argc, const char* argv[])
if (tunable_ssl_enable)
{
ssl_init(&the_session);
ssl_add_entropy(&the_session);
}
if (tunable_deny_email_enable)
{
@ -270,10 +275,6 @@ 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)
{
@ -293,6 +294,12 @@ env_init(void)
vsf_sysutil_install_null_sighandler(kVSFSysUtilSigPIPE);
}
static void
limits_init(void)
{
vsf_sysutil_set_address_space_limit(VSFTP_AS_LIMIT);
}
static void
session_init(struct vsf_session* p_sess)
{

View File

@ -16,8 +16,13 @@
#include "sysutil.h"
void
str_netfd_alloc(struct mystr* p_str, int fd, char term, char* p_readbuf,
unsigned int maxlen)
str_netfd_alloc(struct vsf_session* p_sess,
struct mystr* p_str,
char term,
char* p_readbuf,
unsigned int maxlen,
str_netfd_read_t p_peekfunc,
str_netfd_read_t p_readfunc)
{
int retval;
unsigned int bytes_read;
@ -36,7 +41,7 @@ str_netfd_alloc(struct mystr* p_str, int fd, char term, char* p_readbuf,
str_empty(p_str);
return;
}
retval = vsf_sysutil_recv_peek(fd, p_readpos, left);
retval = (*p_peekfunc)(p_sess, p_readpos, left);
if (vsf_sysutil_retval_is_error(retval))
{
die("vsf_sysutil_recv_peek");
@ -52,7 +57,7 @@ str_netfd_alloc(struct mystr* p_str, int fd, char term, char* p_readbuf,
if (p_readpos[i] == term)
{
/* Got it! */
retval = vsf_sysutil_read_loop(fd, p_readpos, i + 1);
retval = (*p_readfunc)(p_sess, p_readpos, i + 1);
if (vsf_sysutil_retval_is_error(retval) ||
(unsigned int) retval != i + 1)
{
@ -72,7 +77,7 @@ str_netfd_alloc(struct mystr* p_str, int fd, char term, char* p_readbuf,
bug("bytes_read > left in str_netfd_alloc");
}
left -= bytes_read;
retval = vsf_sysutil_read_loop(fd, p_readpos, bytes_read);
retval = (*p_readfunc)(p_sess, p_readpos, bytes_read);
if (vsf_sysutil_retval_is_error(retval) ||
(unsigned int) retval != bytes_read)
{

View File

@ -2,6 +2,11 @@
#define VSFTP_NETSTR_H
struct mystr;
struct vsf_session;
typedef int (*str_netfd_read_t)(struct vsf_session*
p_sess, char*,
unsigned int);
/* str_netfd_alloc()
* PURPOSE
@ -11,17 +16,24 @@ struct mystr;
* will exit the program.
* This method avoids reading one character at a time from the network.
* PARAMETERS
* p_sess - the session object, used for passing into the I/O callbacks
* p_str - the destination string object
* fd - the file descriptor of the remote network socket
* term - the character which will terminate the string. This character
* is included in the returned string.
* p_readbuf - pointer to a scratch buffer into which to read from the
* network. This buffer must be at least "maxlen" characters!
* maxlen - maximum length of string to return. If this limit is passed,
* an empty string will be returned.
* p_peekfunc - a function called to peek data from the network
* p_readfunc - a function called to read data from the network
*/
void str_netfd_alloc(struct mystr* p_str, int fd, char term,
char* p_readbuf, unsigned int maxlen);
void str_netfd_alloc(struct vsf_session* p_sess,
struct mystr* p_str,
char term,
char* p_readbuf,
unsigned int maxlen,
str_netfd_read_t p_peekfunc,
str_netfd_read_t p_readfunc);
/* str_netfd_read()
* PURPOSE

View File

@ -22,10 +22,47 @@
#include "utility.h"
#include "sysstr.h"
#include "sysdeputil.h"
#include "sysutil.h"
#include "ptracesandbox.h"
#include "ftppolicy.h"
static void one_process_start(void* p_arg);
void
vsf_one_process_start(struct vsf_session* p_sess)
{
if (tunable_sandbox)
{
struct pt_sandbox* p_sandbox = ptrace_sandbox_alloc();
if (p_sandbox == 0)
{
die("could not allocate sandbox");
}
policy_setup(p_sandbox, p_sess);
if (ptrace_sandbox_launch_process(p_sandbox,
one_process_start,
(void*) p_sess) <= 0)
{
die("could not launch sandboxed child");
}
/* TODO - could drop privs here. For now, run as root as the attack surface
* is negligible, and running as root permits us to correctly deliver the
* parent death signal upon unexpected crash.
*/
(void) ptrace_sandbox_run_processes(p_sandbox);
ptrace_sandbox_free(p_sandbox);
vsf_sysutil_exit(0);
}
else
{
one_process_start((void*) p_sess);
}
}
static void
one_process_start(void* p_arg)
{
struct vsf_session* p_sess = (struct vsf_session*) p_arg;
unsigned int caps = 0;
if (tunable_chown_uploads)
{
@ -58,6 +95,10 @@ vsf_one_process_start(struct vsf_session* p_sess)
str_free(&user_name);
str_free(&chdir_str);
}
if (tunable_sandbox)
{
ptrace_sandbox_attach_point();
}
init_connection(p_sess);
}

View File

@ -17,15 +17,12 @@
#include "utility.h"
static const char* s_p_saved_filename;
static int s_strings_copied;
/* File local functions */
static void handle_config_setting(struct mystr* p_setting_str,
struct mystr* p_value_str,
int errs_fatal);
static void copy_string_settings(void);
/* Tables mapping setting names to runtime variables */
/* Boolean settings */
static struct parseconf_bool_setting
@ -106,6 +103,9 @@ parseconf_bool_array[] =
{ "strict_ssl_write_shutdown", &tunable_strict_ssl_write_shutdown },
{ "ssl_request_cert", &tunable_ssl_request_cert },
{ "delete_failed_uploads", &tunable_delete_failed_uploads },
{ "implicit_ssl", &tunable_implicit_ssl },
{ "sandbox", &tunable_sandbox },
{ "require_ssl_reuse", &tunable_require_ssl_reuse },
{ 0, 0 }
};
@ -177,6 +177,7 @@ parseconf_str_array[] =
{ "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 },
{ "cmds_denied", &tunable_cmds_denied },
{ 0, 0 }
};
@ -204,15 +205,6 @@ vsf_parseconf_load_file(const char* p_filename, int errs_fatal)
{
bug("null filename in vsf_parseconf_load_file");
}
if (!s_strings_copied)
{
s_strings_copied = 1;
/* A minor hack to make sure all strings are malloc()'ed so we can free
* them at some later date. Specifically handles strings embedded in the
* binary.
*/
copy_string_settings();
}
retval = str_fileread(&config_file_str, p_filename, VSFTP_CONF_FILE_MAX);
if (vsf_sysutil_retval_is_error(retval))
{
@ -222,13 +214,29 @@ vsf_parseconf_load_file(const char* p_filename, int errs_fatal)
}
else
{
str_free(&config_file_str);
return;
}
}
{
struct vsf_sysutil_statbuf* p_statbuf = 0;
retval = vsf_sysutil_stat(p_filename, &p_statbuf);
/* Security: check current user owns the config file. This is a sanity
* check for the admin, and is NOT designed to be a check safe from
* race conditions.
*/
if (vsf_sysutil_retval_is_error(retval) ||
vsf_sysutil_statbuf_get_uid(p_statbuf) != vsf_sysutil_getuid())
{
die("config file not owned by correct user");
}
vsf_sysutil_free(p_statbuf);
}
while (str_getline(&config_file_str, &config_setting_str, &str_pos))
{
if (str_isempty(&config_setting_str) ||
str_get_char_at(&config_setting_str, 0) == '#')
str_get_char_at(&config_setting_str, 0) == '#' ||
str_all_space(&config_setting_str))
{
continue;
}
@ -341,19 +349,3 @@ handle_config_setting(struct mystr* p_setting_str, struct mystr* p_value_str,
die2("unrecognised variable in config file: ", str_getbuf(p_setting_str));
}
}
static void
copy_string_settings(void)
{
const struct parseconf_str_setting* p_str_setting = parseconf_str_array;
while (p_str_setting->p_setting_name != 0)
{
if (*p_str_setting->p_variable != 0)
{
*p_str_setting->p_variable =
vsf_sysutil_strdup(*p_str_setting->p_variable);
}
p_str_setting++;
}
}

View File

@ -95,7 +95,7 @@ process_post_login(struct vsf_session* p_sess)
}
if (tunable_async_abor_enable)
{
vsf_sysutil_install_sighandler(kVSFSysUtilSigURG, handle_sigurg, p_sess);
vsf_sysutil_install_sighandler(kVSFSysUtilSigURG, handle_sigurg, p_sess, 0);
vsf_sysutil_activate_sigurg(VSFTP_COMMAND_FD);
}
/* Handle any login message */
@ -125,7 +125,7 @@ process_post_login(struct vsf_session* p_sess)
vsf_sysutil_setproctitle_str(&proctitle_str);
str_free(&proctitle_str);
}
/* Test command against the allowed list.. */
/* Test command against the allowed lists.. */
if (tunable_cmds_allowed)
{
static struct mystr s_src_str;
@ -146,6 +146,26 @@ process_post_login(struct vsf_session* p_sess)
str_copy(&s_src_str, &s_rhs_str);
}
}
if (tunable_cmds_denied)
{
static struct mystr s_src_str;
static struct mystr s_rhs_str;
str_alloc_text(&s_src_str, tunable_cmds_denied);
while (1)
{
str_split_char(&s_src_str, &s_rhs_str, ',');
if (str_isempty(&s_src_str))
{
break;
}
else if (str_equal(&s_src_str, &p_sess->ftp_cmd_str))
{
cmd_ok = 0;
break;
}
str_copy(&s_src_str, &s_rhs_str);
}
}
if (!cmd_ok)
{
vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
@ -317,7 +337,9 @@ process_post_login(struct vsf_session* p_sess)
vsf_cmdio_write(p_sess, FTP_BADMODE, "Bad MODE command.");
}
}
else if (str_equal_text(&p_sess->ftp_cmd_str, "STOU"))
else if (tunable_write_enable &&
(tunable_anon_upload_enable || !p_sess->is_anonymous) &&
str_equal_text(&p_sess->ftp_cmd_str, "STOU"))
{
handle_stou(p_sess);
}
@ -401,6 +423,11 @@ process_post_login(struct vsf_session* p_sess)
{
vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
}
else if (str_isempty(&p_sess->ftp_cmd_str) &&
str_isempty(&p_sess->ftp_arg_str))
{
// Deliberately ignore to avoid NAT device bugs. ProFTPd does the same.
}
else
{
vsf_cmdio_write(p_sess, FTP_BADCMD, "Unknown command.");
@ -574,7 +601,9 @@ handle_pasv(struct vsf_session* p_sess, int is_epsv)
break;
}
}
if (vsf_sysutil_get_error() == kVSFSysUtilErrADDRINUSE)
/* SELinux systems can give you an inopportune EACCES, it seems. */
if (vsf_sysutil_get_error() == kVSFSysUtilErrADDRINUSE ||
vsf_sysutil_get_error() == kVSFSysUtilErrACCES)
{
continue;
}
@ -588,7 +617,7 @@ handle_pasv(struct vsf_session* p_sess, int is_epsv)
{
str_alloc_text(&s_pasv_res_str, "Entering Extended Passive Mode (|||");
str_append_ulong(&s_pasv_res_str, (unsigned long) the_port);
str_append_text(&s_pasv_res_str, "|)");
str_append_text(&s_pasv_res_str, "|).");
vsf_cmdio_write_str(p_sess, FTP_EPSVOK, &s_pasv_res_str);
return;
}
@ -622,7 +651,7 @@ handle_pasv(struct vsf_session* p_sess, int is_epsv)
str_append_ulong(&s_pasv_res_str, the_port >> 8);
str_append_text(&s_pasv_res_str, ",");
str_append_ulong(&s_pasv_res_str, the_port & 255);
str_append_text(&s_pasv_res_str, ")");
str_append_text(&s_pasv_res_str, ").");
vsf_cmdio_write_str(p_sess, FTP_PASVOK, &s_pasv_res_str);
}
@ -983,6 +1012,7 @@ handle_upload_common(struct vsf_session* p_sess, int is_append, int is_unique)
int remote_fd;
int success = 0;
int created = 0;
int do_truncate = 0;
filesize_t offset = p_sess->restart_pos;
p_sess->restart_pos = 0;
if (!data_transfer_checks_ok(p_sess))
@ -1015,13 +1045,10 @@ handle_upload_common(struct vsf_session* p_sess, int is_append, int is_unique)
else
{
/* For non-anonymous, allow open() to overwrite or append existing files */
new_file_fd = str_create_append(p_filename);
if (!is_append && offset == 0)
{
new_file_fd = str_create_overwrite(p_filename);
}
else
{
new_file_fd = str_create_append(p_filename);
do_truncate = 1;
}
}
if (vsf_sysutil_retval_is_error(new_file_fd))
@ -1056,9 +1083,19 @@ handle_upload_common(struct vsf_session* p_sess, int is_append, int is_unique)
{
vsf_sysutil_lock_file_write(new_file_fd);
}
/* Must truncate the file AFTER locking it! */
if (do_truncate)
{
vsf_sysutil_ftruncate(new_file_fd);
vsf_sysutil_lseek_to(new_file_fd, 0);
}
if (!is_append && offset != 0)
{
/* XXX - warning, allows seek past end of file! Check for seek > size? */
/* XXX - also, currently broken as the O_APPEND flag will always write
* at the end of file. No known complaints yet; can easily fix if one
* comes in.
*/
vsf_sysutil_lseek_to(new_file_fd, offset);
}
if (is_unique)
@ -1101,7 +1138,7 @@ handle_upload_common(struct vsf_session* p_sess, int is_append, int is_unique)
{
vsf_cmdio_write(p_sess, FTP_BADSENDFILE, "Failure writing to local file.");
}
else if (trans_ret.retval == -2)
else if (trans_ret.retval == -2 || p_sess->abor_received)
{
vsf_cmdio_write(p_sess, FTP_BADSENDNET, "Failure reading network stream.");
}
@ -1727,7 +1764,13 @@ get_unique_filename(struct mystr* p_outstr, const struct mystr* p_base_str)
*/
static struct vsf_sysutil_statbuf* s_p_statbuf;
unsigned int suffix = 1;
int retval;
/* Do not add any suffix at all if the name is not taken. */
int retval = str_stat(p_base_str, &s_p_statbuf);
if (vsf_sysutil_retval_is_error(retval))
{
str_copy(p_outstr, p_base_str);
return;
}
while (1)
{
str_copy(p_outstr, p_base_str);

View File

@ -42,10 +42,8 @@ static void
process_post_login_req(struct vsf_session* p_sess)
{
char cmd;
vsf_sysutil_unblock_sig(kVSFSysUtilSigCHLD);
/* Blocks */
cmd = priv_sock_get_cmd(p_sess->parent_fd);
vsf_sysutil_block_sig(kVSFSysUtilSigCHLD);
if (tunable_chown_uploads && cmd == PRIV_SOCK_CHOWN)
{
cmd_process_chown(p_sess);
@ -66,9 +64,6 @@ 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)
{
@ -76,12 +71,6 @@ minimize_privilege(struct vsf_session* p_sess)
* 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;

View File

@ -26,10 +26,13 @@
#include "opts.h"
/* Functions used */
static void check_limits(struct vsf_session* p_sess);
static void emit_greeting(struct vsf_session* p_sess);
static void parse_username_password(struct vsf_session* p_sess);
static void handle_user_command(struct vsf_session* p_sess);
static void handle_pass_command(struct vsf_session* p_sess);
static void check_login_delay();
static void check_login_fails(struct vsf_session* p_sess);
void
init_connection(struct vsf_session* p_sess)
@ -42,12 +45,21 @@ init_connection(struct vsf_session* p_sess)
* writing the initial greetings should block.
*/
vsf_cmdio_set_alarm(p_sess);
/* Check limits before doing an implicit SSL handshake, to avoid DoS
* attacks. This will result in plain text messages being sent to the SSL
* client, but we can live with that.
*/
check_limits(p_sess);
if (tunable_ssl_enable && tunable_implicit_ssl)
{
ssl_control_handshake(p_sess);
}
emit_greeting(p_sess);
parse_username_password(p_sess);
}
static void
emit_greeting(struct vsf_session* p_sess)
check_limits(struct vsf_session* p_sess)
{
struct mystr str_log_line = INIT_MYSTR;
/* Check for client limits (standalone mode only) */
@ -76,6 +88,11 @@ emit_greeting(struct vsf_session* p_sess)
vsf_cmdio_write_exit(p_sess, FTP_IP_DENY, "Service not available.");
}
vsf_log_line(p_sess, kVSFLogEntryConnection, &str_log_line);
}
static void
emit_greeting(struct vsf_session* p_sess)
{
if (!str_isempty(&p_sess->banner_str))
{
vsf_banner_write(p_sess, &p_sess->banner_str, FTP_GREET);
@ -121,7 +138,9 @@ parse_username_password(struct vsf_session* p_sess)
{
handle_opts(p_sess);
}
else if (tunable_ssl_enable && str_equal_text(&p_sess->ftp_cmd_str, "AUTH"))
else if (tunable_ssl_enable &&
str_equal_text(&p_sess->ftp_cmd_str, "AUTH") &&
!p_sess->control_use_ssl)
{
handle_auth(p_sess);
}
@ -133,6 +152,11 @@ parse_username_password(struct vsf_session* p_sess)
{
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. ProFTPd does the same.
}
else
{
vsf_cmdio_write(p_sess, FTP_LOGINERR,
@ -194,6 +218,7 @@ handle_user_command(struct vsf_session* p_sess)
(!located && !tunable_userlist_deny))
{
vsf_cmdio_write(p_sess, FTP_LOGINERR, "Permission denied.");
check_login_fails(p_sess);
str_empty(&p_sess->user_str);
return;
}
@ -227,12 +252,25 @@ handle_pass_command(struct vsf_session* p_sess)
{
vsf_two_process_login(p_sess, &p_sess->ftp_arg_str);
}
check_login_delay();
vsf_cmdio_write(p_sess, FTP_LOGINERR, "Login incorrect.");
if (++p_sess->login_fails >= tunable_max_login_fails)
{
vsf_sysutil_exit(0);
}
check_login_fails(p_sess);
str_empty(&p_sess->user_str);
/* FALLTHRU if login fails */
}
static void check_login_delay()
{
if (tunable_delay_failed_login)
{
vsf_sysutil_sleep((double) tunable_delay_failed_login);
}
}
static void check_login_fails(struct vsf_session* p_sess)
{
if (++p_sess->login_fails >= tunable_max_login_fails)
{
vsf_sysutil_exit(0);
}
}

View File

@ -102,10 +102,6 @@ vsf_privop_do_login(struct vsf_session* p_sess,
if (result == kVSFLoginFail)
{
vsf_log_do_log(p_sess, 0);
if (tunable_delay_failed_login)
{
vsf_sysutil_sleep((double) tunable_delay_failed_login);
}
}
else
{
@ -126,13 +122,17 @@ handle_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
* almost certainly can.
*/
int anonymous_login = 0;
char first_char;
unsigned int len = str_getlen(p_user_str);
if (len == 0 || len > VSFTP_USERNAME_MAX)
{
return kVSFLoginFail;
}
/* Throw out dodgy start characters */
if (!vsf_sysutil_isalnum(str_get_char_at(p_user_str, 0)))
first_char = str_get_char_at(p_user_str, 0);
if (!vsf_sysutil_isalnum(first_char) &&
first_char != '_' &&
first_char != '.')
{
return kVSFLoginFail;
}

View File

@ -23,12 +23,57 @@
void
priv_sock_init(struct vsf_session* p_sess)
{
if (p_sess->parent_fd != -1)
{
bug("parent_fd active");
}
if (p_sess->child_fd != -1)
{
bug("child_fd active");
}
const struct vsf_sysutil_socketpair_retval retval =
vsf_sysutil_unix_stream_socketpair();
p_sess->parent_fd = retval.socket_one;
p_sess->child_fd = retval.socket_two;
}
void
priv_sock_close(struct vsf_session* p_sess)
{
if (p_sess->parent_fd != -1)
{
vsf_sysutil_close(p_sess->parent_fd);
p_sess->parent_fd = -1;
}
if (p_sess->child_fd != -1)
{
vsf_sysutil_close(p_sess->child_fd);
p_sess->child_fd = -1;
}
}
void
priv_sock_set_parent_context(struct vsf_session* p_sess)
{
if (p_sess->child_fd == -1)
{
bug("child_fd not active");
}
vsf_sysutil_close(p_sess->child_fd);
p_sess->child_fd = -1;
}
void
priv_sock_set_child_context(struct vsf_session* p_sess)
{
if (p_sess->parent_fd == -1)
{
bug("parent_fd not active");
}
vsf_sysutil_close(p_sess->parent_fd);
p_sess->parent_fd = -1;
}
void
priv_sock_send_cmd(int fd, char cmd)
{
@ -50,6 +95,36 @@ priv_sock_send_str(int fd, const struct mystr* p_str)
}
}
void
priv_sock_send_buf(int fd, const char* p_buf, unsigned int len)
{
priv_sock_send_int(fd, (int) len);
if (len > 0)
{
if (vsf_sysutil_write_loop(fd, p_buf, len) != (int) len)
{
die("priv_sock_send_buf");
}
}
}
void
priv_sock_recv_buf(int fd, char* p_buf, unsigned int len)
{
unsigned int recv_len = (unsigned int) priv_sock_get_int(fd);
if (recv_len > len)
{
bug("recv_len bigger than buffer");
}
if (recv_len > 0)
{
if (vsf_sysutil_read_loop(fd, p_buf, recv_len) != (int) recv_len)
{
die("priv_sock_recv_buf");
}
}
}
char
priv_sock_get_result(int fd)
{

View File

@ -7,11 +7,39 @@ struct vsf_session;
/* priv_sock_init()
* PURPOSE
* Initialize the priv_sock system, by opening the communications sockets.
*
* PARAMETERS
* p_sess - the current session object
*/
void priv_sock_init(struct vsf_session* p_sess);
/* priv_sock_close()
* PURPOSE
* Closes any open file descriptors relating to the priv_sock system.
*
* PARAMETERS
* p_sess - the current session object
*/
void priv_sock_close(struct vsf_session* p_sess);
/* priv_sock_set_parent_context()
* PURPOSE
* Closes the child's fd, e.g. p_sess->child_fd.
*
* PARAMETERS
* p_sess - the current session object
*/
void priv_sock_set_parent_context(struct vsf_session* p_sess);
/* priv_sock_set_child_context()
* PURPOSE
* Closes the parent's fd, e.g. p_sess->parent_fd.
*
* PARAMETERS
* p_sess - the current session object
*/
void priv_sock_set_child_context(struct vsf_session* p_sess);
/* priv_sock_send_cmd()
* PURPOSE
* Sends a command, typically to the privileged side of the channel.
@ -30,6 +58,28 @@ void priv_sock_send_cmd(int fd, char cmd);
*/
void priv_sock_send_str(int fd, const struct mystr* p_str);
/* priv_sock_send_buf()
* PURPOSE
* Sends a buffer to the other side of the channel. The protocol used is the
* same as priv_sock_send_str()
* PARAMETERS
* fd - the fd on which to send the buffer
* p_buf - the buffer to send
* len - length of the buffer
*/
void priv_sock_send_buf(int fd, const char* p_buf, unsigned int len);
/* priv_sock_recv_buf()
* PURPOSE
* Receives a buffer from the other side of the channel. The protocol used is
* the same as priv_sock_recv_str()
* PARAMETERS
* fd - the fd on which to receive the buffer
* p_buf - the buffer to write into
* len - length of the buffer
*/
void priv_sock_recv_buf(int fd, char* p_buf, unsigned int len);
/* priv_sock_get_result()
* PURPOSE
* Receives a response, typically from the privileged side of the channel.
@ -111,6 +161,10 @@ int priv_sock_get_int(int fd);
#define PRIV_SOCK_GET_DATA_SOCK 3
#define PRIV_SOCK_GET_USER_CMD 4
#define PRIV_SOCK_WRITE_USER_RESP 5
#define PRIV_SOCK_DO_SSL_HANDSHAKE 6
#define PRIV_SOCK_DO_SSL_CLOSE 7
#define PRIV_SOCK_DO_SSL_READ 8
#define PRIV_SOCK_DO_SSL_WRITE 9
#define PRIV_SOCK_RESULT_OK 1
#define PRIV_SOCK_RESULT_BAD 2

1529
ptracesandbox.c Normal file

File diff suppressed because it is too large Load Diff

264
ptracesandbox.h Normal file
View File

@ -0,0 +1,264 @@
#ifndef VSF_PTRACESANDBOX_H
#define VSF_PTRACESANDBOX_H
/* Forward delcarations */
struct pt_sandbox;
typedef int (*ptrace_sandbox_validator_t)(struct pt_sandbox*, void*);
/* ptrace_sandbox_alloc()
* PURPOSE
* Allocates a ptrace sandbox object which is needed for the rest of the API.
* RETURNS
* NULL on failure, otherwise an opaque handle.
* TODO
* Only one per process supported at this time.
*/
struct pt_sandbox* ptrace_sandbox_alloc();
/* ptrace_sandbox_free()
* PURPOSE
* Frees the sandbox object.
* PARAMETERS
* p_sandbox - the sandbox handle to free
*/
void ptrace_sandbox_free(struct pt_sandbox* p_sandbox);
/* ptrace_sandbox_launch_process()
* PURPOSE
* Launches a new process and attaches the sandbox to it when it stops.
* PARAMETERS
* p_sandbox - the sandbox handle
* p_func - the function to call at the start of the new process
* p_arg - an argument to pass to the function
* RETURNS
* -1 on failure, otherwise an id for the created process. Not necessarily a
* "pid", please treat is as opaque!
* TODO
* Only one call to this per sandbox object is supported at this time.
*/
int ptrace_sandbox_launch_process(struct pt_sandbox* p_sandbox,
void (*p_func)(void*),
void* p_arg);
/* ptrace_sandbox_run_processes()
* PURPOSE
* Runs sandboxed children until they exit or are killed.
* PARAMETERS
* p_sandbox - the sandbox handle
* RETURNS
* 0 on normal exit or death of processes.
* -1 if any process breached the policy.
*/
int ptrace_sandbox_run_processes(struct pt_sandbox* p_sandbox);
/* ptrace_sandbox_kill_processes()
* PURPOSE
* Safely kills off all sandboxed processes.
* PARAMETERS
* p_sandbox - the sandbox handle
*/
void ptrace_sandbox_kill_processes(struct pt_sandbox* p_sandbox);
/* ptrace_sandbox_get_arg()
* PURPOSE
* Gets a syscall argument value for a process stopped in syscall entry.
* PARAMETERS
* p_sandbox - the sandbox handle
* arg - the arg number to get (zero-based)
* p_out - the result is written here
* RETURNS
* 0 on success; otherwise it's a failure.
*/
int ptrace_sandbox_get_arg(struct pt_sandbox* p_sandbox,
int arg,
unsigned long* p_out);
/* ptrace_sandbox_get_socketcall_arg()
* PURPOSE
* Gets a syscall argument value for a process stopped in syscall entry, where
* the system call is a socket-related one. On some architectures (e.g. i386,
* socket calls are in fact multiplexed and store the arguments in a struct
* in user space, hence the need for abstraction.
* PARAMETERS
* p_sandbox - the sandbox handle
* arg - the arg number to get (zero-based)
* p_out - the result is written here
* RETURNS
* 0 on success; otherwise it's a failure.
*/
int ptrace_sandbox_get_socketcall_arg(struct pt_sandbox* p_sandbox,
int arg,
unsigned long* p_out);
/* ptrace_sandbox_get_long()
* PURPOSE
* Gets a long from the address space of the process stopped in syscall entry.
* PARAMETERS
* p_sandbox - the sandbox handle
* ptr - the address to read the long from
* p_out - the result is written here
* RETURNS
* 0 on success; otherwise it's a failure.
*/
int ptrace_sandbox_get_long(struct pt_sandbox* p_sandbox,
unsigned long ptr,
unsigned long* p_out);
/* ptrace_sandbox_get_buf()
* PURPOSE
* Gets a piece of memory from the address space of the process stopped in
* syscall entry.
* PARAMETERS
* p_sandbox - the sandbox handle
* ptr - the address to read the buffer from
* len - the length of the buffer
* p_buf - the result is written here
* RETURNS
* 0 on success; otherwise it's a failure.
*/
int ptrace_sandbox_get_buf(struct pt_sandbox* p_sandbox,
unsigned long ptr,
unsigned long len,
void* p_buf);
/* ptrace_sandbox_attach_point()
* PURPOSE
* Used by the sandbox child code to stop and indicate it is ready to be
* attached to.
* NOTES
* In the event of error trying to stop, the process is forcibly killed as a
* security measure.
*/
void ptrace_sandbox_attach_point(void);
/* POLICY EDIT: permits exit() and exit_group() */
void ptrace_sandbox_permit_exit(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits read() */
void ptrace_sandbox_permit_read(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits write() */
void ptrace_sandbox_permit_write(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits sigaction() and rt_sigaction() */
void ptrace_sandbox_permit_sigaction(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits alarm() */
void ptrace_sandbox_permit_alarm(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits time() and gettimeofday() */
void ptrace_sandbox_permit_query_time(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits mmap2() (but not the MAP_SHARED flag) */
void ptrace_sandbox_permit_mmap(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits mprotect() */
void ptrace_sandbox_permit_mprotect(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits stat(), stat64(), lstat(), lstat64() */
void ptrace_sandbox_permit_file_stats(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits fstat(), fstat64() */
void ptrace_sandbox_permit_fd_stats(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits getcwd() */
void ptrace_sandbox_permit_getcwd(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits chdir() */
void ptrace_sandbox_permit_chdir(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits umask() */
void ptrace_sandbox_permit_umask(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits open(), except O_ASYNC and O_DIRECT. Only O_RDONLY
* allowed unless writeable is 1
*/
void ptrace_sandbox_permit_open(struct pt_sandbox* p_sandbox, int writeable);
/* POLICY EDIT: permits close() */
void ptrace_sandbox_permit_close(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits getdents(), getdents64() */
void ptrace_sandbox_permit_getdents(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits fcntl(), fcntl64() for file locking, safe F_SETFL flag
* setting (no O_ASYNC, O_DIRECT), F_SETOWN for your own pid and F_SETFD.
*/
void ptrace_sandbox_permit_fcntl(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits sendfile(), sendfile64() */
void ptrace_sandbox_permit_sendfile(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits lseek(), llseek() */
void ptrace_sandbox_permit_seek(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits select(), newselect() */
void ptrace_sandbox_permit_select(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits unlink() */
void ptrace_sandbox_permit_unlink(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits mkdir() */
void ptrace_sandbox_permit_mkdir(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits rmdir() */
void ptrace_sandbox_permit_rmdir(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits rename() */
void ptrace_sandbox_permit_rename(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits utime(), utimes() */
void ptrace_sandbox_permit_utime(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits sigreturn() */
void ptrace_sandbox_permit_sigreturn(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits recv() */
void ptrace_sandbox_permit_recv(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits readlink() */
void ptrace_sandbox_permit_readlink(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits brk() */
void ptrace_sandbox_permit_brk(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits nanosleep() */
void ptrace_sandbox_permit_sleep(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits fchmod() */
void ptrace_sandbox_permit_fchmod(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits chmod() */
void ptrace_sandbox_permit_chmod(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits fchown(), fchown32() */
void ptrace_sandbox_permit_fchown(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits mremap() */
void ptrace_sandbox_permit_mremap(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits ftruncate(), ftruncate64() */
void ptrace_sandbox_permit_ftruncate(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits socket() */
void ptrace_sandbox_permit_socket(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: set validator for socket() */
void ptrace_sandbox_set_socket_validator(struct pt_sandbox* p_sandbox,
ptrace_sandbox_validator_t val,
void* p_arg);
/* POLICY EDIT: permits bind() */
void ptrace_sandbox_permit_bind(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: set validator for bind() */
void ptrace_sandbox_set_bind_validator(struct pt_sandbox* p_sandbox,
ptrace_sandbox_validator_t val,
void* p_arg);
/* POLICY EDIT: permits connect() */
void ptrace_sandbox_permit_connect(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: set validator for connect() */
void ptrace_sandbox_set_connect_validator(struct pt_sandbox* p_sandbox,
ptrace_sandbox_validator_t val,
void* p_arg);
/* POLICY EDIT: permits listen() */
void ptrace_sandbox_permit_listen(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits accept() */
void ptrace_sandbox_permit_accept(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: permits setsockopt() */
void ptrace_sandbox_permit_setsockopt(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: set validator for setsockopt() */
void ptrace_sandbox_set_setsockopt_validator(struct pt_sandbox* p_sandbox,
ptrace_sandbox_validator_t val,
void* p_arg);
/* POLICY EDIT: permits getsockopt() */
void ptrace_sandbox_permit_getsockopt(struct pt_sandbox* p_sandbox);
/* POLICY EDIT: set validator for getsockopt() */
void ptrace_sandbox_set_getsockopt_validator(struct pt_sandbox* p_sandbox,
ptrace_sandbox_validator_t val,
void* p_arg);
/* POLICY EDIT: permits shutdown() */
void ptrace_sandbox_permit_shutdown(struct pt_sandbox* p_sandbox);
/* The traced process is unexpectedly dead; probably an external SIGKILL */
#define PTRACE_SANDBOX_ERR_DEAD -1
/* An unexpected error from ptrace() */
#define PTRACE_SANDBOX_ERR_PTRACE -2
/* An unexpected error from waitpid() */
#define PTRACE_SANDBOX_ERR_WAITPID -3
/* An unexpected waitpid() status was returned */
#define PTRACE_SANDBOX_ERR_WAIT_STATUS -4
/* A syscall not in the policy was attempted */
#define PTRACE_SANDBOX_ERR_POLICY_SYSCALL -5
/* A "bad" syscall was attemped: out-of-bounds, 64-bit in a 32-bit child etc. */
#define PTRACE_SANDBOX_ERR_BAD_SYSCALL -6
/* Bad arguments to a generally accepted syscall */
#define PTRACE_SANDBOX_ERR_POLICY_ARGS -7
/* Abuse of our API */
#define PTRACE_SANDBOX_ERR_API_ABUSE_STOPIT -8
#endif /* VSF_PTRACESANDBOX_H */

View File

@ -16,13 +16,39 @@
#include "defs.h"
#include "sysutil.h"
static int plain_peek_adapter(struct vsf_session* p_sess,
char* p_buf,
unsigned int len);
static int plain_read_adapter(struct vsf_session* p_sess,
char* p_buf,
unsigned int len);
static int ssl_peek_adapter(struct vsf_session* p_sess,
char* p_buf,
unsigned int len);
static int ssl_read_adapter(struct vsf_session* p_sess,
char* p_buf,
unsigned int len);
int
ftp_write_str(const struct vsf_session* p_sess, const struct mystr* p_str,
enum EVSFRWTarget target)
{
if (target == kVSFRWData)
{
if (p_sess->data_use_ssl)
if (p_sess->data_use_ssl && p_sess->ssl_slave_active)
{
int ret = -1;
int written;
priv_sock_send_cmd(p_sess->ssl_consumer_fd, PRIV_SOCK_DO_SSL_WRITE);
priv_sock_send_str(p_sess->ssl_consumer_fd, p_str);
written = priv_sock_get_int(p_sess->ssl_consumer_fd);
if (written > 0 && written == (int) str_getlen(p_str))
{
ret = 0;
}
return ret;
}
else if (p_sess->data_use_ssl)
{
return ssl_write_str(p_sess->p_data_ssl, p_str);
}
@ -53,9 +79,17 @@ ftp_write_str(const struct vsf_session* p_sess, const struct mystr* p_str,
int
ftp_read_data(struct vsf_session* p_sess, char* p_buf, unsigned int len)
{
if (p_sess->data_use_ssl)
if (p_sess->data_use_ssl && p_sess->ssl_slave_active)
{
return ssl_read(p_sess, p_buf, len);
int ret;
priv_sock_send_cmd(p_sess->ssl_consumer_fd, PRIV_SOCK_DO_SSL_READ);
ret = priv_sock_get_int(p_sess->ssl_consumer_fd);
priv_sock_recv_buf(p_sess->ssl_consumer_fd, p_buf, len);
return ret;
}
else if (p_sess->data_use_ssl)
{
return ssl_read(p_sess, p_sess->p_data_ssl, p_buf, len);
}
else
{
@ -67,7 +101,13 @@ int
ftp_write_data(const struct vsf_session* p_sess, const char* p_buf,
unsigned int len)
{
if (p_sess->data_use_ssl)
if (p_sess->data_use_ssl && p_sess->ssl_slave_active)
{
priv_sock_send_cmd(p_sess->ssl_consumer_fd, PRIV_SOCK_DO_SSL_WRITE);
priv_sock_send_buf(p_sess->ssl_consumer_fd, p_buf, len);
return priv_sock_get_int(p_sess->ssl_consumer_fd);
}
else if (p_sess->data_use_ssl)
{
return ssl_write(p_sess->p_data_ssl, p_buf, len);
}
@ -78,21 +118,54 @@ ftp_write_data(const struct vsf_session* p_sess, const char* p_buf,
}
void
ftp_getline(const struct vsf_session* p_sess, struct mystr* p_str, char* p_buf)
ftp_getline(struct vsf_session* p_sess, struct mystr* p_str, char* p_buf)
{
if (p_sess->control_use_ssl && p_sess->ssl_slave_active)
{
priv_sock_send_cmd(p_sess->ssl_consumer_fd, PRIV_SOCK_GET_USER_CMD);
priv_sock_get_str(p_sess->ssl_consumer_fd, p_str);
}
else if (p_sess->control_use_ssl)
{
ssl_getline(p_sess, p_str, '\n', p_buf, VSFTP_MAX_COMMAND_LINE);
}
else
{
str_netfd_alloc(
p_str, VSFTP_COMMAND_FD, '\n', p_buf, VSFTP_MAX_COMMAND_LINE);
str_netfd_read_t p_peek = plain_peek_adapter;
str_netfd_read_t p_read = plain_read_adapter;
if (p_sess->control_use_ssl)
{
p_peek = ssl_peek_adapter;
p_read = ssl_read_adapter;
}
str_netfd_alloc(p_sess,
p_str,
'\n',
p_buf,
VSFTP_MAX_COMMAND_LINE,
p_peek,
p_read);
}
}
static int
plain_peek_adapter(struct vsf_session* p_sess, char* p_buf, unsigned int len)
{
(void) p_sess;
return vsf_sysutil_recv_peek(VSFTP_COMMAND_FD, p_buf, len);
}
static int
plain_read_adapter(struct vsf_session* p_sess, char* p_buf, unsigned int len)
{
(void) p_sess;
return vsf_sysutil_read_loop(VSFTP_COMMAND_FD, p_buf, len);
}
static int
ssl_peek_adapter(struct vsf_session* p_sess, char* p_buf, unsigned int len)
{
return ssl_peek(p_sess, p_sess->p_control_ssl, p_buf, len);
}
static int
ssl_read_adapter(struct vsf_session* p_sess, char* p_buf, unsigned int len)
{
return ssl_read(p_sess, p_sess->p_control_ssl, p_buf, len);
}

View File

@ -15,8 +15,7 @@ int ftp_write_str(const struct vsf_session* p_sess, const struct mystr* p_str,
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,
char* p_buf);
void ftp_getline(struct vsf_session* p_sess, struct mystr* p_str, char* p_buf);
#endif /* VSF_READWRITE_H */

226
ssl.c
View File

@ -41,6 +41,11 @@ 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_read_common(struct vsf_session* p_sess,
SSL* p_ssl,
char* p_buf,
unsigned int len,
int (*p_ssl_func)(SSL*, void*, int));
static int ssl_inited;
static struct mystr debug_str;
@ -133,11 +138,31 @@ ssl_init(struct vsf_session* p_sess)
}
}
}
{
char* p_ctx_id = "vsftpd";
SSL_CTX_set_session_id_context(p_ctx, (void*) p_ctx_id,
vsf_sysutil_strlen(p_ctx_id));
}
p_sess->p_ssl_ctx = p_ctx;
ssl_inited = 1;
}
}
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;
}
void
handle_auth(struct vsf_session* p_sess)
{
@ -148,16 +173,7 @@ handle_auth(struct vsf_session* p_sess)
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: ");
/* 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;
ssl_control_handshake(p_sess);
if (str_equal_text(&p_sess->ftp_arg_str, "SSL") ||
str_equal_text(&p_sess->ftp_arg_str, "TLS-P"))
{
@ -212,50 +228,31 @@ handle_prot(struct vsf_session* p_sess)
}
}
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(struct vsf_session* p_sess, void* p_ssl, char* p_buf, unsigned int len)
{
char* p_buf_start;
if (buflen == 0)
{
return;
}
p_buf_start = p_buf;
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] = end_char;
buflen -= retval;
if (p_buf[retval - 1] == end_char || buflen == 0)
{
break;
}
p_buf += retval;
}
str_alloc_alt_term(p_str, p_buf_start, end_char);
return ssl_read_common(p_sess, (SSL*) p_ssl, p_buf, len, SSL_read);
}
int
ssl_read(struct vsf_session* p_sess, char* p_buf, unsigned int len)
ssl_peek(struct vsf_session* p_sess, void* p_ssl, char* p_buf, unsigned int len)
{
return ssl_read_common(p_sess, (SSL*) p_ssl, p_buf, len, SSL_peek);
}
static int
ssl_read_common(struct vsf_session* p_sess,
SSL* p_void_ssl,
char* p_buf,
unsigned int len,
int (*p_ssl_func)(SSL*, void*, int))
{
int retval;
int err;
SSL* p_ssl = p_sess->p_data_ssl;
SSL* p_ssl = (SSL*) p_void_ssl;
do
{
retval = SSL_read(p_ssl, p_buf, len);
retval = (*p_ssl_func)(p_ssl, p_buf, len);
err = SSL_get_error(p_ssl, retval);
}
while (retval < 0 && (err == SSL_ERROR_WANT_READ ||
@ -302,6 +299,22 @@ ssl_write_str(void* p_ssl, const struct mystr* p_str)
return 0;
}
int
ssl_read_into_str(struct vsf_session* p_sess, void* p_ssl, struct mystr* p_str)
{
unsigned int len = str_getlen(p_str);
int ret = ssl_read(p_sess, p_ssl, (char*) str_getbuf(p_str), len);
if (ret >= 0)
{
str_trunc(p_str, (unsigned int) ret);
}
else
{
str_empty(p_str);
}
return ret;
}
static void
maybe_log_shutdown_state(struct vsf_session* p_sess)
{
@ -399,6 +412,7 @@ ssl_accept(struct vsf_session* p_sess, int fd)
* we can check for a match between the control and data connections.
*/
SSL* p_ssl;
int reused;
if (p_sess->p_data_ssl != NULL)
{
die("p_data_ssl should be NULL.");
@ -410,26 +424,28 @@ ssl_accept(struct vsf_session* p_sess, int fd)
}
p_sess->p_data_ssl = p_ssl;
setup_bio_callbacks(p_ssl);
reused = SSL_session_reused(p_ssl);
if (tunable_require_ssl_reuse && !reused)
{
str_alloc_text(&debug_str, "No SSL session reuse on data channel.");
vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
ssl_data_close(p_sess);
return 0;
}
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);
}
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);
}
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;
}
@ -445,12 +461,42 @@ ssl_accept(struct vsf_session* p_sess, int fd)
void
ssl_comm_channel_init(struct vsf_session* p_sess)
{
if (p_sess->ssl_consumer_fd != -1)
{
bug("ssl_consumer_fd active");
}
if (p_sess->ssl_slave_fd != -1)
{
bug("ssl_slave_fd active");
}
const struct vsf_sysutil_socketpair_retval retval =
vsf_sysutil_unix_stream_socketpair();
p_sess->ssl_consumer_fd = retval.socket_one;
p_sess->ssl_slave_fd = retval.socket_two;
}
void
ssl_comm_channel_set_consumer_context(struct vsf_session* p_sess)
{
if (p_sess->ssl_slave_fd == -1)
{
bug("ssl_slave_fd already closed");
}
vsf_sysutil_close(p_sess->ssl_slave_fd);
p_sess->ssl_slave_fd = -1;
}
void
ssl_comm_channel_set_producer_context(struct vsf_session* p_sess)
{
if (p_sess->ssl_consumer_fd == -1)
{
bug("ssl_consumer_fd already closed");
}
vsf_sysutil_close(p_sess->ssl_consumer_fd);
p_sess->ssl_consumer_fd = -1;
}
static SSL*
get_ssl(struct vsf_session* p_sess, int fd)
{
@ -483,17 +529,18 @@ get_ssl(struct vsf_session* p_sess, int fd)
str_append_text(&debug_str, p_err);
vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
}
/* The RFC is quite clear that we can just close the control channel
* here.
*/
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);
int reused = SSL_session_reused(p_ssl);
str_alloc_text(&debug_str, "SSL version: ");
str_append_text(&debug_str, p_ssl_version);
str_append_text(&debug_str, ", SSL cipher: ");
@ -613,6 +660,22 @@ ssl_verify_callback(int verify_ok, X509_STORE_CTX* p_ctx)
return 1;
}
void
ssl_add_entropy(struct vsf_session* p_sess)
{
/* Although each child does seem to have its different pool of entropy, I
* don't trust the interaction of OpenSSL's opaque RAND API and fork(). So
* throw a bit more in (only works on systems with /dev/urandom for now).
*/
int ret = RAND_load_file("/dev/urandom", 16);
if (ret != 16)
{
str_alloc_text(&debug_str, "Couldn't add extra OpenSSL entropy: ");
str_append_ulong(&debug_str, (unsigned long) ret);
vsf_log_line(p_sess, kVSFLogEntryDebug, &debug_str);
}
}
#else /* VSF_BUILD_SSL */
void
@ -622,6 +685,12 @@ ssl_init(struct vsf_session* p_sess)
die("SSL: ssl_enable is set but SSL support not compiled in");
}
void
ssl_control_handshake(struct vsf_session* p_sess)
{
(void) p_sess;
}
void
handle_auth(struct vsf_session* p_sess)
{
@ -640,21 +709,21 @@ handle_prot(struct vsf_session* p_sess)
(void) p_sess;
}
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(struct vsf_session* p_sess, void* p_ssl, char* p_buf, unsigned int len)
{
(void) p_sess;
(void) p_str;
(void) end_char;
(void) p_ssl;
(void) p_buf;
(void) buflen;
(void) len;
return -1;
}
int
ssl_read(struct vsf_session* p_sess, char* p_buf, unsigned int len)
ssl_peek(struct vsf_session* p_sess, void* p_ssl, char* p_buf, unsigned int len)
{
(void) p_sess;
(void) p_ssl;
(void) p_buf;
(void) len;
return -1;
@ -698,5 +767,32 @@ ssl_comm_channel_init(struct vsf_session* p_sess)
(void) p_sess;
}
void
ssl_comm_channel_set_consumer_context(struct vsf_session* p_sess)
{
(void) p_sess;
}
void
ssl_comm_channel_set_producer_context(struct vsf_session* p_sess)
{
(void) p_sess;
}
void
ssl_add_entropy(struct vsf_session* p_sess)
{
(void) p_sess;
}
int
ssl_read_into_str(struct vsf_session* p_sess, void* p_ssl, struct mystr* p_str)
{
(void) p_sess;
(void) p_ssl;
(void) p_str;
return -1;
}
#endif /* VSF_BUILD_SSL */

18
ssl.h
View File

@ -4,18 +4,30 @@
struct vsf_session;
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(struct vsf_session* p_sess, char* p_buf, unsigned int len);
int ssl_read(struct vsf_session* p_sess,
void* p_ssl,
char* p_buf,
unsigned int len);
int ssl_peek(struct vsf_session* p_sess,
void* p_ssl,
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);
int ssl_read_into_str(struct vsf_session* p_sess,
void* p_ssl,
struct mystr* p_str);
void ssl_init(struct vsf_session* p_sess);
int ssl_accept(struct vsf_session* p_sess, int fd);
int ssl_data_close(struct vsf_session* p_sess);
void ssl_comm_channel_init(struct vsf_session* p_sess);
void ssl_comm_channel_set_consumer_context(struct vsf_session* p_sess);
void ssl_comm_channel_set_producer_context(struct vsf_session* p_sess);
void handle_auth(struct vsf_session* p_sess);
void handle_pbsz(struct vsf_session* p_sess);
void handle_prot(struct vsf_session* p_sess);
void ssl_control_handshake(struct vsf_session* p_sess);
void ssl_add_entropy(struct vsf_session* p_sess);
#endif /* VSF_SSL_H */

109
sslslave.c Normal file
View File

@ -0,0 +1,109 @@
/*
* Part of Very Secure FTPd
* Licence: GPL v2
* Author: Chris Evans
* sslslave.c
*/
#include "sslslave.h"
#include "session.h"
#include "privsock.h"
#include "tunables.h"
#include "sysutil.h"
#include "sysdeputil.h"
#include "utility.h"
#include "ssl.h"
#include "readwrite.h"
#include "defs.h"
void
ssl_slave(struct vsf_session* p_sess)
{
struct mystr data_str = INIT_MYSTR;
str_reserve(&data_str, VSFTP_DATA_BUFSIZE);
/* Before becoming the slave, clear the alarm for the FTP protocol. */
vsf_sysutil_clear_alarm();
/* No need for any further communications with the privileged parent. */
priv_sock_set_parent_context(p_sess);
if (tunable_setproctitle_enable)
{
vsf_sysutil_setproctitle("SSL handler");
}
while (1)
{
char cmd = priv_sock_get_cmd(p_sess->ssl_slave_fd);
int retval;
if (cmd == PRIV_SOCK_GET_USER_CMD)
{
ftp_getline(p_sess, &p_sess->ftp_cmd_str, p_sess->p_control_line_buf);
priv_sock_send_str(p_sess->ssl_slave_fd, &p_sess->ftp_cmd_str);
}
else if (cmd == PRIV_SOCK_WRITE_USER_RESP)
{
priv_sock_get_str(p_sess->ssl_slave_fd, &p_sess->ftp_cmd_str);
retval = ftp_write_str(p_sess, &p_sess->ftp_cmd_str, kVSFRWControl);
priv_sock_send_int(p_sess->ssl_slave_fd, retval);
}
else if (cmd == PRIV_SOCK_DO_SSL_HANDSHAKE)
{
int ret;
char result = PRIV_SOCK_RESULT_BAD;
if (p_sess->data_fd != -1 || p_sess->p_data_ssl != 0)
{
bug("state not clean");
}
p_sess->data_fd = priv_sock_recv_fd(p_sess->ssl_slave_fd);
ret = ssl_accept(p_sess, p_sess->data_fd);
if (ret == 1)
{
result = PRIV_SOCK_RESULT_OK;
}
else
{
vsf_sysutil_close(p_sess->data_fd);
p_sess->data_fd = -1;
}
priv_sock_send_result(p_sess->ssl_slave_fd, result);
}
else if (cmd == PRIV_SOCK_DO_SSL_READ)
{
str_trunc(&data_str, VSFTP_DATA_BUFSIZE);
int ret = ssl_read_into_str(p_sess, p_sess->p_data_ssl, &data_str);
priv_sock_send_int(p_sess->ssl_slave_fd, ret);
priv_sock_send_str(p_sess->ssl_slave_fd, &data_str);
}
else if (cmd == PRIV_SOCK_DO_SSL_WRITE)
{
int ret;
priv_sock_get_str(p_sess->ssl_slave_fd, &data_str);
ret = ssl_write(p_sess->p_data_ssl,
str_getbuf(&data_str),
str_getlen(&data_str));
priv_sock_send_int(p_sess->ssl_slave_fd, ret);
}
else if (cmd == PRIV_SOCK_DO_SSL_CLOSE)
{
int ret;
char result = PRIV_SOCK_RESULT_BAD;
if (p_sess->data_fd == -1 && p_sess->p_data_ssl == 0)
{
result = PRIV_SOCK_RESULT_OK;
}
else
{
ret = ssl_data_close(p_sess);
if (ret == 1)
{
result = PRIV_SOCK_RESULT_OK;
}
vsf_sysutil_close(p_sess->data_fd);
p_sess->data_fd = -1;
}
priv_sock_send_result(p_sess->ssl_slave_fd, result);
}
else
{
die("bad request in process_ssl_slave_req");
}
}
}

17
sslslave.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef VSF_SSLSLAVE_H
#define VSF_SSLSLAVE_H
struct vsf_session;
/* ssl_slave()
* PURPOSE
* An internal function that takes care of running the "SSL slave" process. It
* is needed because the initial SSL handshake state may belong to a different
* process that the process running the FTP protocol.
* PARAMETERS
* p_sess - the session object
*/
void ssl_slave(struct vsf_session* p_sess);
#endif /* VSF_SSLSLAVE_H */

View File

@ -24,8 +24,8 @@ static struct hash* s_p_ip_count_hash;
static struct hash* s_p_pid_ip_hash;
static unsigned int s_ipaddr_size;
static void handle_sigchld(int duff);
static void handle_sighup(int duff);
static void handle_sigchld(void* duff);
static void handle_sighup(void* duff);
static void prepare_child(int sockfd);
static unsigned int handle_ip_count(void* p_raw_addr);
static void drop_ip_count(void* p_raw_addr);
@ -53,9 +53,7 @@ vsf_standalone_main(void)
vsf_sysutil_exit(0);
}
/* Son, close standard FDs to avoid SSH hang-on-exit */
vsf_sysutil_close_failok(0);
vsf_sysutil_close_failok(1);
vsf_sysutil_close_failok(2);
vsf_sysutil_reopen_standard_fds();
vsf_sysutil_make_session_leader();
}
if (tunable_listen)
@ -76,8 +74,8 @@ vsf_standalone_main(void)
{
vsf_sysutil_setproctitle("LISTENER");
}
vsf_sysutil_install_async_sighandler(kVSFSysUtilSigCHLD, handle_sigchld);
vsf_sysutil_install_async_sighandler(kVSFSysUtilSigHUP, handle_sighup);
vsf_sysutil_install_sighandler(kVSFSysUtilSigCHLD, handle_sigchld, 0, 1);
vsf_sysutil_install_sighandler(kVSFSysUtilSigHUP, handle_sighup, 0, 1);
if (tunable_listen)
{
struct vsf_sysutil_sockaddr* p_sockaddr = 0;
@ -142,12 +140,8 @@ vsf_standalone_main(void)
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;
@ -177,6 +171,7 @@ vsf_standalone_main(void)
else
{
/* Child context */
vsf_set_die_if_parent_dies();
vsf_sysutil_close(listen_sock);
prepare_child(new_client_sock);
/* By returning here we "launch" the child process with the same
@ -224,7 +219,7 @@ drop_ip_count(void* p_raw_addr)
}
static void
handle_sigchld(int duff)
handle_sigchld(void* duff)
{
unsigned int reap_one = 1;
(void) duff;
@ -246,10 +241,11 @@ handle_sigchld(int duff)
}
static void
handle_sighup(int duff)
handle_sighup(void* duff)
{
(void) duff;
/* We don't crash the out the listener if an invalid config was added */
tunables_load_defaults();
vsf_parseconf_load_file(0, 0);
}

14
str.c
View File

@ -573,6 +573,20 @@ str_contains_space(const struct mystr* p_str)
return 0;
}
int
str_all_space(const struct mystr* p_str)
{
unsigned int i;
for (i=0; i < p_str->len; i++)
{
if (!vsf_sysutil_isspace(p_str->p_buf[i]))
{
return 0;
}
}
return 1;
}
int
str_contains_unprintable(const struct mystr* p_str)
{

1
str.h
View File

@ -94,6 +94,7 @@ void str_mid_to_end(const struct mystr* p_str, struct mystr* p_out,
char str_get_char_at(const struct mystr* p_str, const unsigned int indexx);
int str_contains_space(const struct mystr* p_str);
int str_all_space(const struct mystr* p_str);
int str_contains_unprintable(const struct mystr* p_str);
void str_replace_unprintable(struct mystr* p_str, char new_char);
int str_atoi(const struct mystr* p_str);

View File

@ -35,9 +35,13 @@
#include <sys/param.h>
#include <sys/uio.h>
#include <sys/prctl.h>
#include <signal.h>
/* Configuration.. here are the possibilities */
#undef VSF_SYSDEP_HAVE_CAPABILITIES
#undef VSF_SYSDEP_HAVE_SETKEEPCAPS
#undef VSF_SYSDEP_HAVE_SETPDEATHSIG
#undef VSF_SYSDEP_HAVE_LINUX_SENDFILE
#undef VSF_SYSDEP_HAVE_FREEBSD_SENDFILE
#undef VSF_SYSDEP_HAVE_HPUX_SENDFILE
@ -66,10 +70,12 @@
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))
#define VSF_SYSDEP_HAVE_CAPABILITIES
#define VSF_SYSDEP_HAVE_LINUX_SENDFILE
#include <sys/prctl.h>
#ifdef PR_SET_KEEPCAPS
#define VSF_SYSDEP_HAVE_SETKEEPCAPS
#endif
#ifdef PR_SET_PDEATHSIG
#define VSF_SYSDEP_HAVE_SETPDEATHSIG
#endif
#endif
#endif
#endif
@ -159,7 +165,10 @@
#include <linux/capability.h>
#include <errno.h>
#include <syscall.h>
_syscall2(int, capset, cap_user_header_t, header, const cap_user_data_t, data)
int capset(cap_user_header_t header, const cap_user_data_t data)
{
return syscall(__NR_capset, header, data);
}
/* Gross HACK to avoid warnings - linux headers overlap glibc headers */
#undef __NFDBITS
#undef __FDMASK
@ -308,7 +317,7 @@ vsf_sysdep_check_auth(const struct mystr* p_user_str,
retval = pam_set_item(s_pamh, PAM_RHOST, str_getbuf(p_remote_host));
if (retval != PAM_SUCCESS)
{
(void) pam_end(s_pamh, 0);
(void) pam_end(s_pamh, retval);
s_pamh = 0;
return 0;
}
@ -317,7 +326,7 @@ vsf_sysdep_check_auth(const struct mystr* p_user_str,
retval = pam_set_item(s_pamh, PAM_TTY, "ftp");
if (retval != PAM_SUCCESS)
{
(void) pam_end(s_pamh, 0);
(void) pam_end(s_pamh, retval);
s_pamh = 0;
return 0;
}
@ -326,7 +335,7 @@ vsf_sysdep_check_auth(const struct mystr* p_user_str,
retval = pam_set_item(s_pamh, PAM_RUSER, str_getbuf(p_user_str));
if (retval != PAM_SUCCESS)
{
(void) pam_end(s_pamh, 0);
(void) pam_end(s_pamh, retval);
s_pamh = 0;
return 0;
}
@ -334,28 +343,28 @@ vsf_sysdep_check_auth(const struct mystr* p_user_str,
retval = pam_authenticate(s_pamh, 0);
if (retval != PAM_SUCCESS)
{
(void) pam_end(s_pamh, 0);
(void) pam_end(s_pamh, retval);
s_pamh = 0;
return 0;
}
retval = pam_acct_mgmt(s_pamh, 0);
if (retval != PAM_SUCCESS)
{
(void) pam_end(s_pamh, 0);
(void) pam_end(s_pamh, retval);
s_pamh = 0;
return 0;
}
retval = pam_setcred(s_pamh, PAM_ESTABLISH_CRED);
if (retval != PAM_SUCCESS)
{
(void) pam_end(s_pamh, 0);
(void) pam_end(s_pamh, retval);
s_pamh = 0;
return 0;
}
if (!tunable_session_support)
{
/* You're in already! */
(void) pam_end(s_pamh, 0);
(void) pam_end(s_pamh, retval);
s_pamh = 0;
return 1;
}
@ -366,7 +375,7 @@ vsf_sysdep_check_auth(const struct mystr* p_user_str,
{
vsf_remove_uwtmp();
(void) pam_setcred(s_pamh, PAM_DELETE_CRED);
(void) pam_end(s_pamh, 0);
(void) pam_end(s_pamh, retval);
s_pamh = 0;
return 0;
}
@ -387,7 +396,7 @@ vsf_auth_shutdown(void)
}
(void) pam_close_session(s_pamh, 0);
(void) pam_setcred(s_pamh, PAM_DELETE_CRED);
(void) pam_end(s_pamh, 0);
(void) pam_end(s_pamh, PAM_SUCCESS);
s_pamh = 0;
vsf_remove_uwtmp();
}
@ -1190,3 +1199,24 @@ vsf_remove_uwtmp(void)
#endif /* !VSF_SYSDEP_HAVE_UTMPX */
void
vsf_set_die_if_parent_dies()
{
#ifdef VSF_SYSDEP_HAVE_SETPDEATHSIG
if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) != 0)
{
die("prctl");
}
#endif
}
void
vsf_set_term_if_parent_dies()
{
#ifdef VSF_SYSDEP_HAVE_SETPDEATHSIG
if (prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0) != 0)
{
die("prctl");
}
#endif
}

View File

@ -55,5 +55,10 @@ void* vsf_sysutil_map_anon_pages(unsigned int length);
void vsf_sysutil_send_fd(int sock_fd, int send_fd);
int vsf_sysutil_recv_fd(int sock_fd);
/* If supported, arrange for current process to die when parent dies. */
void vsf_set_die_if_parent_dies();
/* Or a softer version delivering SIGTERM. */
void vsf_set_term_if_parent_dies();
#endif /* VSF_SYSDEPUTIL_H */

116
sysutil.c
View File

@ -53,6 +53,7 @@
#include <syslog.h>
#include <utime.h>
#include <netdb.h>
#include <sys/resource.h>
/* Private variables to this file */
/* Current umask() */
@ -73,6 +74,7 @@ static struct vsf_sysutil_sig_details
void* p_private;
int pending;
int running;
int use_alarm;
} s_sig_details[NSIG];
static vsf_context_io_t s_io_handler;
@ -91,6 +93,7 @@ struct vsf_sysutil_sockaddr
/* File locals */
static void vsf_sysutil_common_sighandler(int signum);
static void vsf_sysutil_alrm_sighandler(int signum);
static int vsf_sysutil_translate_sig(const enum EVSFSysUtilSignal sig);
static void vsf_sysutil_set_sighandler(int sig, void (*p_handlefunc)(int));
static int vsf_sysutil_translate_memprot(
@ -101,16 +104,33 @@ static void vsf_sysutil_alloc_statbuf(struct vsf_sysutil_statbuf** p_ptr);
void vsf_sysutil_sockaddr_alloc(struct vsf_sysutil_sockaddr** p_sockptr);
static int lock_internal(int fd, int lock_type);
static void
vsf_sysutil_alrm_sighandler(int signum)
{
(void) signum;
alarm(1);
}
static void
vsf_sysutil_common_sighandler(int signum)
{
if (signum < 0 || signum >= NSIG)
{
// bug() is not async safe but this check really is a "cannot happen"
// debug aid.
bug("signal out of range in vsf_sysutil_common_sighandler");
}
if (s_sig_details[signum].sync_sig_handler)
{
s_sig_details[signum].pending = 1;
/* Since this synchronous signal framework has a small race (signal coming
* in just before we start a blocking call), there's the option to fire an
* alarm repeatedly until the signal is handled.
*/
if (s_sig_details[signum].use_alarm)
{
alarm(1);
}
}
}
@ -143,6 +163,10 @@ vsf_sysutil_check_pending_actions(
if (s_sig_details[i].pending && !s_sig_details[i].running)
{
s_sig_details[i].running = 1;
if (s_sig_details[i].use_alarm)
{
alarm(0);
}
if (s_sig_details[i].sync_sig_handler)
{
s_sig_details[i].pending = 0;
@ -190,12 +214,19 @@ vsf_sysutil_translate_sig(const enum EVSFSysUtilSignal sig)
void
vsf_sysutil_install_sighandler(const enum EVSFSysUtilSignal sig,
vsf_sighandle_t handler, void* p_private)
vsf_sighandle_t handler,
void* p_private,
int use_alarm)
{
int realsig = vsf_sysutil_translate_sig(sig);
s_sig_details[realsig].p_private = p_private;
s_sig_details[realsig].sync_sig_handler = handler;
s_sig_details[realsig].use_alarm = use_alarm;
vsf_sysutil_set_sighandler(realsig, vsf_sysutil_common_sighandler);
if (use_alarm && realsig != SIGALRM)
{
vsf_sysutil_set_sighandler(SIGALRM, vsf_sysutil_alrm_sighandler);
}
}
void
@ -601,17 +632,20 @@ int
vsf_sysutil_wait_exited_normally(
const struct vsf_sysutil_wait_retval* p_waitret)
{
return WIFEXITED(p_waitret->exit_status);
int status = ((struct vsf_sysutil_wait_retval*) p_waitret)->exit_status;
return WIFEXITED(status);
}
int
vsf_sysutil_wait_get_exitcode(const struct vsf_sysutil_wait_retval* p_waitret)
{
int status;
if (!vsf_sysutil_wait_exited_normally(p_waitret))
{
bug("not a normal exit in vsf_sysutil_wait_get_exitcode");
}
return WEXITSTATUS(p_waitret->exit_status);
status = ((struct vsf_sysutil_wait_retval*) p_waitret)->exit_status;
return WEXITSTATUS(status);
}
void
@ -1561,6 +1595,9 @@ vsf_sysutil_get_error(void)
case EOPNOTSUPP:
retval = kVSFSysUtilErrOPNOTSUPP;
break;
case EACCES:
retval = kVSFSysUtilErrOPNOTSUPP;
break;
}
return retval;
}
@ -2452,6 +2489,28 @@ vsf_sysutil_make_session_leader(void)
}
}
void
vsf_sysutil_reopen_standard_fds(void)
{
/* This reopens STDIN, STDOUT and STDERR to /dev/null */
int fd;
if ((fd = open("/dev/null", O_RDWR, 0)) < 0)
{
goto error;
}
vsf_sysutil_dupfd2(fd, STDIN_FILENO);
vsf_sysutil_dupfd2(fd, STDOUT_FILENO);
vsf_sysutil_dupfd2(fd, STDERR_FILENO);
if ( fd > 2 )
{
vsf_sysutil_close(fd);
}
return;
error:
die("reopening standard file descriptors to /dev/null failed");
}
void
vsf_sysutil_tzset(void)
{
@ -2587,13 +2646,24 @@ vsf_sysutil_getenv(const char* p_var)
}
void
vsf_sysutil_openlog(void)
vsf_sysutil_openlog(int force)
{
int facility = LOG_DAEMON;
int option = LOG_PID;
if (!force)
{
option |= LOG_NDELAY;
}
#ifdef LOG_FTP
facility = LOG_FTP;
#endif
openlog("vsftpd", LOG_NDELAY, facility);
openlog("vsftpd", option, facility);
}
void
vsf_sysutil_closelog(void)
{
closelog();
}
void
@ -2654,3 +2724,39 @@ vsf_sysutil_setmodtime(const char* p_file, long the_time, int is_localtime)
return utime(p_file, &new_times);
}
void
vsf_sysutil_ftruncate(int fd)
{
int ret = ftruncate(fd, 0);
if (ret != 0)
{
die("ftruncate");
}
}
int
vsf_sysutil_getuid(void)
{
return getuid();
}
void
vsf_sysutil_set_address_space_limit(long bytes)
{
/* Unfortunately, OpenBSD is missing RLIMIT_AS. */
#ifdef RLIMIT_AS
int ret;
struct rlimit rlim;
rlim.rlim_cur = bytes;
rlim.rlim_max = bytes;
ret = setrlimit(RLIMIT_AS, &rlim);
/* Permit EPERM as this could indicate that the shell launching vsftpd already
* has a lower limit.
*/
if (ret != 0 && errno != EPERM)
{
die("setrlimit");
}
#endif /* RLIMIT_AS */
(void) bytes;
}

View File

@ -16,7 +16,8 @@ enum EVSFSysUtilError
kVSFSysUtilErrNOSYS,
kVSFSysUtilErrINTR,
kVSFSysUtilErrINVAL,
kVSFSysUtilErrOPNOTSUPP
kVSFSysUtilErrOPNOTSUPP,
kVSFSysUtilErrACCES
};
enum EVSFSysUtilError vsf_sysutil_get_error(void);
@ -41,7 +42,9 @@ typedef void (*vsf_context_io_t)(int, int, void*);
void vsf_sysutil_install_null_sighandler(const enum EVSFSysUtilSignal sig);
void vsf_sysutil_install_sighandler(const enum EVSFSysUtilSignal,
vsf_sighandle_t handler, void* p_private);
vsf_sighandle_t handler,
void* p_private,
int use_alarm);
void vsf_sysutil_install_async_sighandler(const enum EVSFSysUtilSignal sig,
vsf_async_sighandle_t handler);
void vsf_sysutil_default_sig(const enum EVSFSysUtilSignal sig);
@ -90,6 +93,7 @@ void vsf_sysutil_close(int fd);
int vsf_sysutil_close_failok(int fd);
int vsf_sysutil_unlink(const char* p_dead);
int vsf_sysutil_write_access(const char* p_filename);
void vsf_sysutil_ftruncate(int fd);
/* Reading and writing */
void vsf_sysutil_lseek_to(const int fd, filesize_t seek_pos);
@ -293,6 +297,7 @@ unsigned char vsf_sysutil_get_random_byte(void);
unsigned int vsf_sysutil_get_umask(void);
void vsf_sysutil_set_umask(unsigned int umask);
void vsf_sysutil_make_session_leader(void);
void vsf_sysutil_reopen_standard_fds(void);
void vsf_sysutil_tzset(void);
const char* vsf_sysutil_get_current_date(void);
void vsf_sysutil_qsort(void* p_base, unsigned int num_elem,
@ -301,10 +306,12 @@ void vsf_sysutil_qsort(void* p_base, unsigned int num_elem,
char* vsf_sysutil_getenv(const char* p_var);
typedef void (*exitfunc_t)(void);
void vsf_sysutil_set_exit_func(exitfunc_t exitfunc);
int vsf_sysutil_getuid(void);
/* Syslogging (bah) */
void vsf_sysutil_openlog(void);
void vsf_sysutil_openlog(int force);
void vsf_sysutil_syslog(const char* p_text, int severe);
void vsf_sysutil_closelog(void);
/* Credentials handling */
int vsf_sysutil_running_as_root(void);
@ -330,5 +337,8 @@ long vsf_sysutil_parse_time(const char* p_text);
void vsf_sysutil_sleep(double seconds);
int vsf_sysutil_setmodtime(const char* p_file, long the_time, int is_localtime);
/* Limits */
void vsf_sysutil_set_address_space_limit(long bytes);
#endif /* VSF_SYSUTIL_H */

View File

@ -10,6 +10,7 @@
#include "tcpwrap.h"
#include "builddefs.h"
#include "utility.h"
#include "sysutil.h"
#ifdef VSF_BUILD_TCPWRAPPERS
#include <tcpd.h>
@ -26,12 +27,15 @@ int
vsf_tcp_wrapper_ok(int remote_fd)
{
struct request_info req;
vsf_sysutil_openlog(0);
request_init(&req, RQ_DAEMON, "vsftpd", RQ_FILE, remote_fd, 0);
fromhost(&req);
if (!hosts_access(&req))
{
vsf_sysutil_closelog();
return 0;
}
vsf_sysutil_closelog();
return 1;
}

View File

@ -6,133 +6,289 @@
*/
#include "tunables.h"
#include "sysutil.h"
int tunable_anonymous_enable = 1;
int tunable_local_enable = 0;
int tunable_pasv_enable = 1;
int tunable_port_enable = 1;
int tunable_chroot_local_user = 0;
int tunable_write_enable = 0;
int tunable_anon_upload_enable = 0;
int tunable_anon_mkdir_write_enable = 0;
int tunable_anon_other_write_enable = 0;
int tunable_chown_uploads = 0;
int tunable_connect_from_port_20 = 0;
int tunable_xferlog_enable = 0;
int tunable_dirmessage_enable = 0;
int tunable_anon_world_readable_only = 1;
int tunable_async_abor_enable = 0;
int tunable_ascii_upload_enable = 0;
int tunable_ascii_download_enable = 0;
int tunable_one_process_model = 0;
int tunable_xferlog_std_format = 0;
int tunable_pasv_promiscuous = 0;
int tunable_deny_email_enable = 0;
int tunable_chroot_list_enable = 0;
int tunable_setproctitle_enable = 0;
int tunable_text_userdb_names = 0;
int tunable_ls_recurse_enable = 0;
int tunable_log_ftp_protocol = 0;
int tunable_guest_enable = 0;
int tunable_userlist_enable = 0;
int tunable_userlist_deny = 1;
int tunable_use_localtime = 0;
int tunable_check_shell = 1;
int tunable_hide_ids = 0;
int tunable_listen = 0;
int tunable_port_promiscuous = 0;
int tunable_passwd_chroot_enable = 0;
int tunable_no_anon_password = 0;
int tunable_tcp_wrappers = 0;
int tunable_use_sendfile = 1;
int tunable_force_dot_files = 0;
int tunable_listen_ipv6 = 0;
int tunable_dual_log_enable = 0;
int tunable_syslog_enable = 0;
int tunable_background = 0;
int tunable_virtual_use_local_privs = 0;
int tunable_session_support = 0;
int tunable_download_enable = 1;
int tunable_dirlist_enable = 1;
int tunable_chmod_enable = 1;
int tunable_secure_email_list_enable = 0;
int tunable_run_as_launching_user = 0;
int tunable_no_log_lock = 0;
int tunable_ssl_enable = 0;
int tunable_allow_anon_ssl = 0;
int tunable_force_local_logins_ssl = 1;
int tunable_force_local_data_ssl = 1;
int tunable_sslv2 = 0;
int tunable_sslv3 = 0;
int tunable_tlsv1 = 1;
int tunable_tilde_user_enable = 0;
int tunable_force_anon_logins_ssl = 0;
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;
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;
int tunable_anonymous_enable;
int tunable_local_enable;
int tunable_pasv_enable;
int tunable_port_enable;
int tunable_chroot_local_user;
int tunable_write_enable;
int tunable_anon_upload_enable;
int tunable_anon_mkdir_write_enable;
int tunable_anon_other_write_enable;
int tunable_chown_uploads;
int tunable_connect_from_port_20;
int tunable_xferlog_enable;
int tunable_dirmessage_enable;
int tunable_anon_world_readable_only;
int tunable_async_abor_enable;
int tunable_ascii_upload_enable;
int tunable_ascii_download_enable;
int tunable_one_process_model;
int tunable_xferlog_std_format;
int tunable_pasv_promiscuous;
int tunable_deny_email_enable;
int tunable_chroot_list_enable;
int tunable_setproctitle_enable;
int tunable_text_userdb_names;
int tunable_ls_recurse_enable;
int tunable_log_ftp_protocol;
int tunable_guest_enable;
int tunable_userlist_enable;
int tunable_userlist_deny;
int tunable_use_localtime;
int tunable_check_shell;
int tunable_hide_ids;
int tunable_listen;
int tunable_port_promiscuous;
int tunable_passwd_chroot_enable;
int tunable_no_anon_password;
int tunable_tcp_wrappers;
int tunable_use_sendfile;
int tunable_force_dot_files;
int tunable_listen_ipv6;
int tunable_dual_log_enable;
int tunable_syslog_enable;
int tunable_background;
int tunable_virtual_use_local_privs;
int tunable_session_support;
int tunable_download_enable;
int tunable_dirlist_enable;
int tunable_chmod_enable;
int tunable_secure_email_list_enable;
int tunable_run_as_launching_user;
int tunable_no_log_lock;
int tunable_ssl_enable;
int tunable_allow_anon_ssl;
int tunable_force_local_logins_ssl;
int tunable_force_local_data_ssl;
int tunable_sslv2;
int tunable_sslv3;
int tunable_tlsv1;
int tunable_tilde_user_enable;
int tunable_force_anon_logins_ssl;
int tunable_force_anon_data_ssl;
int tunable_mdtm_write;
int tunable_lock_upload_files;
int tunable_pasv_addr_resolve;
int tunable_debug_ssl;
int tunable_require_cert;
int tunable_validate_cert;
int tunable_strict_ssl_read_eof;
int tunable_strict_ssl_write_shutdown;
int tunable_ssl_request_cert;
int tunable_delete_failed_uploads;
int tunable_implicit_ssl;
int tunable_sandbox;
int tunable_require_ssl_reuse;
unsigned int tunable_accept_timeout = 60;
unsigned int tunable_connect_timeout = 60;
unsigned int tunable_local_umask = 077;
unsigned int tunable_anon_umask = 077;
unsigned int tunable_ftp_data_port = 20;
unsigned int tunable_idle_session_timeout = 300;
unsigned int tunable_data_connection_timeout = 300;
/* IPPORT_USERRESERVED + 1 */
unsigned int tunable_pasv_min_port = 5001;
unsigned int tunable_pasv_max_port = 0;
unsigned int tunable_anon_max_rate = 0;
unsigned int tunable_local_max_rate = 0;
/* IPPORT_FTP */
unsigned int tunable_listen_port = 21;
unsigned int tunable_max_clients = 0;
/* -rw-rw-rw- */
unsigned int tunable_file_open_mode = 0666;
unsigned int tunable_max_per_ip = 0;
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;
unsigned int tunable_accept_timeout;
unsigned int tunable_connect_timeout;
unsigned int tunable_local_umask;
unsigned int tunable_anon_umask;
unsigned int tunable_ftp_data_port;
unsigned int tunable_idle_session_timeout;
unsigned int tunable_data_connection_timeout;
unsigned int tunable_pasv_min_port;
unsigned int tunable_pasv_max_port;
unsigned int tunable_anon_max_rate;
unsigned int tunable_local_max_rate;
unsigned int tunable_listen_port;
unsigned int tunable_max_clients;
unsigned int tunable_file_open_mode;
unsigned int tunable_max_per_ip;
unsigned int tunable_trans_chunk_size;
unsigned int tunable_delay_failed_login;
unsigned int tunable_delay_successful_login;
unsigned int tunable_max_login_fails;
unsigned int tunable_chown_upload_mode;
const char* tunable_secure_chroot_dir = "/usr/share/empty";
const char* tunable_ftp_username = "ftp";
const char* tunable_chown_username = "root";
const char* tunable_xferlog_file = "/var/log/xferlog";
const char* tunable_vsftpd_log_file = "/var/log/vsftpd.log";
const char* tunable_message_file = ".message";
const char* tunable_nopriv_user = "nobody";
const char* tunable_ftpd_banner = 0;
const char* tunable_banned_email_file = "/etc/vsftpd.banned_emails";
const char* tunable_chroot_list_file = "/etc/vsftpd.chroot_list";
const char* tunable_pam_service_name = "ftp";
const char* tunable_guest_username = "ftp";
const char* tunable_userlist_file = "/etc/vsftpd.user_list";
const char* tunable_anon_root = 0;
const char* tunable_local_root = 0;
const char* tunable_banner_file = 0;
const char* tunable_pasv_address = 0;
const char* tunable_listen_address = 0;
const char* tunable_user_config_dir = 0;
const char* tunable_listen_address6 = 0;
const char* tunable_cmds_allowed = 0;
const char* tunable_hide_file = 0;
const char* tunable_deny_file = 0;
const char* tunable_user_sub_token = 0;
const char* tunable_email_password_file = "/etc/vsftpd.email_passwords";
const char* tunable_rsa_cert_file = "/usr/share/ssl/certs/vsftpd.pem";
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;
const char* tunable_secure_chroot_dir;
const char* tunable_ftp_username;
const char* tunable_chown_username;
const char* tunable_xferlog_file;
const char* tunable_vsftpd_log_file;
const char* tunable_message_file;
const char* tunable_nopriv_user;
const char* tunable_ftpd_banner;
const char* tunable_banned_email_file;
const char* tunable_chroot_list_file;
const char* tunable_pam_service_name;
const char* tunable_guest_username;
const char* tunable_userlist_file;
const char* tunable_anon_root;
const char* tunable_local_root;
const char* tunable_banner_file;
const char* tunable_pasv_address;
const char* tunable_listen_address;
const char* tunable_user_config_dir;
const char* tunable_listen_address6;
const char* tunable_cmds_allowed;
const char* tunable_cmds_denied;
const char* tunable_hide_file;
const char* tunable_deny_file;
const char* tunable_user_sub_token;
const char* tunable_email_password_file;
const char* tunable_rsa_cert_file;
const char* tunable_dsa_cert_file;
const char* tunable_ssl_ciphers;
const char* tunable_rsa_private_key_file;
const char* tunable_dsa_private_key_file;
const char* tunable_ca_certs_file;
static void install_str_setting(const char* p_value, const char** p_storage);
void
tunables_load_defaults()
{
tunable_anonymous_enable = 1;
tunable_local_enable = 0;
tunable_pasv_enable = 1;
tunable_port_enable = 1;
tunable_chroot_local_user = 0;
tunable_write_enable = 0;
tunable_anon_upload_enable = 0;
tunable_anon_mkdir_write_enable = 0;
tunable_anon_other_write_enable = 0;
tunable_chown_uploads = 0;
tunable_connect_from_port_20 = 0;
tunable_xferlog_enable = 0;
tunable_dirmessage_enable = 0;
tunable_anon_world_readable_only = 1;
tunable_async_abor_enable = 0;
tunable_ascii_upload_enable = 0;
tunable_ascii_download_enable = 0;
tunable_one_process_model = 0;
tunable_xferlog_std_format = 0;
tunable_pasv_promiscuous = 0;
tunable_deny_email_enable = 0;
tunable_chroot_list_enable = 0;
tunable_setproctitle_enable = 0;
tunable_text_userdb_names = 0;
tunable_ls_recurse_enable = 0;
tunable_log_ftp_protocol = 0;
tunable_guest_enable = 0;
tunable_userlist_enable = 0;
tunable_userlist_deny = 1;
tunable_use_localtime = 0;
tunable_check_shell = 1;
tunable_hide_ids = 0;
tunable_listen = 1;
tunable_port_promiscuous = 0;
tunable_passwd_chroot_enable = 0;
tunable_no_anon_password = 0;
tunable_tcp_wrappers = 0;
tunable_use_sendfile = 1;
tunable_force_dot_files = 0;
tunable_listen_ipv6 = 0;
tunable_dual_log_enable = 0;
tunable_syslog_enable = 0;
tunable_background = 0;
tunable_virtual_use_local_privs = 0;
tunable_session_support = 0;
tunable_download_enable = 1;
tunable_dirlist_enable = 1;
tunable_chmod_enable = 1;
tunable_secure_email_list_enable = 0;
tunable_run_as_launching_user = 0;
tunable_no_log_lock = 0;
tunable_ssl_enable = 0;
tunable_allow_anon_ssl = 0;
tunable_force_local_logins_ssl = 1;
tunable_force_local_data_ssl = 1;
tunable_sslv2 = 0;
tunable_sslv3 = 0;
tunable_tlsv1 = 1;
tunable_tilde_user_enable = 0;
tunable_force_anon_logins_ssl = 0;
tunable_force_anon_data_ssl = 0;
tunable_mdtm_write = 1;
tunable_lock_upload_files = 1;
tunable_pasv_addr_resolve = 0;
tunable_debug_ssl = 0;
tunable_require_cert = 0;
tunable_validate_cert = 0;
tunable_strict_ssl_read_eof = 0;
tunable_strict_ssl_write_shutdown = 0;
tunable_ssl_request_cert = 1;
tunable_delete_failed_uploads = 0;
tunable_implicit_ssl = 0;
tunable_sandbox = 0;
tunable_require_ssl_reuse = 1;
tunable_accept_timeout = 60;
tunable_connect_timeout = 60;
tunable_local_umask = 077;
tunable_anon_umask = 077;
tunable_ftp_data_port = 20;
tunable_idle_session_timeout = 300;
tunable_data_connection_timeout = 300;
/* IPPORT_USERRESERVED + 1 */
tunable_pasv_min_port = 5001;
tunable_pasv_max_port = 0;
tunable_anon_max_rate = 0;
tunable_local_max_rate = 0;
/* IPPORT_FTP */
tunable_listen_port = 21;
tunable_max_clients = 0;
/* -rw-rw-rw- */
tunable_file_open_mode = 0666;
tunable_max_per_ip = 0;
tunable_trans_chunk_size = 0;
tunable_delay_failed_login = 1;
tunable_delay_successful_login = 0;
tunable_max_login_fails = 3;
/* -rw------- */
tunable_chown_upload_mode = 0600;
install_str_setting("/usr/share/empty", &tunable_secure_chroot_dir);
install_str_setting("ftp", &tunable_ftp_username);
install_str_setting("root", &tunable_chown_username);
install_str_setting("/var/log/xferlog", &tunable_xferlog_file);
install_str_setting("/var/log/vsftpd.log", &tunable_vsftpd_log_file);
install_str_setting(".message", &tunable_message_file);
install_str_setting("nobody", &tunable_nopriv_user);
install_str_setting(0, &tunable_ftpd_banner);
install_str_setting("/etc/vsftpd.banned_emails", &tunable_banned_email_file);
install_str_setting("/etc/vsftpd.chroot_list", &tunable_chroot_list_file);
install_str_setting("ftp", &tunable_pam_service_name);
install_str_setting("ftp", &tunable_guest_username);
install_str_setting("/etc/vsftpd.user_list", &tunable_userlist_file);
install_str_setting(0, &tunable_anon_root);
install_str_setting(0, &tunable_local_root);
install_str_setting(0, &tunable_banner_file);
install_str_setting(0, &tunable_pasv_address);
install_str_setting(0, &tunable_listen_address);
install_str_setting(0, &tunable_user_config_dir);
install_str_setting(0, &tunable_listen_address6);
install_str_setting(0, &tunable_cmds_allowed);
install_str_setting(0, &tunable_cmds_denied);
install_str_setting(0, &tunable_hide_file);
install_str_setting(0, &tunable_deny_file);
install_str_setting(0, &tunable_user_sub_token);
install_str_setting("/etc/vsftpd.email_passwords",
&tunable_email_password_file);
install_str_setting("/usr/share/ssl/certs/vsftpd.pem",
&tunable_rsa_cert_file);
install_str_setting(0, &tunable_dsa_cert_file);
install_str_setting("DES-CBC3-SHA", &tunable_ssl_ciphers);
install_str_setting(0, &tunable_rsa_private_key_file);
install_str_setting(0, &tunable_dsa_private_key_file);
install_str_setting(0, &tunable_ca_certs_file);
}
void
install_str_setting(const char* p_value, const char** p_storage)
{
char* p_curr_val = (char*) *p_storage;
if (p_curr_val != 0)
{
vsf_sysutil_free(p_curr_val);
}
if (p_value != 0)
{
p_value = vsf_sysutil_strdup(p_value);
}
*p_storage = p_value;
}

View File

@ -1,6 +1,12 @@
#ifndef VSF_TUNABLES_H
#define VSF_TUNABLES_H
/* tunables_load_defaults()
* PURPOSE
* Load the default values into the global settings variables.
*/
void tunables_load_defaults();
/* Configurable preferences */
/* Booleans */
extern int tunable_anonymous_enable; /* Allow anon logins */
@ -74,6 +80,9 @@ 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 */
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 */
/* Integer/numeric defines */
extern unsigned int tunable_accept_timeout;
@ -129,6 +138,7 @@ 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;
extern const char* tunable_cmds_denied;
#endif /* VSF_TUNABLES_H */

View File

@ -26,11 +26,12 @@
#include "readwrite.h"
#include "sysutil.h"
#include "sysdeputil.h"
#include "sslslave.h"
static void drop_all_privs(void);
static void handle_sigchld(int duff);
static void handle_sigchld(void* duff);
static void handle_sigterm(void* duff);
static void process_login_req(struct vsf_session* p_sess);
static void process_ssl_slave_req(struct vsf_session* p_sess);
static void common_do_login(struct vsf_session* p_sess,
const struct mystr* p_user_str, int do_chroot,
int anon);
@ -42,8 +43,9 @@ static void calculate_chdir_dir(int anon, struct mystr* p_userdir_str,
const struct mystr* p_orig_user_str);
static void
handle_sigchld(int duff)
handle_sigchld(void* 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
@ -61,9 +63,22 @@ handle_sigchld(int duff)
}
}
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
vsf_two_process_start(struct vsf_session* p_sess)
{
vsf_sysutil_install_sighandler(kVSFSysUtilSigTERM, handle_sigterm, 0, 1);
/* Overrides the SIGKILL setting set by the standalone listener. */
vsf_set_term_if_parent_dies();
/* Create the comms channel between privileged parent and no-priv child */
priv_sock_init(p_sess);
if (tunable_ssl_enable)
@ -73,11 +88,16 @@ vsf_two_process_start(struct vsf_session* p_sess)
*/
ssl_comm_channel_init(p_sess);
}
vsf_sysutil_install_async_sighandler(kVSFSysUtilSigCHLD, handle_sigchld);
vsf_sysutil_install_sighandler(kVSFSysUtilSigCHLD, handle_sigchld, 0, 1);
{
int newpid = vsf_sysutil_fork();
if (newpid != 0)
{
priv_sock_set_parent_context(p_sess);
if (tunable_ssl_enable)
{
ssl_comm_channel_set_consumer_context(p_sess);
}
/* Parent - go into pre-login parent process mode */
while (1)
{
@ -88,10 +108,11 @@ vsf_two_process_start(struct vsf_session* p_sess)
/* Child process - time to lose as much privilege as possible and do the
* login processing
*/
vsf_sysutil_close(p_sess->parent_fd);
vsf_set_die_if_parent_dies();
priv_sock_set_child_context(p_sess);
if (tunable_ssl_enable)
{
vsf_sysutil_close(p_sess->ssl_consumer_fd);
ssl_comm_channel_set_producer_context(p_sess);
}
if (tunable_local_enable && tunable_userlist_enable)
{
@ -153,13 +174,7 @@ vsf_two_process_login(struct vsf_session* p_sess,
}
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);
ssl_slave(p_sess);
}
/* NOTREACHED */
}
@ -205,10 +220,8 @@ process_login_req(struct vsf_session* p_sess)
{
enum EVSFPrivopLoginResult e_login_result = kVSFLoginNull;
char cmd;
vsf_sysutil_unblock_sig(kVSFSysUtilSigCHLD);
/* Blocks */
cmd = priv_sock_get_cmd(p_sess->parent_fd);
vsf_sysutil_block_sig(kVSFSysUtilSigCHLD);
if (cmd != PRIV_SOCK_LOGIN)
{
die("bad request");
@ -279,32 +292,6 @@ process_login_req(struct vsf_session* p_sess)
/* NOTREACHED */
}
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);
int retval;
if (cmd == PRIV_SOCK_GET_USER_CMD)
{
ftp_getline(p_sess, &p_sess->ftp_cmd_str, p_sess->p_control_line_buf);
priv_sock_send_str(p_sess->ssl_slave_fd, &p_sess->ftp_cmd_str);
}
else if (cmd == PRIV_SOCK_WRITE_USER_RESP)
{
priv_sock_get_str(p_sess->ssl_slave_fd, &p_sess->ftp_cmd_str);
retval = ftp_write_str(p_sess, &p_sess->ftp_cmd_str, kVSFRWControl);
priv_sock_send_int(p_sess->ssl_slave_fd, retval);
}
else
{
die("bad request in process_ssl_slave_req");
}
}
}
static void
common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
int do_chroot, int anon)
@ -323,13 +310,13 @@ common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
{
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, handle_sigchld);
priv_sock_close(p_sess);
priv_sock_init(p_sess);
vsf_sysutil_install_sighandler(kVSFSysUtilSigCHLD, handle_sigchld, 0, 1);
newpid = vsf_sysutil_fork();
if (newpid == 0)
{
@ -339,16 +326,12 @@ common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
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 (p_sess->ssl_slave_active)
{
priv_sock_get_str(p_sess->ssl_consumer_fd,
&p_sess->control_cert_digest);
}
}
/* 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;
@ -393,10 +376,10 @@ common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
bug("should not get here: common_do_login");
}
/* Parent */
priv_sock_set_parent_context(p_sess);
if (tunable_ssl_enable)
{
vsf_sysutil_close(p_sess->ssl_consumer_fd);
/* Keep the SSL slave fd around so we can shutdown() upon exit */
ssl_comm_channel_set_producer_context(p_sess);
}
vsf_priv_parent_postlogin(p_sess);
bug("should not get here in common_do_login");
@ -423,12 +406,8 @@ handle_per_user_config(const struct mystr* p_user_str)
str_append_char(&filename_str, '/');
str_append_str(&filename_str, p_user_str);
retval = str_stat(&filename_str, &p_statbuf);
/* Security - ignore unless owned by root */
if (!vsf_sysutil_retval_is_error(retval) &&
vsf_sysutil_statbuf_get_uid(p_statbuf) == VSFTP_ROOT_UID)
{
vsf_parseconf_load_file(str_getbuf(&filename_str), 1);
}
/* Security - file ownership check now in vsf_parseconf_load_file() */
vsf_parseconf_load_file(str_getbuf(&filename_str), 1);
str_free(&filename_str);
vsf_sysutil_free(p_statbuf);
}

View File

@ -43,9 +43,12 @@ locate_library /usr/lib/libutil.so && echo "-lutil";
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";
if locate_library /lib/libcap.so.1; then
echo "/lib/libcap.so.1";
else
locate_library /usr/lib/libcap.so && echo "-lcap";
locate_library /lib/libcap.so && echo "-lcap";
fi
# Solaris needs this for nanosleep()..
locate_library /lib/libposix4.so && echo "-lposix4";

View File

@ -28,7 +28,8 @@ binary will then launch the FTP service ready for immediate client connections.
.Sh OPTIONS
An optional
.Op configuration file
may be given on the command line. The default configuration file is
may be given on the command line. This file must be owned as root if running as
root. The default configuration file is
.Pa /etc/vsftpd.conf .
.Sh SEE ALSO
.Xr vsftpd.conf 5

View File

@ -50,7 +50,8 @@ connect_from_port_20=YES
# below.
#xferlog_file=/var/log/vsftpd.log
#
# If you want, you can have your log file in standard ftpd xferlog format
# If you want, you can have your log file in standard ftpd xferlog format.
# Note that the default log file location is /var/log/xferlog in this case.
#xferlog_std_format=YES
#
# You may change the default value for timing out an idle session.
@ -100,4 +101,13 @@ connect_from_port_20=YES
# sites. However, some broken FTP clients such as "ncftp" and "mirror" assume
# the presence of the "-R" option, so there is a strong case for enabling it.
#ls_recurse_enable=YES
#
# When "listen" directive is enabled, vsftpd runs in standalone mode and
# listens on IPv4 sockets. This directive cannot be used in conjunction
# with the listen_ipv6 directive.
listen=YES
#
# This directive enables listening on IPv6 sockets. To listen on IPv4 and IPv6
# sockets, you must run two copies of vsftpd whith two configuration files.
# Make sure, that one of the listen options is commented !!
#listen_ipv6=YES

View File

@ -262,6 +262,13 @@ Default: NO
If enabled, all user and group information in directory listings will be
displayed as "ftp".
Default: NO
.TP
.B implicit_ssl
If enabled, an SSL handshake is the first thing expect on all connections
(the FTPS protocol). To support explicit SSL and/or plain text too, a
separate vsftpd listener process should be run.
Default: NO
.TP
.B listen
@ -270,7 +277,7 @@ not be run from an inetd of some kind. Instead, the vsftpd executable is
run once directly. vsftpd itself will then take care of listening for and
handling incoming connections.
Default: NO
Default: YES
.TP
.B listen_ipv6
Like the listen parameter, except vsftpd will listen on an IPv6 socket instead
@ -391,6 +398,13 @@ controlled by
Default: NO
.TP
.B require_ssl_reuse
If set to yes, all SSL data connections are required to exhibit SSL session
reuse (which proves that they know the same master secret as the control
channel). (Added in v2.1.0).
Default: YES
.TP
.B run_as_launching_user
Set to YES if you want vsftpd to run as the user which launched vsftpd. This is
useful where root access is not available. MASSIVE WARNING! Do NOT enable this
@ -788,10 +802,19 @@ Default: /etc/vsftpd.chroot_list
.TP
.B cmds_allowed
This options specifies a comma separated list of allowed FTP commands (post
login. USER, PASS and QUIT are always allowed pre-login). Other
login. USER, PASS and QUIT and others are always allowed pre-login). Other
commands are rejected. This is a powerful method of really locking down an
FTP server. Example: cmds_allowed=PASV,RETR,QUIT
Default: (none)
.TP
.B cmds_denied
This options specifies a comma separated list of denied FTP commands (post
login. USER, PASS, QUIT and others are always allowed pre-login). If a command
appears on both this and
.BR cmds_allowed
then the denial takes precedence. (Added in v2.1.0).
Default: (none)
.TP
.B deny_file

View File

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