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:
parent
19940a28a1
commit
57529485ed
105
Changelog
105
Changelog
@ -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
14
FAQ
@ -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
|
||||
|
2
Makefile
2
Makefile
@ -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
2
README
@ -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/
|
||||
|
@ -1,4 +1,5 @@
|
||||
/var/log/vsftpd.log {
|
||||
# ftpd doesn't handle SIGHUP properly
|
||||
nocompress
|
||||
missingok
|
||||
}
|
||||
|
38
TODO
38
TODO
@ -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
33
ascii.c
@ -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
16
ascii.h
@ -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
5
defs.h
@ -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
7
dummyinc/sys/prctl.h
Normal 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 */
|
||||
|
18
ftpcmdio.c
18
ftpcmdio.c
@ -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)
|
||||
{
|
||||
|
81
ftpdataio.c
81
ftpdataio.c
@ -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
326
ftppolicy.c
Normal 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
18
ftppolicy.h
Normal 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 */
|
23
logging.c
23
logging.c
@ -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
15
main.c
@ -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)
|
||||
{
|
||||
|
15
netstr.c
15
netstr.c
@ -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)
|
||||
{
|
||||
|
18
netstr.h
18
netstr.h
@ -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
|
||||
|
41
oneprocess.c
41
oneprocess.c
@ -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);
|
||||
}
|
||||
|
||||
|
50
parseconf.c
50
parseconf.c
@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
|
69
postlogin.c
69
postlogin.c
@ -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);
|
||||
|
@ -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;
|
||||
|
50
prelogin.c
50
prelogin.c
@ -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);
|
||||
}
|
||||
}
|
||||
|
10
privops.c
10
privops.c
@ -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;
|
||||
}
|
||||
|
75
privsock.c
75
privsock.c
@ -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)
|
||||
{
|
||||
|
54
privsock.h
54
privsock.h
@ -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
1529
ptracesandbox.c
Normal file
File diff suppressed because it is too large
Load Diff
264
ptracesandbox.h
Normal file
264
ptracesandbox.h
Normal 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 */
|
||||
|
95
readwrite.c
95
readwrite.c
@ -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);
|
||||
}
|
||||
|
@ -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
226
ssl.c
@ -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
18
ssl.h
@ -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
109
sslslave.c
Normal 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
17
sslslave.h
Normal 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 */
|
||||
|
22
standalone.c
22
standalone.c
@ -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
14
str.c
@ -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
1
str.h
@ -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);
|
||||
|
52
sysdeputil.c
52
sysdeputil.c
@ -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
|
||||
}
|
||||
|
@ -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
116
sysutil.c
@ -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;
|
||||
}
|
||||
|
16
sysutil.h
16
sysutil.h
@ -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 */
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
408
tunables.c
408
tunables.c
@ -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;
|
||||
}
|
||||
|
10
tunables.h
10
tunables.h
@ -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 */
|
||||
|
||||
|
103
twoprocess.c
103
twoprocess.c
@ -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);
|
||||
}
|
||||
|
@ -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";
|
||||
|
3
vsftpd.8
3
vsftpd.8
@ -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
|
||||
|
14
vsftpd.conf
14
vsftpd.conf
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user