mirror of
https://github.com/InfrastructureServices/vsftpd.git
synced 2025-04-19 01:24:02 +03:00
Updated to v3.0.0
This commit is contained in:
parent
6ab49f7a58
commit
8ab4684483
4
BUGS
4
BUGS
@ -13,10 +13,6 @@ complies here, almost all other FTPd's don't
|
||||
directory name itself
|
||||
- In ASCII mode, the SIZE command needs to take into account the size of
|
||||
the file _after_ ASCII linefeed mangling?
|
||||
- ASCII mode uploads: we're only supposed to remove \r if they preceed \n,
|
||||
but I rip them out unconditionally.
|
||||
- Security model: vsf_privop_get_ftp_port_sock() should probably do the
|
||||
connect() to the remote location itself.
|
||||
- If someone has one of the timeouts (command, data) setup, but not the other,
|
||||
then timeout will behave whackily.
|
||||
|
||||
|
49
Changelog
49
Changelog
@ -1274,3 +1274,52 @@ on chroot_local_user but such is life.
|
||||
|
||||
At this point: v2.3.5 released!
|
||||
===============================
|
||||
|
||||
- Update vsf_findlibs.sh to work on Ubuntu 11.10+
|
||||
- Make listen mode the default.
|
||||
- Add -Werror to build flags.
|
||||
- Fix missing "const" in ssl.c
|
||||
- Add seccompsandbox.c to support a seccomp filter sandbox; works against Ubuntu
|
||||
12.04 ABI.
|
||||
- Rearrange ftppolicy.c a bit so the syscall list is easily comparable with
|
||||
seccompsandbox.c
|
||||
- Rename deprecated "sandbox" to "ptrace_sandbox".
|
||||
- Add a few more state checks to the privileged helper processes.
|
||||
- Add tunable "seccomp_sandbox", default on.
|
||||
- Use hardened build flags. Distros of course override these and provide their
|
||||
own build flags but no harm in showing how it could be done.
|
||||
- Retry creating a PASV socket upon port reuse race between bind() and listen(),
|
||||
patch from Ralph Wuerthner <ralph.wuerthner@de.ibm.com>.
|
||||
- Don't die() if recv() indicates a closed remote connection. Problem report
|
||||
on a Windows client from Herbert van den Bergh,
|
||||
<herbert.van.den.bergh@oracle.com>.
|
||||
- Add new config setting "allow_writeable_chroot" to help people in a bit of
|
||||
a spot with the v2.3.5 defensive change. Only applies to non-anonymous.
|
||||
- Remove a couple of fixed things from BUGS.
|
||||
- strlen() trunction fix -- no particular impact.
|
||||
- Apply some tidyups from mmoufid@yorku.ca.
|
||||
(vsftpd-3.0.0-pre1)
|
||||
- Fix delete_failed_uploads if there is a timeout. Report from Alejandro
|
||||
Hernández Hdez <aalejandrohdez@gmail.com>.
|
||||
- Fix other data channel bugs such as failure to log failure upon timeout.
|
||||
- Use exit codes a bit more consistently.
|
||||
- Fix bad interaction between SSL and trans_chunk_size.
|
||||
- Redo data timeout to fire properly for SSL sessions.
|
||||
- Redo idle timeout to fire properly for SSL sessions.
|
||||
- Make sure PROT_EXEC isn't allowed, thanks to Will Drewry for noticing.
|
||||
- Use 10 minutes as a max linger time just in case an alarm gets lost.
|
||||
(vsftpd-3.0.0-pre2)
|
||||
- Change PR_SET_NO_NEW_PRIVS define, from Kees Cook.
|
||||
- Add AES128-SHA to default SSL cipher suites for FileZilla compatibility.
|
||||
Unfortunately the default vsftpd SSL confiuration still doesn't fully work
|
||||
with FileZilla, because FileZilla has a data connection security problem:
|
||||
no client certificate presentation and no session reuse. At least the error
|
||||
message is now very clear.
|
||||
- Add restart_syscall to seccomp policy. Triggers reliably if you strace whilst
|
||||
a data transfer is in progress.
|
||||
- Fix delete_failed_uploads for anonymous sessions.
|
||||
- Don't listen for urgent data if the control connection is SSL, due to possible
|
||||
protocol synchronization issues.
|
||||
|
||||
At this point: v3.0.0 released!
|
||||
===============================
|
||||
|
11
Makefile
11
Makefile
@ -3,10 +3,14 @@ CC = gcc
|
||||
INSTALL = install
|
||||
IFLAGS = -idirafter dummyinc
|
||||
#CFLAGS = -g
|
||||
CFLAGS = -O2 -Wall -W -Wshadow #-pedantic -Werror -Wconversion
|
||||
CFLAGS = -O2 -fPIE -fstack-protector --param=ssp-buffer-size=4 \
|
||||
-Wall -W -Wshadow -Werror -Wformat-security \
|
||||
-D_FORTIFY_SOURCE=2 \
|
||||
#-pedantic -Wconversion
|
||||
|
||||
LIBS = `./vsf_findlibs.sh`
|
||||
LINK = -Wl,-s
|
||||
LDFLAGS = -fPIE -pie -Wl,-z,relro -Wl,-z,now
|
||||
|
||||
OBJS = main.o utility.o prelogin.o ftpcmdio.o postlogin.o privsock.o \
|
||||
tunables.o ftpdataio.o secbuf.o ls.o \
|
||||
@ -14,14 +18,15 @@ 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 sslslave.o ptracesandbox.o ftppolicy.o sysutil.o sysdeputil.o
|
||||
ssl.o sslslave.o ptracesandbox.o ftppolicy.o sysutil.o sysdeputil.o \
|
||||
seccompsandbox.o
|
||||
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $*.c $(CFLAGS) $(IFLAGS)
|
||||
|
||||
vsftpd: $(OBJS)
|
||||
$(CC) -o vsftpd $(OBJS) $(LINK) $(LIBS) $(LDFLAGS)
|
||||
$(CC) -o vsftpd $(OBJS) $(LINK) $(LDFLAGS) $(LIBS)
|
||||
|
||||
install:
|
||||
if [ -x /usr/local/sbin ]; then \
|
||||
|
2
README
2
README
@ -1,4 +1,4 @@
|
||||
This is vsftpd, version 2.3.5
|
||||
This is vsftpd, version 3.0.0
|
||||
Author: Chris Evans
|
||||
Contact: scarybeasts@gmail.com
|
||||
Website: http://vsftpd.beasts.org/
|
||||
|
5
TODO
5
TODO
@ -1,9 +1,12 @@
|
||||
CRITICAL
|
||||
========
|
||||
|
||||
|
||||
NOT SO CRITICAL
|
||||
===============
|
||||
|
||||
- ABOR handling broken for SSL connections (does any client actually use it?)
|
||||
- Add -Wconversion
|
||||
- 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
|
||||
@ -25,8 +28,6 @@ can result in a non-zero exit code.
|
||||
- add example global bandwidth limiting.
|
||||
- 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
|
||||
==================
|
||||
|
69
ftpcmdio.c
69
ftpcmdio.c
@ -21,12 +21,11 @@
|
||||
#include "readwrite.h"
|
||||
|
||||
/* Internal functions */
|
||||
static void control_getline(struct mystr* p_str, struct vsf_session* p_sess);
|
||||
static int control_getline(struct mystr* p_str, struct vsf_session* p_sess);
|
||||
static void ftp_write_text_common(struct vsf_session* p_sess, int status,
|
||||
const char* p_text, int noblock, char sep);
|
||||
const char* p_text, char sep);
|
||||
static void ftp_write_str_common(struct vsf_session* p_sess, int status,
|
||||
char sep, const struct mystr* p_str,
|
||||
int noblock);
|
||||
char sep, const struct mystr* p_str);
|
||||
static void handle_alarm_timeout(void* p_private);
|
||||
|
||||
void
|
||||
@ -41,20 +40,22 @@ static void
|
||||
handle_alarm_timeout(void* p_private)
|
||||
{
|
||||
struct vsf_session* p_sess = (struct vsf_session*) p_private;
|
||||
vsf_cmdio_write_exit(p_sess, FTP_IDLE_TIMEOUT, "Timeout.");
|
||||
p_sess->idle_timeout = 1;
|
||||
vsf_sysutil_activate_noblock(VSFTP_COMMAND_FD);
|
||||
vsf_sysutil_shutdown_read_failok(VSFTP_COMMAND_FD);
|
||||
}
|
||||
|
||||
void
|
||||
vsf_cmdio_write(struct vsf_session* p_sess, int status, const char* p_text)
|
||||
{
|
||||
ftp_write_text_common(p_sess, status, p_text, 0, ' ');
|
||||
ftp_write_text_common(p_sess, status, p_text, ' ');
|
||||
}
|
||||
|
||||
void
|
||||
vsf_cmdio_write_hyphen(struct vsf_session* p_sess, int status,
|
||||
const char* p_text)
|
||||
{
|
||||
ftp_write_text_common(p_sess, status, p_text, 0, '-');
|
||||
ftp_write_text_common(p_sess, status, p_text, '-');
|
||||
}
|
||||
|
||||
void
|
||||
@ -75,45 +76,47 @@ vsf_cmdio_write_raw(struct vsf_session* p_sess, const char* p_text)
|
||||
}
|
||||
|
||||
void
|
||||
vsf_cmdio_write_exit(struct vsf_session* p_sess, int status, const char* p_text)
|
||||
vsf_cmdio_write_exit(struct vsf_session* p_sess, int status, const char* p_text,
|
||||
int exit_val)
|
||||
{
|
||||
/* Unblock any readers on the dying control channel. This is needed for SSL
|
||||
* connections, where the SSL control channel slave is in a separate
|
||||
* process.
|
||||
*/
|
||||
vsf_sysutil_activate_noblock(VSFTP_COMMAND_FD);
|
||||
vsf_sysutil_shutdown_read_failok(VSFTP_COMMAND_FD);
|
||||
ftp_write_text_common(p_sess, status, p_text, 1, ' ');
|
||||
vsf_cmdio_write(p_sess, status, p_text);
|
||||
vsf_sysutil_shutdown_failok(VSFTP_COMMAND_FD);
|
||||
vsf_sysutil_exit(0);
|
||||
vsf_sysutil_exit(exit_val);
|
||||
}
|
||||
|
||||
static void
|
||||
ftp_write_text_common(struct vsf_session* p_sess, int status,
|
||||
const char* p_text, int noblock, char sep)
|
||||
const char* p_text, char sep)
|
||||
{
|
||||
/* XXX - could optimize */
|
||||
static struct mystr s_the_str;
|
||||
str_alloc_text(&s_the_str, p_text);
|
||||
ftp_write_str_common(p_sess, status, sep, &s_the_str, noblock);
|
||||
ftp_write_str_common(p_sess, status, sep, &s_the_str);
|
||||
}
|
||||
|
||||
void
|
||||
vsf_cmdio_write_str_hyphen(struct vsf_session* p_sess, int status,
|
||||
const struct mystr* p_str)
|
||||
{
|
||||
ftp_write_str_common(p_sess, status, '-', p_str, 0);
|
||||
ftp_write_str_common(p_sess, status, '-', p_str);
|
||||
}
|
||||
|
||||
void
|
||||
vsf_cmdio_write_str(struct vsf_session* p_sess, int status,
|
||||
const struct mystr* p_str)
|
||||
{
|
||||
ftp_write_str_common(p_sess, status, ' ', p_str, 0);
|
||||
ftp_write_str_common(p_sess, status, ' ', p_str);
|
||||
}
|
||||
|
||||
static void
|
||||
ftp_write_str_common(struct vsf_session* p_sess, int status, char sep,
|
||||
const struct mystr* p_str, int noblock)
|
||||
const struct mystr* p_str)
|
||||
{
|
||||
static struct mystr s_write_buf_str;
|
||||
static struct mystr s_text_mangle_str;
|
||||
@ -136,19 +139,11 @@ ftp_write_str_common(struct vsf_session* p_sess, int status, char sep,
|
||||
str_append_char(&s_write_buf_str, sep);
|
||||
str_append_str(&s_write_buf_str, &s_text_mangle_str);
|
||||
str_append_text(&s_write_buf_str, "\r\n");
|
||||
if (noblock)
|
||||
{
|
||||
vsf_sysutil_activate_noblock(VSFTP_COMMAND_FD);
|
||||
}
|
||||
retval = ftp_write_str(p_sess, &s_write_buf_str, kVSFRWControl);
|
||||
if (retval != 0 && !noblock)
|
||||
if (retval != 0)
|
||||
{
|
||||
die("ftp_write");
|
||||
}
|
||||
if (noblock)
|
||||
{
|
||||
vsf_sysutil_deactivate_noblock(VSFTP_COMMAND_FD);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -168,13 +163,26 @@ void
|
||||
vsf_cmdio_get_cmd_and_arg(struct vsf_session* p_sess, struct mystr* p_cmd_str,
|
||||
struct mystr* p_arg_str, int set_alarm)
|
||||
{
|
||||
int ret;
|
||||
/* Prepare an alarm to timeout the session.. */
|
||||
if (set_alarm)
|
||||
{
|
||||
vsf_cmdio_set_alarm(p_sess);
|
||||
}
|
||||
/* Blocks */
|
||||
control_getline(p_cmd_str, p_sess);
|
||||
ret = control_getline(p_cmd_str, p_sess);
|
||||
if (p_sess->idle_timeout)
|
||||
{
|
||||
vsf_cmdio_write_exit(p_sess, FTP_IDLE_TIMEOUT, "Timeout.", 1);
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
/* Remote end hung up without a polite QUIT. The shutdown is to make
|
||||
* sure buggy clients don't ever see an OOPS message.
|
||||
*/
|
||||
vsf_sysutil_shutdown_failok(VSFTP_COMMAND_FD);
|
||||
vsf_sysutil_exit(1);
|
||||
}
|
||||
/* View a single space as a command of " ", which although a useless command,
|
||||
* permits the caller to distinguish input of "" from " ".
|
||||
*/
|
||||
@ -207,7 +215,7 @@ vsf_cmdio_get_cmd_and_arg(struct vsf_session* p_sess, struct mystr* p_cmd_str,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
control_getline(struct mystr* p_str, struct vsf_session* p_sess)
|
||||
{
|
||||
int ret;
|
||||
@ -216,9 +224,13 @@ control_getline(struct mystr* p_str, struct vsf_session* p_sess)
|
||||
vsf_secbuf_alloc(&p_sess->p_control_line_buf, VSFTP_MAX_COMMAND_LINE);
|
||||
}
|
||||
ret = ftp_getline(p_sess, p_str, p_sess->p_control_line_buf);
|
||||
if (ret < 0)
|
||||
if (ret == 0)
|
||||
{
|
||||
vsf_cmdio_write_exit(p_sess, FTP_BADCMD, "Input line too long.");
|
||||
return ret;
|
||||
}
|
||||
else if (ret < 0)
|
||||
{
|
||||
vsf_cmdio_write_exit(p_sess, FTP_BADCMD, "Input line too long.", 1);
|
||||
}
|
||||
/* As mandated by the FTP specifications.. */
|
||||
str_replace_char(p_str, '\0', '\n');
|
||||
@ -231,5 +243,6 @@ control_getline(struct mystr* p_str, struct vsf_session* p_sess)
|
||||
--len;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ void vsf_cmdio_write_raw(struct vsf_session* p_sess, const char* p_text);
|
||||
* write is _guaranteed_ to not block (ditching output if neccessary).
|
||||
*/
|
||||
void vsf_cmdio_write_exit(struct vsf_session* p_sess, int status,
|
||||
const char* p_text);
|
||||
const char* p_text, int exit_val);
|
||||
|
||||
/* vsf_cmdio_write_str()
|
||||
* PURPOSE
|
||||
|
35
ftpdataio.c
35
ftpdataio.c
@ -61,12 +61,11 @@ vsf_ftpdataio_dispose_transfer_fd(struct vsf_session* p_sess)
|
||||
{
|
||||
bug("no data descriptor in vsf_ftpdataio_dispose_transfer_fd");
|
||||
}
|
||||
/* 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->data_use_ssl && p_sess->ssl_slave_active)
|
||||
{
|
||||
char result;
|
||||
start_data_alarm(p_sess);
|
||||
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)
|
||||
@ -76,8 +75,17 @@ vsf_ftpdataio_dispose_transfer_fd(struct vsf_session* p_sess)
|
||||
}
|
||||
else if (p_sess->p_data_ssl)
|
||||
{
|
||||
start_data_alarm(p_sess);
|
||||
dispose_ret = ssl_data_close(p_sess);
|
||||
}
|
||||
if (!p_sess->abor_received && !p_sess->data_timeout && dispose_ret == 1)
|
||||
{
|
||||
/* If we didn't get a failure, linger on the close() in order to get more
|
||||
* accurate transfer times.
|
||||
*/
|
||||
start_data_alarm(p_sess);
|
||||
vsf_sysutil_activate_linger(p_sess->data_fd);
|
||||
}
|
||||
/* This close() blocks because we set SO_LINGER */
|
||||
retval = vsf_sysutil_close_failok(p_sess->data_fd);
|
||||
if (vsf_sysutil_retval_is_error(retval))
|
||||
@ -86,11 +94,15 @@ 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);
|
||||
}
|
||||
p_sess->data_fd = -1;
|
||||
if (tunable_data_connection_timeout > 0)
|
||||
{
|
||||
vsf_sysutil_clear_alarm();
|
||||
}
|
||||
p_sess->data_fd = -1;
|
||||
if (p_sess->abor_received || p_sess->data_timeout)
|
||||
{
|
||||
dispose_ret = 0;
|
||||
}
|
||||
return dispose_ret;
|
||||
}
|
||||
|
||||
@ -188,11 +200,16 @@ handle_sigalrm(void* p_private)
|
||||
struct vsf_session* p_sess = (struct vsf_session*) p_private;
|
||||
if (!p_sess->data_progress)
|
||||
{
|
||||
vsf_cmdio_write_exit(p_sess, FTP_DATA_TIMEOUT,
|
||||
"Data timeout. Reconnect. Sorry.");
|
||||
p_sess->data_timeout = 1;
|
||||
vsf_sysutil_shutdown_failok(p_sess->data_fd);
|
||||
vsf_sysutil_shutdown_read_failok(VSFTP_COMMAND_FD);
|
||||
vsf_sysutil_activate_noblock(VSFTP_COMMAND_FD);
|
||||
}
|
||||
else
|
||||
{
|
||||
p_sess->data_progress = 0;
|
||||
start_data_alarm(p_sess);
|
||||
}
|
||||
p_sess->data_progress = 0;
|
||||
start_data_alarm(p_sess);
|
||||
}
|
||||
|
||||
void
|
||||
@ -224,10 +241,6 @@ init_data_sock_params(struct vsf_session* p_sess, int sock_fd)
|
||||
vsf_sysutil_activate_keepalive(sock_fd);
|
||||
/* And in the vague hope it might help... */
|
||||
vsf_sysutil_set_iptos_throughput(sock_fd);
|
||||
/* Set up lingering, so that we wait for all data to transfer, and report
|
||||
* more accurate transfer rates.
|
||||
*/
|
||||
vsf_sysutil_activate_linger(sock_fd);
|
||||
/* Start the timeout monitor */
|
||||
vsf_sysutil_install_io_handler(handle_io, p_sess);
|
||||
start_data_alarm(p_sess);
|
||||
|
60
ftppolicy.c
60
ftppolicy.c
@ -41,41 +41,52 @@ policy_setup(struct pt_sandbox* p_sandbox, const struct vsf_session* p_sess)
|
||||
*/
|
||||
ptrace_sandbox_permit_read(p_sandbox);
|
||||
ptrace_sandbox_permit_write(p_sandbox);
|
||||
/* Reading FTP commands from the network. */
|
||||
ptrace_sandbox_permit_recv(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);
|
||||
|
||||
/* Typically post-login things follow. */
|
||||
/* 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. */
|
||||
/* Other pathname-based metadata queries. */
|
||||
ptrace_sandbox_permit_file_stats(p_sandbox);
|
||||
ptrace_sandbox_permit_readlink(p_sandbox);
|
||||
/* File locking. */
|
||||
ptrace_sandbox_permit_fcntl(p_sandbox);
|
||||
/* Seeking for REST. */
|
||||
/* Querying, reading and changing directory. */
|
||||
ptrace_sandbox_permit_getcwd(p_sandbox);
|
||||
ptrace_sandbox_permit_chdir(p_sandbox);
|
||||
ptrace_sandbox_permit_getdents(p_sandbox);
|
||||
/* Simple fd-based operations. */
|
||||
ptrace_sandbox_permit_fd_stats(p_sandbox);
|
||||
ptrace_sandbox_permit_seek(p_sandbox);
|
||||
ptrace_sandbox_permit_shutdown(p_sandbox);
|
||||
ptrace_sandbox_permit_fcntl(p_sandbox);
|
||||
ptrace_sandbox_permit_setsockopt(p_sandbox);
|
||||
ptrace_sandbox_set_setsockopt_validator(p_sandbox, setsockopt_validator, 0);
|
||||
/* Misc */
|
||||
/* Setting umask. */
|
||||
ptrace_sandbox_permit_umask(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);
|
||||
/* High-speed transfers... */
|
||||
ptrace_sandbox_permit_sendfile(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);
|
||||
}
|
||||
/* May need ability to install signal handlers. */
|
||||
if (tunable_async_abor_enable ||
|
||||
tunable_idle_session_timeout > 0 ||
|
||||
@ -88,17 +99,7 @@ policy_setup(struct pt_sandbox* p_sandbox, const struct vsf_session* p_sess)
|
||||
{
|
||||
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,
|
||||
@ -108,9 +109,6 @@ policy_setup(struct pt_sandbox* p_sandbox, const struct vsf_session* p_sess)
|
||||
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);
|
||||
|
2
main.c
2
main.c
@ -38,7 +38,7 @@ main(int argc, const char* argv[])
|
||||
struct vsf_session the_session =
|
||||
{
|
||||
/* Control connection */
|
||||
0, 0, 0,
|
||||
0, 0, 0, 0, 0,
|
||||
/* Data connection */
|
||||
-1, 0, -1, 0, 0, 0, 0,
|
||||
/* Login */
|
||||
|
11
netstr.c
11
netstr.c
@ -29,6 +29,7 @@ str_netfd_alloc(struct vsf_session* p_sess,
|
||||
unsigned int i;
|
||||
char* p_readpos = p_readbuf;
|
||||
unsigned int left = maxlen;
|
||||
str_empty(p_str);
|
||||
while (1)
|
||||
{
|
||||
if (p_readpos + left != p_readbuf + maxlen)
|
||||
@ -38,7 +39,6 @@ str_netfd_alloc(struct vsf_session* p_sess,
|
||||
/* Did we hit the max? */
|
||||
if (left == 0)
|
||||
{
|
||||
str_empty(p_str);
|
||||
return -1;
|
||||
}
|
||||
retval = (*p_peekfunc)(p_sess, p_readpos, left);
|
||||
@ -48,7 +48,7 @@ str_netfd_alloc(struct vsf_session* p_sess,
|
||||
}
|
||||
else if (retval == 0)
|
||||
{
|
||||
die("vsf_sysutil_recv_peek: no data");
|
||||
return 0;
|
||||
}
|
||||
bytes_read = (unsigned int) retval;
|
||||
/* Search for the terminator */
|
||||
@ -57,13 +57,14 @@ str_netfd_alloc(struct vsf_session* p_sess,
|
||||
if (p_readpos[i] == term)
|
||||
{
|
||||
/* Got it! */
|
||||
retval = (*p_readfunc)(p_sess, p_readpos, i + 1);
|
||||
i++;
|
||||
retval = (*p_readfunc)(p_sess, p_readpos, i);
|
||||
if (vsf_sysutil_retval_is_error(retval) ||
|
||||
(unsigned int) retval != i + 1)
|
||||
(unsigned int) retval != i)
|
||||
{
|
||||
die("vsf_sysutil_read_loop");
|
||||
}
|
||||
if (p_readpos[i] != term)
|
||||
if (p_readpos[i - 1] != term)
|
||||
{
|
||||
die("missing terminator in str_netfd_alloc");
|
||||
}
|
||||
|
3
netstr.h
3
netstr.h
@ -28,7 +28,8 @@ typedef int (*str_netfd_read_t)(struct vsf_session*
|
||||
* p_readfunc - a function called to read data from the network
|
||||
* RETURNS
|
||||
* -1 upon reaching max buffer length without seeing terminator, or the number
|
||||
* of bytes read, _excluding_ the terminator.
|
||||
* of bytes read, _including_ the terminator. 0 for an EOF on the socket.
|
||||
* Does not return (exits) for a serious socket error.
|
||||
*/
|
||||
int str_netfd_alloc(struct vsf_session* p_sess,
|
||||
struct mystr* p_str,
|
||||
|
@ -25,13 +25,14 @@
|
||||
#include "sysutil.h"
|
||||
#include "ptracesandbox.h"
|
||||
#include "ftppolicy.h"
|
||||
#include "seccompsandbox.h"
|
||||
|
||||
static void one_process_start(void* p_arg);
|
||||
|
||||
void
|
||||
vsf_one_process_start(struct vsf_session* p_sess)
|
||||
{
|
||||
if (tunable_sandbox)
|
||||
if (tunable_ptrace_sandbox)
|
||||
{
|
||||
struct pt_sandbox* p_sandbox = ptrace_sandbox_alloc();
|
||||
if (p_sandbox == 0)
|
||||
@ -97,10 +98,13 @@ one_process_start(void* p_arg)
|
||||
str_free(&user_name);
|
||||
str_free(&chdir_str);
|
||||
}
|
||||
if (tunable_sandbox)
|
||||
if (tunable_ptrace_sandbox)
|
||||
{
|
||||
ptrace_sandbox_attach_point();
|
||||
}
|
||||
seccomp_sandbox_init();
|
||||
seccomp_sandbox_setup_postlogin(p_sess);
|
||||
seccomp_sandbox_lockdown();
|
||||
init_connection(p_sess);
|
||||
}
|
||||
|
||||
|
@ -99,12 +99,14 @@ parseconf_bool_array[] =
|
||||
{ "ssl_request_cert", &tunable_ssl_request_cert },
|
||||
{ "delete_failed_uploads", &tunable_delete_failed_uploads },
|
||||
{ "implicit_ssl", &tunable_implicit_ssl },
|
||||
{ "sandbox", &tunable_sandbox },
|
||||
{ "ptrace_sandbox", &tunable_ptrace_sandbox },
|
||||
{ "require_ssl_reuse", &tunable_require_ssl_reuse },
|
||||
{ "isolate", &tunable_isolate },
|
||||
{ "isolate_network", &tunable_isolate_network },
|
||||
{ "ftp_enable", &tunable_ftp_enable },
|
||||
{ "http_enable", &tunable_http_enable },
|
||||
{ "seccomp_sandbox", &tunable_seccomp_sandbox },
|
||||
{ "allow_writeable_chroot", &tunable_allow_writeable_chroot },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
41
postlogin.c
41
postlogin.c
@ -100,7 +100,11 @@ process_post_login(struct vsf_session* p_sess)
|
||||
bug("should not be reached");
|
||||
}
|
||||
|
||||
if (tunable_async_abor_enable)
|
||||
/* Don't support async ABOR if we have an SSL channel. The spec says SHOULD
|
||||
* NOT, and I think there are synchronization issues between command and
|
||||
* data reads.
|
||||
*/
|
||||
if (tunable_async_abor_enable && !p_sess->control_use_ssl)
|
||||
{
|
||||
vsf_sysutil_install_sighandler(kVSFSysUtilSigURG, handle_sigurg, p_sess, 0);
|
||||
vsf_sysutil_activate_sigurg(VSFTP_COMMAND_FD);
|
||||
@ -180,7 +184,7 @@ process_post_login(struct vsf_session* p_sess)
|
||||
}
|
||||
else if (str_equal_text(&p_sess->ftp_cmd_str, "QUIT"))
|
||||
{
|
||||
vsf_cmdio_write_exit(p_sess, FTP_GOODBYE, "Goodbye.");
|
||||
vsf_cmdio_write_exit(p_sess, FTP_GOODBYE, "Goodbye.", 0);
|
||||
}
|
||||
else if (str_equal_text(&p_sess->ftp_cmd_str, "PWD") ||
|
||||
str_equal_text(&p_sess->ftp_cmd_str, "XPWD"))
|
||||
@ -443,6 +447,11 @@ process_post_login(struct vsf_session* p_sess)
|
||||
{
|
||||
vsf_log_do_log(p_sess, 0);
|
||||
}
|
||||
if (p_sess->data_timeout)
|
||||
{
|
||||
vsf_cmdio_write_exit(p_sess, FTP_DATA_TIMEOUT,
|
||||
"Data timeout. Reconnect. Sorry.", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -755,7 +764,11 @@ handle_retr(struct vsf_session* p_sess, int is_http)
|
||||
}
|
||||
else if (trans_ret.retval == -2)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDNET, "Failure writing network stream.");
|
||||
if (!p_sess->data_timeout)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDNET,
|
||||
"Failure writing network stream.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -893,18 +906,22 @@ handle_dir_common(struct vsf_session* p_sess, int full_details, int stat_cmd)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_STATFILE_OK, "End of status");
|
||||
}
|
||||
else if (retval != 0)
|
||||
{
|
||||
if (!p_sess->data_timeout)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDNET,
|
||||
"Failure writing network stream.");
|
||||
}
|
||||
}
|
||||
else if (p_dir == 0 || !dir_allow_read)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_TRANSFEROK,
|
||||
"Transfer done (but failed to open directory).");
|
||||
}
|
||||
else if (retval == 0)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Directory send OK.");
|
||||
}
|
||||
else
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDNET, "Failure writing network stream.");
|
||||
vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Directory send OK.");
|
||||
}
|
||||
check_abor(p_sess);
|
||||
dir_close_out:
|
||||
@ -1124,9 +1141,13 @@ 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 || p_sess->abor_received)
|
||||
else if (trans_ret.retval == -2)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDNET, "Failure reading network stream.");
|
||||
if (!p_sess->data_timeout)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDNET,
|
||||
"Failure reading network stream.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "secutil.h"
|
||||
#include "sysstr.h"
|
||||
#include "sysdeputil.h"
|
||||
#include "seccompsandbox.h"
|
||||
|
||||
static void minimize_privilege(struct vsf_session* p_sess);
|
||||
static void process_post_login_req(struct vsf_session* p_sess);
|
||||
@ -110,6 +111,9 @@ minimize_privilege(struct vsf_session* p_sess)
|
||||
str_free(&user_str);
|
||||
str_free(&dir_str);
|
||||
}
|
||||
seccomp_sandbox_init();
|
||||
seccomp_sandbox_setup_postlogin_broker();
|
||||
seccomp_sandbox_lockdown();
|
||||
}
|
||||
|
||||
static void
|
||||
|
21
prelogin.c
21
prelogin.c
@ -73,7 +73,7 @@ check_limits(struct vsf_session* p_sess)
|
||||
str_alloc_text(&str_log_line, "Connection refused: too many sessions.");
|
||||
vsf_log_line(p_sess, kVSFLogEntryConnection, &str_log_line);
|
||||
vsf_cmdio_write_exit(p_sess, FTP_TOO_MANY_USERS,
|
||||
"There are too many connected users, please try later.");
|
||||
"There are too many connected users, please try later.", 1);
|
||||
}
|
||||
if (tunable_max_per_ip > 0 &&
|
||||
p_sess->num_this_ip > tunable_max_per_ip)
|
||||
@ -82,14 +82,14 @@ check_limits(struct vsf_session* p_sess)
|
||||
"Connection refused: too many sessions for this address.");
|
||||
vsf_log_line(p_sess, kVSFLogEntryConnection, &str_log_line);
|
||||
vsf_cmdio_write_exit(p_sess, FTP_IP_LIMIT,
|
||||
"There are too many connections from your internet address.");
|
||||
"There are too many connections from your internet address.", 1);
|
||||
}
|
||||
if (!p_sess->tcp_wrapper_ok)
|
||||
{
|
||||
str_alloc_text(&str_log_line,
|
||||
"Connection refused: tcp_wrappers denial.");
|
||||
vsf_log_line(p_sess, kVSFLogEntryConnection, &str_log_line);
|
||||
vsf_cmdio_write_exit(p_sess, FTP_IP_DENY, "Service not available.");
|
||||
vsf_cmdio_write_exit(p_sess, FTP_IP_DENY, "Service not available.", 1);
|
||||
}
|
||||
vsf_log_line(p_sess, kVSFLogEntryConnection, &str_log_line);
|
||||
}
|
||||
@ -133,7 +133,7 @@ parse_username_password(struct vsf_session* p_sess)
|
||||
}
|
||||
else if (str_equal_text(&p_sess->ftp_cmd_str, "QUIT"))
|
||||
{
|
||||
vsf_cmdio_write_exit(p_sess, FTP_GOODBYE, "Goodbye.");
|
||||
vsf_cmdio_write_exit(p_sess, FTP_GOODBYE, "Goodbye.", 0);
|
||||
}
|
||||
else if (str_equal_text(&p_sess->ftp_cmd_str, "FEAT"))
|
||||
{
|
||||
@ -228,16 +228,14 @@ handle_user_command(struct vsf_session* p_sess)
|
||||
if (tunable_ssl_enable && !is_anon && !p_sess->control_use_ssl &&
|
||||
tunable_force_local_logins_ssl)
|
||||
{
|
||||
vsf_cmdio_write(
|
||||
p_sess, FTP_LOGINERR, "Non-anonymous sessions must use encryption.");
|
||||
vsf_sysutil_exit(0);
|
||||
vsf_cmdio_write_exit(
|
||||
p_sess, FTP_LOGINERR, "Non-anonymous sessions must use encryption.", 1);
|
||||
}
|
||||
if (tunable_ssl_enable && is_anon && !p_sess->control_use_ssl &&
|
||||
tunable_force_anon_logins_ssl)
|
||||
{
|
||||
vsf_cmdio_write(
|
||||
p_sess, FTP_LOGINERR, "Anonymous sessions must use encryption.");
|
||||
vsf_sysutil_exit(0);
|
||||
vsf_cmdio_write_exit(
|
||||
p_sess, FTP_LOGINERR, "Anonymous sessions must use encryption.", 1);
|
||||
}
|
||||
if (tunable_userlist_enable)
|
||||
{
|
||||
@ -299,6 +297,7 @@ static void check_login_fails(struct vsf_session* p_sess)
|
||||
{
|
||||
if (++p_sess->login_fails >= tunable_max_login_fails)
|
||||
{
|
||||
vsf_sysutil_exit(0);
|
||||
vsf_sysutil_shutdown_failok(VSFTP_COMMAND_FD);
|
||||
vsf_sysutil_exit(1);
|
||||
}
|
||||
}
|
||||
|
30
privops.c
30
privops.c
@ -42,6 +42,10 @@ vsf_privop_get_ftp_port_sock(struct vsf_session* p_sess,
|
||||
int i;
|
||||
int s = vsf_sysutil_get_ipsock(p_sess->p_local_addr);
|
||||
int port = 0;
|
||||
if (p_sess->pasv_listen_fd != -1)
|
||||
{
|
||||
die("listed fd is active?");
|
||||
}
|
||||
if (vsf_sysutil_is_port_reserved(remote_port))
|
||||
{
|
||||
die("Illegal port request");
|
||||
@ -117,20 +121,15 @@ vsf_privop_pasv_listen(struct vsf_session* p_sess)
|
||||
{
|
||||
static struct vsf_sysutil_sockaddr* s_p_sockaddr;
|
||||
int bind_retries = 10;
|
||||
unsigned short the_port = 0;
|
||||
unsigned short the_port;
|
||||
/* IPPORT_RESERVED */
|
||||
unsigned short min_port = 1024;
|
||||
unsigned short max_port = 65535;
|
||||
int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr);
|
||||
if (is_ipv6)
|
||||
if (p_sess->pasv_listen_fd != -1)
|
||||
{
|
||||
p_sess->pasv_listen_fd = vsf_sysutil_get_ipv6_sock();
|
||||
die("listed fd already active");
|
||||
}
|
||||
else
|
||||
{
|
||||
p_sess->pasv_listen_fd = vsf_sysutil_get_ipv4_sock();
|
||||
}
|
||||
vsf_sysutil_activate_reuseaddr(p_sess->pasv_listen_fd);
|
||||
|
||||
if (tunable_pasv_min_port > min_port && tunable_pasv_min_port <= max_port)
|
||||
{
|
||||
@ -152,6 +151,15 @@ vsf_privop_pasv_listen(struct vsf_session* p_sess)
|
||||
scaled_port += ((double) the_port / (double) 65536) *
|
||||
((double) max_port - min_port + 1);
|
||||
the_port = (unsigned short) scaled_port;
|
||||
if (is_ipv6)
|
||||
{
|
||||
p_sess->pasv_listen_fd = vsf_sysutil_get_ipv6_sock();
|
||||
}
|
||||
else
|
||||
{
|
||||
p_sess->pasv_listen_fd = vsf_sysutil_get_ipv4_sock();
|
||||
}
|
||||
vsf_sysutil_activate_reuseaddr(p_sess->pasv_listen_fd);
|
||||
vsf_sysutil_sockaddr_clone(&s_p_sockaddr, p_sess->p_local_addr);
|
||||
vsf_sysutil_sockaddr_set_port(s_p_sockaddr, the_port);
|
||||
retval = vsf_sysutil_bind(p_sess->pasv_listen_fd, s_p_sockaddr);
|
||||
@ -167,6 +175,8 @@ vsf_privop_pasv_listen(struct vsf_session* p_sess)
|
||||
if (vsf_sysutil_get_error() == kVSFSysUtilErrADDRINUSE ||
|
||||
vsf_sysutil_get_error() == kVSFSysUtilErrACCES)
|
||||
{
|
||||
vsf_sysutil_close(p_sess->pasv_listen_fd);
|
||||
p_sess->pasv_listen_fd = -1;
|
||||
continue;
|
||||
}
|
||||
die("vsf_sysutil_bind / listen");
|
||||
@ -183,6 +193,10 @@ vsf_privop_accept_pasv(struct vsf_session* p_sess)
|
||||
{
|
||||
struct vsf_sysutil_sockaddr* p_accept_addr = 0;
|
||||
int remote_fd;
|
||||
if (p_sess->pasv_listen_fd == -1)
|
||||
{
|
||||
die("listed fd not active");
|
||||
}
|
||||
vsf_sysutil_sockaddr_alloc(&p_accept_addr);
|
||||
remote_fd = vsf_sysutil_accept_timeout(p_sess->pasv_listen_fd, p_accept_addr,
|
||||
tunable_accept_timeout);
|
||||
|
@ -83,6 +83,7 @@ ftp_read_data(struct vsf_session* p_sess, char* p_buf, unsigned int len)
|
||||
{
|
||||
int ret;
|
||||
priv_sock_send_cmd(p_sess->ssl_consumer_fd, PRIV_SOCK_DO_SSL_READ);
|
||||
priv_sock_send_int(p_sess->ssl_consumer_fd, len);
|
||||
ret = priv_sock_get_int(p_sess->ssl_consumer_fd);
|
||||
priv_sock_recv_buf(p_sess->ssl_consumer_fd, p_buf, len);
|
||||
/* Need to do this here too because it is useless in the slave process. */
|
||||
|
654
seccompsandbox.c
Normal file
654
seccompsandbox.c
Normal file
@ -0,0 +1,654 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL v2
|
||||
* Author: Chris Evans
|
||||
* seccompsandbox.c
|
||||
*
|
||||
* Code to lock down the accessible kernel API in a Linux seccomp filter
|
||||
* sandbox. Works in Ubuntu 11.10 and newer.
|
||||
*/
|
||||
|
||||
#include "seccompsandbox.h"
|
||||
|
||||
#if defined(__linux__) && defined(__x86_64__)
|
||||
|
||||
#include "session.h"
|
||||
#include "sysutil.h"
|
||||
#include "tunables.h"
|
||||
#include "utility.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <linux/filter.h>
|
||||
|
||||
#include <asm/unistd.h>
|
||||
|
||||
/* #define DEBUG_SIGSYS 1 */
|
||||
|
||||
#ifndef PR_SET_NO_NEW_PRIVS
|
||||
#define PR_SET_NO_NEW_PRIVS 38
|
||||
#endif
|
||||
|
||||
#ifndef __NR_openat
|
||||
#define __NR_openat 257
|
||||
#endif
|
||||
|
||||
#ifndef O_LARGEFILE
|
||||
#define O_LARGEFILE 00100000
|
||||
#endif
|
||||
|
||||
#define kMaxSyscalls 100
|
||||
|
||||
#ifdef DEBUG_SIGSYS
|
||||
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
|
||||
void
|
||||
handle_sigsys(int sig)
|
||||
{
|
||||
(void) sig;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const int kOpenFlags =
|
||||
O_CREAT|O_EXCL|O_APPEND|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_LARGEFILE;
|
||||
|
||||
static size_t s_syscall_index;
|
||||
static size_t s_1_arg_validations;
|
||||
static size_t s_2_arg_validations;
|
||||
static size_t s_3_arg_validations;
|
||||
static int s_syscalls[kMaxSyscalls];
|
||||
static int s_args_1[kMaxSyscalls];
|
||||
static int s_vals_1[kMaxSyscalls];
|
||||
static int s_args_2[kMaxSyscalls];
|
||||
static int s_vals_2[kMaxSyscalls];
|
||||
static int s_args_3[kMaxSyscalls];
|
||||
static int s_vals_3[kMaxSyscalls];
|
||||
|
||||
static void
|
||||
allow_nr(int nr)
|
||||
{
|
||||
if (s_syscall_index >= kMaxSyscalls)
|
||||
{
|
||||
bug("out of syscall space");
|
||||
}
|
||||
if (nr < 0)
|
||||
{
|
||||
bug("negative syscall");
|
||||
}
|
||||
s_syscalls[s_syscall_index++] = nr;
|
||||
}
|
||||
|
||||
static void
|
||||
allow_nr_1_arg_match(int nr, int arg, int val)
|
||||
{
|
||||
if (s_syscall_index >= kMaxSyscalls)
|
||||
{
|
||||
bug("out of syscall space");
|
||||
}
|
||||
if (nr < 0)
|
||||
{
|
||||
bug("negative syscall");
|
||||
}
|
||||
if (arg < 1 || arg > 6)
|
||||
{
|
||||
bug("arg out of range");
|
||||
}
|
||||
s_args_1[s_syscall_index] = arg;
|
||||
s_vals_1[s_syscall_index] = val;
|
||||
s_syscalls[s_syscall_index++] = nr;
|
||||
s_1_arg_validations++;
|
||||
}
|
||||
|
||||
static void
|
||||
allow_nr_1_arg_mask(int nr, int arg, int val)
|
||||
{
|
||||
if (s_syscall_index >= kMaxSyscalls)
|
||||
{
|
||||
bug("out of syscall space");
|
||||
}
|
||||
if (nr < 0)
|
||||
{
|
||||
bug("negative syscall");
|
||||
}
|
||||
if (arg < 1 || arg > 6)
|
||||
{
|
||||
bug("arg out of range");
|
||||
}
|
||||
s_args_1[s_syscall_index] = 100 + arg;
|
||||
s_vals_1[s_syscall_index] = val;
|
||||
s_syscalls[s_syscall_index++] = nr;
|
||||
s_1_arg_validations++;
|
||||
}
|
||||
|
||||
static void
|
||||
allow_nr_2_arg_match(int nr, int arg1, int val1, int arg2, int val2)
|
||||
{
|
||||
if (s_syscall_index >= kMaxSyscalls)
|
||||
{
|
||||
bug("out of syscall space");
|
||||
}
|
||||
if (nr < 0)
|
||||
{
|
||||
bug("negative syscall");
|
||||
}
|
||||
if (arg1 < 1 || arg1 > 6)
|
||||
{
|
||||
bug("arg1 out of range");
|
||||
}
|
||||
if (arg2 < 1 || arg2 > 6)
|
||||
{
|
||||
bug("arg2 out of range");
|
||||
}
|
||||
s_args_1[s_syscall_index] = arg1;
|
||||
s_vals_1[s_syscall_index] = val1;
|
||||
s_args_2[s_syscall_index] = arg2;
|
||||
s_vals_2[s_syscall_index] = val2;
|
||||
s_syscalls[s_syscall_index++] = nr;
|
||||
s_2_arg_validations++;
|
||||
}
|
||||
|
||||
static void
|
||||
allow_nr_2_arg_mask_match(int nr, int arg1, int val1, int arg2, int val2)
|
||||
{
|
||||
if (s_syscall_index >= kMaxSyscalls)
|
||||
{
|
||||
bug("out of syscall space");
|
||||
}
|
||||
if (nr < 0)
|
||||
{
|
||||
bug("negative syscall");
|
||||
}
|
||||
if (arg1 < 1 || arg1 > 6)
|
||||
{
|
||||
bug("arg1 out of range");
|
||||
}
|
||||
if (arg2 < 1 || arg2 > 6)
|
||||
{
|
||||
bug("arg2 out of range");
|
||||
}
|
||||
s_args_1[s_syscall_index] = 100 + arg1;
|
||||
s_vals_1[s_syscall_index] = val1;
|
||||
s_args_2[s_syscall_index] = arg2;
|
||||
s_vals_2[s_syscall_index] = val2;
|
||||
s_syscalls[s_syscall_index++] = nr;
|
||||
s_2_arg_validations++;
|
||||
}
|
||||
|
||||
static void
|
||||
allow_nr_3_arg_match(int nr, int arg1, int val1, int arg2, int val2, int arg3,
|
||||
int val3)
|
||||
{
|
||||
if (s_syscall_index >= kMaxSyscalls)
|
||||
{
|
||||
bug("out of syscall space");
|
||||
}
|
||||
if (nr < 0)
|
||||
{
|
||||
bug("negative syscall");
|
||||
}
|
||||
if (arg1 < 1 || arg1 > 6)
|
||||
{
|
||||
bug("arg1 out of range");
|
||||
}
|
||||
if (arg2 < 1 || arg2 > 6)
|
||||
{
|
||||
bug("arg2 out of range");
|
||||
}
|
||||
if (arg3 < 1 || arg3 > 6)
|
||||
{
|
||||
bug("arg3 out of range");
|
||||
}
|
||||
s_args_1[s_syscall_index] = arg1;
|
||||
s_vals_1[s_syscall_index] = val1;
|
||||
s_args_2[s_syscall_index] = arg2;
|
||||
s_vals_2[s_syscall_index] = val2;
|
||||
s_args_3[s_syscall_index] = arg3;
|
||||
s_vals_3[s_syscall_index] = val3;
|
||||
s_syscalls[s_syscall_index++] = nr;
|
||||
s_3_arg_validations++;
|
||||
}
|
||||
|
||||
static void
|
||||
seccomp_sandbox_setup_data_connections()
|
||||
{
|
||||
allow_nr_3_arg_match(__NR_socket, 1, PF_INET, 2, SOCK_STREAM, 3, IPPROTO_TCP);
|
||||
allow_nr_3_arg_match(__NR_socket,
|
||||
1, PF_INET6,
|
||||
2, SOCK_STREAM,
|
||||
3, IPPROTO_TCP);
|
||||
allow_nr(__NR_bind);
|
||||
allow_nr(__NR_select);
|
||||
if (tunable_port_enable)
|
||||
{
|
||||
allow_nr(__NR_connect);
|
||||
allow_nr_2_arg_match(__NR_getsockopt, 2, SOL_SOCKET, 3, SO_ERROR);
|
||||
allow_nr_2_arg_match(__NR_setsockopt, 2, SOL_SOCKET, 3, SO_REUSEADDR);
|
||||
allow_nr_1_arg_match(__NR_fcntl, 2, F_GETFL);
|
||||
allow_nr_2_arg_match(__NR_fcntl, 2, F_SETFL, 3, O_RDWR|O_NONBLOCK);
|
||||
allow_nr_2_arg_match(__NR_fcntl, 2, F_SETFL, 3, O_RDWR);
|
||||
}
|
||||
if (tunable_pasv_enable)
|
||||
{
|
||||
allow_nr(__NR_listen);
|
||||
allow_nr(__NR_accept);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
seccomp_sandbox_setup_base()
|
||||
{
|
||||
/* Simple reads and writes on existing descriptors. */
|
||||
allow_nr(__NR_read);
|
||||
allow_nr(__NR_write);
|
||||
|
||||
/* Needed for memory management. */
|
||||
allow_nr_2_arg_match(__NR_mmap,
|
||||
3, PROT_READ|PROT_WRITE,
|
||||
4, MAP_PRIVATE|MAP_ANON);
|
||||
allow_nr_1_arg_mask(__NR_mprotect, 3, PROT_READ);
|
||||
allow_nr(__NR_brk);
|
||||
|
||||
/* Misc simple low-risk calls. */
|
||||
allow_nr(__NR_rt_sigreturn); /* Used to handle SIGPIPE. */
|
||||
allow_nr(__NR_restart_syscall);
|
||||
allow_nr(__NR_close);
|
||||
|
||||
/* Always need to be able to exit ! */
|
||||
allow_nr(__NR_exit_group);
|
||||
}
|
||||
|
||||
void
|
||||
seccomp_sandbox_init()
|
||||
{
|
||||
if (s_syscall_index != 0)
|
||||
{
|
||||
bug("bad state in seccomp_sandbox_init");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
seccomp_sandbox_setup_prelogin(const struct vsf_session* p_sess)
|
||||
{
|
||||
(void) p_sess;
|
||||
|
||||
seccomp_sandbox_setup_base();
|
||||
|
||||
/* Peeking FTP commands from the network. */
|
||||
allow_nr_1_arg_match(__NR_recvfrom, 4, MSG_PEEK);
|
||||
|
||||
/* Misc simple low-risk calls */
|
||||
allow_nr(__NR_nanosleep); /* Used for bandwidth / login throttling. */
|
||||
allow_nr(__NR_getpid); /* Used by logging. */
|
||||
allow_nr(__NR_shutdown); /* Used for QUIT or a timeout. */
|
||||
allow_nr_1_arg_match(__NR_fcntl, 2, F_GETFL);
|
||||
/* It's safe to allow O_RDWR in fcntl because these flags cannot be changed.
|
||||
* Also, sockets are O_RDWR.
|
||||
*/
|
||||
allow_nr_2_arg_mask_match(__NR_fcntl, 3, kOpenFlags|O_ACCMODE, 2, F_SETFL);
|
||||
|
||||
/* Config-dependent items follow. */
|
||||
if (tunable_idle_session_timeout > 0)
|
||||
{
|
||||
allow_nr(__NR_rt_sigaction);
|
||||
allow_nr(__NR_alarm);
|
||||
}
|
||||
if (tunable_xferlog_enable || tunable_dual_log_enable)
|
||||
{
|
||||
/* For file locking. */
|
||||
allow_nr_1_arg_match(__NR_fcntl, 2, F_SETLKW);
|
||||
allow_nr_1_arg_match(__NR_fcntl, 2, F_SETLK);
|
||||
}
|
||||
if (tunable_ssl_enable)
|
||||
{
|
||||
allow_nr_1_arg_match(__NR_recvmsg, 3, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
seccomp_sandbox_setup_postlogin(const struct vsf_session* p_sess)
|
||||
{
|
||||
int is_anon = p_sess->is_anonymous;
|
||||
int open_flag = kOpenFlags;
|
||||
if (tunable_write_enable)
|
||||
{
|
||||
open_flag |= O_ACCMODE;
|
||||
}
|
||||
|
||||
/* Put lstat() first because it is a very hot syscall for large directory
|
||||
* listings. And the current BPF only allows a linear scan of allowed
|
||||
* syscalls.
|
||||
*/
|
||||
allow_nr(__NR_lstat);
|
||||
|
||||
/* Allow all the simple pre-login things and then expand upon them. */
|
||||
seccomp_sandbox_setup_prelogin(p_sess);
|
||||
|
||||
/* Simple file descriptor-based operations. */
|
||||
if (tunable_xferlog_enable || tunable_dual_log_enable ||
|
||||
tunable_lock_upload_files)
|
||||
{
|
||||
allow_nr_1_arg_match(__NR_fcntl, 2, F_SETLKW);
|
||||
allow_nr_1_arg_match(__NR_fcntl, 2, F_SETLK);
|
||||
}
|
||||
if (tunable_async_abor_enable)
|
||||
{
|
||||
allow_nr_2_arg_match(__NR_fcntl, 2, F_SETOWN, 3, vsf_sysutil_getpid());
|
||||
}
|
||||
allow_nr_2_arg_match(__NR_setsockopt, 2, SOL_SOCKET, 3, SO_KEEPALIVE);
|
||||
allow_nr_2_arg_match(__NR_setsockopt, 2, SOL_SOCKET, 3, SO_LINGER);
|
||||
allow_nr_2_arg_match(__NR_setsockopt, 2, IPPROTO_IP, 3, IP_TOS);
|
||||
allow_nr(__NR_fstat);
|
||||
allow_nr(__NR_lseek);
|
||||
/* Since we use chroot() to restrict filesystem access, we can just blanket
|
||||
* allow open().
|
||||
*/
|
||||
allow_nr_1_arg_mask(__NR_open, 2, open_flag);
|
||||
allow_nr_1_arg_mask(__NR_openat, 3, open_flag);
|
||||
/* Other pathname-based metadata queries. */
|
||||
allow_nr(__NR_stat);
|
||||
allow_nr(__NR_readlink);
|
||||
/* Directory handling: query, change, read. */
|
||||
allow_nr(__NR_getcwd);
|
||||
allow_nr(__NR_chdir);
|
||||
allow_nr(__NR_getdents);
|
||||
/* Misc */
|
||||
allow_nr(__NR_umask);
|
||||
|
||||
/* Config-dependent items follow. */
|
||||
if (tunable_use_sendfile)
|
||||
{
|
||||
allow_nr(__NR_sendfile);
|
||||
}
|
||||
if (tunable_idle_session_timeout > 0 ||
|
||||
tunable_data_connection_timeout > 0 ||
|
||||
tunable_async_abor_enable)
|
||||
{
|
||||
allow_nr(__NR_rt_sigaction);
|
||||
}
|
||||
if (tunable_idle_session_timeout > 0 || tunable_data_connection_timeout > 0)
|
||||
{
|
||||
allow_nr(__NR_alarm);
|
||||
}
|
||||
|
||||
if (tunable_one_process_model)
|
||||
{
|
||||
seccomp_sandbox_setup_data_connections();
|
||||
if (is_anon && tunable_chown_uploads)
|
||||
{
|
||||
allow_nr(__NR_fchmod);
|
||||
allow_nr(__NR_fchown);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Need to receieve file descriptors from privileged broker. */
|
||||
allow_nr_1_arg_match(__NR_recvmsg, 3, 0);
|
||||
if ((is_anon && tunable_chown_uploads) || tunable_ssl_enable)
|
||||
{
|
||||
/* Need to send file descriptors to privileged broker. */
|
||||
allow_nr_1_arg_match(__NR_sendmsg, 3, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (tunable_text_userdb_names)
|
||||
{
|
||||
allow_nr_2_arg_match(__NR_mmap, 3, PROT_READ, 4, MAP_SHARED);
|
||||
allow_nr(__NR_munmap);
|
||||
}
|
||||
|
||||
if (tunable_write_enable)
|
||||
{
|
||||
if (!is_anon || tunable_anon_mkdir_write_enable)
|
||||
{
|
||||
allow_nr(__NR_mkdir);
|
||||
}
|
||||
if (!is_anon ||
|
||||
tunable_anon_other_write_enable ||
|
||||
tunable_delete_failed_uploads)
|
||||
{
|
||||
allow_nr(__NR_unlink);
|
||||
}
|
||||
if (!is_anon || tunable_anon_other_write_enable)
|
||||
{
|
||||
allow_nr(__NR_rmdir);
|
||||
allow_nr(__NR_rename);
|
||||
allow_nr(__NR_ftruncate);
|
||||
if (tunable_mdtm_write)
|
||||
{
|
||||
allow_nr(__NR_utime);
|
||||
allow_nr(__NR_utimes);
|
||||
}
|
||||
}
|
||||
if (!is_anon && tunable_chmod_enable)
|
||||
{
|
||||
allow_nr(__NR_chmod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
seccomp_sandbox_setup_postlogin_broker()
|
||||
{
|
||||
seccomp_sandbox_setup_base();
|
||||
seccomp_sandbox_setup_data_connections();
|
||||
allow_nr_1_arg_match(__NR_sendmsg, 3, 0);
|
||||
}
|
||||
|
||||
void
|
||||
seccomp_sandbox_lockdown()
|
||||
{
|
||||
size_t len = (s_syscall_index * 2) +
|
||||
(s_1_arg_validations * 3) +
|
||||
(s_2_arg_validations * 5) +
|
||||
(s_3_arg_validations * 7) +
|
||||
5;
|
||||
struct sock_filter filters[len];
|
||||
struct sock_filter* p_filter = filters;
|
||||
struct sock_fprog prog;
|
||||
size_t i;
|
||||
int ret;
|
||||
|
||||
prog.len = len;
|
||||
prog.filter = filters;
|
||||
/* Validate the syscall architecture. */
|
||||
p_filter->code = BPF_LD+BPF_W+BPF_ABS;
|
||||
p_filter->jt = 0;
|
||||
p_filter->jf = 0;
|
||||
/* Offset 4 for syscall architecture. */
|
||||
p_filter->k = 4;
|
||||
p_filter++;
|
||||
p_filter->code = BPF_JMP+BPF_JEQ+BPF_K;
|
||||
p_filter->jt = 1;
|
||||
p_filter->jf = 0;
|
||||
/* AUDIT_ARCH_X86_64 */
|
||||
p_filter->k = 0xc000003e;
|
||||
p_filter++;
|
||||
p_filter->code = BPF_RET+BPF_K;
|
||||
p_filter->jt = 0;
|
||||
p_filter->jf = 0;
|
||||
/* SECCOMP_RET_KILL */
|
||||
p_filter->k = 0;
|
||||
p_filter++;
|
||||
|
||||
/* Load the syscall number. */
|
||||
p_filter->code = BPF_LD+BPF_W+BPF_ABS;
|
||||
p_filter->jt = 0;
|
||||
p_filter->jf = 0;
|
||||
/* Offset 0 for syscall number. */
|
||||
p_filter->k = 0;
|
||||
p_filter++;
|
||||
|
||||
for (i = 0; i < s_syscall_index; ++i)
|
||||
{
|
||||
int block_size = 1;
|
||||
if (s_args_3[i])
|
||||
{
|
||||
block_size = 8;
|
||||
}
|
||||
else if (s_args_2[i])
|
||||
{
|
||||
block_size = 6;
|
||||
}
|
||||
else if (s_args_1[i])
|
||||
{
|
||||
block_size = 4;
|
||||
}
|
||||
/* Check for syscall number match. */
|
||||
p_filter->code = BPF_JMP+BPF_JEQ+BPF_K;
|
||||
p_filter->jt = 0;
|
||||
p_filter->jf = block_size;
|
||||
p_filter->k = s_syscalls[i];
|
||||
p_filter++;
|
||||
/* Check argument matches if necessary. */
|
||||
if (s_args_3[i])
|
||||
{
|
||||
p_filter->code = BPF_LD+BPF_W+BPF_ABS;
|
||||
p_filter->jt = 0;
|
||||
p_filter->jf = 0;
|
||||
p_filter->k = 16 + ((s_args_3[i] - 1) * 8);
|
||||
p_filter++;
|
||||
p_filter->code = BPF_JMP+BPF_JEQ+BPF_K;
|
||||
p_filter->jt = 0;
|
||||
p_filter->jf = 5;
|
||||
p_filter->k = s_vals_3[i];
|
||||
p_filter++;
|
||||
}
|
||||
if (s_args_2[i])
|
||||
{
|
||||
p_filter->code = BPF_LD+BPF_W+BPF_ABS;
|
||||
p_filter->jt = 0;
|
||||
p_filter->jf = 0;
|
||||
p_filter->k = 16 + ((s_args_2[i] - 1) * 8);
|
||||
p_filter++;
|
||||
p_filter->code = BPF_JMP+BPF_JEQ+BPF_K;
|
||||
p_filter->jt = 0;
|
||||
p_filter->jf = 3;
|
||||
p_filter->k = s_vals_2[i];
|
||||
p_filter++;
|
||||
}
|
||||
if (s_args_1[i])
|
||||
{
|
||||
int arg = s_args_1[i];
|
||||
int code = BPF_JMP+BPF_JEQ+BPF_K;
|
||||
int val = s_vals_1[i];
|
||||
int jt = 0;
|
||||
int jf = 1;
|
||||
if (arg > 100)
|
||||
{
|
||||
arg -= 100;
|
||||
code = BPF_JMP+BPF_JSET+BPF_K;
|
||||
val = ~val;
|
||||
jt = 1;
|
||||
jf = 0;
|
||||
}
|
||||
p_filter->code = BPF_LD+BPF_W+BPF_ABS;
|
||||
p_filter->jt = 0;
|
||||
p_filter->jf = 0;
|
||||
p_filter->k = 16 + ((arg - 1) * 8);
|
||||
p_filter++;
|
||||
p_filter->code = code;
|
||||
p_filter->jt = jt;
|
||||
p_filter->jf = jf;
|
||||
p_filter->k = val;
|
||||
p_filter++;
|
||||
}
|
||||
p_filter->code = BPF_RET+BPF_K;
|
||||
p_filter->jt = 0;
|
||||
p_filter->jf = 0;
|
||||
/* SECCOMP_RET_ALLOW */
|
||||
p_filter->k = 0x7fff0000;
|
||||
p_filter++;
|
||||
if (s_args_1[i])
|
||||
{
|
||||
/* We trashed the accumulator so put it back. */
|
||||
p_filter->code = BPF_LD+BPF_W+BPF_ABS;
|
||||
p_filter->jt = 0;
|
||||
p_filter->jf = 0;
|
||||
p_filter->k = 0;
|
||||
p_filter++;
|
||||
}
|
||||
}
|
||||
/* No "allow" matches so kill. */
|
||||
p_filter->code = BPF_RET+BPF_K;
|
||||
p_filter->jt = 0;
|
||||
p_filter->jf = 0;
|
||||
#ifdef DEBUG_SIGSYS
|
||||
/* SECCOMP_RET_TRAP */
|
||||
p_filter->k = 0x00030000;
|
||||
#else
|
||||
/* SECCOMP_RET_KILL */
|
||||
p_filter->k = 0;
|
||||
#endif
|
||||
|
||||
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||
if (ret != 0)
|
||||
{
|
||||
if (errno == EINVAL)
|
||||
{
|
||||
/* Kernel isn't good enough. */
|
||||
return;
|
||||
}
|
||||
die("prctl PR_SET_NO_NEW_PRIVS");
|
||||
}
|
||||
|
||||
if (!tunable_seccomp_sandbox)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_SIGSYS
|
||||
{
|
||||
struct sigaction sa;
|
||||
memset(&sa, '\0', sizeof(sa));
|
||||
sa.sa_handler = handle_sigsys;
|
||||
sigaction(SIGSYS, &sa, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = prctl(PR_SET_SECCOMP, 2, &prog, 0, 0);
|
||||
if (ret != 0)
|
||||
{
|
||||
die("prctl PR_SET_SECCOMP failed");
|
||||
}
|
||||
}
|
||||
|
||||
#else /* __linux__ && __x86_64__ */
|
||||
|
||||
void
|
||||
seccomp_sandbox_init()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
seccomp_sandbox_setup_prelogin(const struct vsf_session* p_sess)
|
||||
{
|
||||
(void) p_sess;
|
||||
}
|
||||
|
||||
void
|
||||
seccomp_sandbox_setup_postlogin(const struct vsf_session* p_sess)
|
||||
{
|
||||
(void) p_sess;
|
||||
}
|
||||
|
||||
void
|
||||
seccomp_sandbox_setup_postlogin_broker()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
seccomp_sandbox_lockdown()
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* __linux__ && __x86_64__ */
|
17
seccompsandbox.h
Normal file
17
seccompsandbox.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef VSF_SECCOMPSANDBOX_H
|
||||
#define VSF_SECCOMPSANDBOX_H
|
||||
|
||||
struct vsf_session;
|
||||
|
||||
void seccomp_sandbox_init();
|
||||
|
||||
void seccomp_sandbox_setup_prelogin(const struct vsf_session* p_sess);
|
||||
|
||||
void seccomp_sandbox_setup_postlogin(const struct vsf_session* p_sess);
|
||||
|
||||
void seccomp_sandbox_setup_postlogin_broker();
|
||||
|
||||
void seccomp_sandbox_lockdown();
|
||||
|
||||
#endif /* VSF_SECCOMPSANDBOX_H */
|
||||
|
@ -132,7 +132,8 @@ vsf_secutil_change_credentials(const struct mystr* p_user_str,
|
||||
/* Misconfiguration check: don't ever chroot() to a directory writable by
|
||||
* the current user.
|
||||
*/
|
||||
if (options & VSF_SECUTIL_OPTION_CHROOT)
|
||||
if ((options & VSF_SECUTIL_OPTION_CHROOT) &&
|
||||
!(options & VSF_SECUTIL_OPTION_ALLOW_WRITEABLE_ROOT))
|
||||
{
|
||||
if (vsf_sysutil_write_access("/"))
|
||||
{
|
||||
|
12
secutil.h
12
secutil.h
@ -23,15 +23,17 @@ struct mystr;
|
||||
*/
|
||||
|
||||
/* chroot() the user into the new directory */
|
||||
#define VSF_SECUTIL_OPTION_CHROOT 1
|
||||
#define VSF_SECUTIL_OPTION_CHROOT 1
|
||||
/* Activate any supplementary groups the user may have */
|
||||
#define VSF_SECUTIL_OPTION_USE_GROUPS 2
|
||||
#define VSF_SECUTIL_OPTION_USE_GROUPS 2
|
||||
/* Do the chdir() as the effective userid of the target user */
|
||||
#define VSF_SECUTIL_OPTION_CHANGE_EUID 4
|
||||
#define VSF_SECUTIL_OPTION_CHANGE_EUID 4
|
||||
/* Use RLIMIT_NOFILE to prevent the opening of new fds */
|
||||
#define VSF_SECUTIL_OPTION_NO_FDS 8
|
||||
#define VSF_SECUTIL_OPTION_NO_FDS 8
|
||||
/* Use RLIMIT_NPROC to prevent the launching of new processes */
|
||||
#define VSF_SECUTIL_OPTION_NO_PROCS 16
|
||||
#define VSF_SECUTIL_OPTION_NO_PROCS 16
|
||||
/* Permit a writeable chroot() root */
|
||||
#define VSF_SECUTIL_OPTION_ALLOW_WRITEABLE_ROOT 32
|
||||
|
||||
void vsf_secutil_change_credentials(const struct mystr* p_user_str,
|
||||
const struct mystr* p_dir_str,
|
||||
|
@ -21,6 +21,8 @@ struct vsf_session
|
||||
struct vsf_sysutil_sockaddr* p_local_addr;
|
||||
struct vsf_sysutil_sockaddr* p_remote_addr;
|
||||
char* p_control_line_buf;
|
||||
int idle_timeout;
|
||||
int data_timeout;
|
||||
|
||||
/* Details of the data connection */
|
||||
int pasv_listen_fd;
|
||||
|
8
ssl.c
8
ssl.c
@ -171,7 +171,7 @@ ssl_control_handshake(struct vsf_session* p_sess)
|
||||
/* 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);
|
||||
vsf_sysutil_exit(1);
|
||||
}
|
||||
p_sess->control_use_ssl = 1;
|
||||
}
|
||||
@ -476,6 +476,8 @@ ssl_accept(struct vsf_session* p_sess, int fd)
|
||||
void
|
||||
ssl_comm_channel_init(struct vsf_session* p_sess)
|
||||
{
|
||||
const struct vsf_sysutil_socketpair_retval retval =
|
||||
vsf_sysutil_unix_stream_socketpair();
|
||||
if (p_sess->ssl_consumer_fd != -1)
|
||||
{
|
||||
bug("ssl_consumer_fd active");
|
||||
@ -484,8 +486,6 @@ ssl_comm_channel_init(struct vsf_session* p_sess)
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -552,7 +552,7 @@ get_ssl(struct vsf_session* p_sess, int fd)
|
||||
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 SSL_CIPHER* p_ssl_cipher = SSL_get_current_cipher(p_ssl);
|
||||
const char* p_cipher_name = SSL_CIPHER_get_name(p_ssl_cipher);
|
||||
X509* p_ssl_cert = SSL_get_peer_certificate(p_ssl);
|
||||
int reused = SSL_session_reused(p_ssl);
|
||||
|
19
sslslave.c
19
sslslave.c
@ -71,13 +71,26 @@ ssl_slave(struct vsf_session* p_sess)
|
||||
}
|
||||
else if (cmd == PRIV_SOCK_DO_SSL_READ)
|
||||
{
|
||||
str_trunc(&data_str, VSFTP_DATA_BUFSIZE);
|
||||
int size = priv_sock_get_int(p_sess->ssl_slave_fd);
|
||||
if (size <= 0 || size > VSFTP_DATA_BUFSIZE)
|
||||
{
|
||||
bug("bad size");
|
||||
}
|
||||
if (p_sess->data_fd == -1 || p_sess->p_data_ssl == 0)
|
||||
{
|
||||
bug("invalid state");
|
||||
}
|
||||
str_trunc(&data_str, (unsigned int) size);
|
||||
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)
|
||||
{
|
||||
if (p_sess->data_fd == -1 || p_sess->p_data_ssl == 0)
|
||||
{
|
||||
bug("invalid state");
|
||||
}
|
||||
priv_sock_get_str(p_sess->ssl_slave_fd, &data_str);
|
||||
ret = ssl_write(p_sess->p_data_ssl,
|
||||
str_getbuf(&data_str),
|
||||
@ -93,6 +106,10 @@ ssl_slave(struct vsf_session* p_sess)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p_sess->data_fd == -1 || p_sess->p_data_ssl == 0)
|
||||
{
|
||||
bug("invalid state");
|
||||
}
|
||||
ret = ssl_data_close(p_sess);
|
||||
if (ret == 1)
|
||||
{
|
||||
|
35
str.c
35
str.c
@ -42,7 +42,12 @@ private_str_alloc_memchunk(struct mystr* p_str, const char* p_src,
|
||||
unsigned int len)
|
||||
{
|
||||
/* Make sure this will fit in the buffer */
|
||||
unsigned int buf_needed = len + 1;
|
||||
unsigned int buf_needed;
|
||||
if (len + 1 < len)
|
||||
{
|
||||
bug("integer overflow");
|
||||
}
|
||||
buf_needed = len + 1;
|
||||
if (buf_needed > p_str->alloc_bytes)
|
||||
{
|
||||
str_free(p_str);
|
||||
@ -58,7 +63,17 @@ void
|
||||
private_str_append_memchunk(struct mystr* p_str, const char* p_src,
|
||||
unsigned int len)
|
||||
{
|
||||
unsigned int buf_needed = p_str->len + len + 1;
|
||||
unsigned int buf_needed;
|
||||
if (len + p_str->len < len)
|
||||
{
|
||||
bug("integer overflow");
|
||||
}
|
||||
buf_needed = len + p_str->len;
|
||||
if (buf_needed + 1 < buf_needed)
|
||||
{
|
||||
bug("integer overflow");
|
||||
}
|
||||
buf_needed++;
|
||||
if (buf_needed > p_str->alloc_bytes)
|
||||
{
|
||||
p_str->p_buf = vsf_sysutil_realloc(p_str->p_buf, buf_needed);
|
||||
@ -98,6 +113,10 @@ str_alloc_alt_term(struct mystr* p_str, const char* p_src, char term)
|
||||
{
|
||||
p_search++;
|
||||
len++;
|
||||
if (len == 0)
|
||||
{
|
||||
bug("integer overflow");
|
||||
}
|
||||
}
|
||||
private_str_alloc_memchunk(p_str, p_src, len);
|
||||
}
|
||||
@ -150,6 +169,10 @@ str_reserve(struct mystr* p_str, unsigned int res_len)
|
||||
{
|
||||
/* Reserve space for the trailing zero as well. */
|
||||
res_len++;
|
||||
if (res_len == 0)
|
||||
{
|
||||
bug("integer overflow");
|
||||
}
|
||||
if (res_len > p_str->alloc_bytes)
|
||||
{
|
||||
p_str->p_buf = vsf_sysutil_realloc(p_str->p_buf, res_len);
|
||||
@ -640,12 +663,20 @@ str_getline(const struct mystr* p_str, struct mystr* p_line_str,
|
||||
while (curr_pos < buf_len && p_buf[curr_pos] != '\n')
|
||||
{
|
||||
curr_pos++;
|
||||
if (curr_pos == 0)
|
||||
{
|
||||
bug("integer overflow");
|
||||
}
|
||||
}
|
||||
out_len = curr_pos - start_pos;
|
||||
/* If we ended on a \n - skip it */
|
||||
if (curr_pos < buf_len && p_buf[curr_pos] == '\n')
|
||||
{
|
||||
curr_pos++;
|
||||
if (curr_pos == 0)
|
||||
{
|
||||
bug("integer overflow");
|
||||
}
|
||||
}
|
||||
private_str_alloc_memchunk(p_line_str, p_buf + start_pos, out_len);
|
||||
*p_pos = curr_pos;
|
||||
|
11
sysutil.c
11
sysutil.c
@ -725,7 +725,7 @@ vsf_sysutil_activate_linger(int fd)
|
||||
struct linger the_linger;
|
||||
vsf_sysutil_memclr(&the_linger, sizeof(the_linger));
|
||||
the_linger.l_onoff = 1;
|
||||
the_linger.l_linger = 32767;
|
||||
the_linger.l_linger = 60 * 10;
|
||||
retval = setsockopt(fd, SOL_SOCKET, SO_LINGER, &the_linger,
|
||||
sizeof(the_linger));
|
||||
if (retval != 0)
|
||||
@ -1020,13 +1020,13 @@ vsf_sysutil_next_dirent(struct vsf_sysutil_dir* p_dir)
|
||||
unsigned int
|
||||
vsf_sysutil_strlen(const char* p_text)
|
||||
{
|
||||
unsigned int ret = strlen(p_text);
|
||||
size_t ret = strlen(p_text);
|
||||
/* A defense in depth measure. */
|
||||
if (ret > INT_MAX / 8)
|
||||
{
|
||||
die("string suspiciously long");
|
||||
}
|
||||
return ret;
|
||||
return (unsigned int) ret;
|
||||
}
|
||||
|
||||
char*
|
||||
@ -2057,7 +2057,8 @@ vsf_sysutil_sockaddr_set_ipv6addr(struct vsf_sysutil_sockaddr* p_sockptr,
|
||||
const void*
|
||||
vsf_sysutil_sockaddr_ipv6_v4(const struct vsf_sysutil_sockaddr* p_addr)
|
||||
{
|
||||
static char pattern[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF };
|
||||
static unsigned char pattern[12] =
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF };
|
||||
const unsigned char* p_addr_start;
|
||||
if (p_addr->u.u_sockaddr.sa_family != AF_INET6)
|
||||
{
|
||||
@ -2074,7 +2075,7 @@ vsf_sysutil_sockaddr_ipv6_v4(const struct vsf_sysutil_sockaddr* p_addr)
|
||||
const void*
|
||||
vsf_sysutil_sockaddr_ipv4_v6(const struct vsf_sysutil_sockaddr* p_addr)
|
||||
{
|
||||
static char ret[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF };
|
||||
static unsigned char ret[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF };
|
||||
if (p_addr->u.u_sockaddr.sa_family != AF_INET)
|
||||
{
|
||||
return 0;
|
||||
|
12
tunables.c
12
tunables.c
@ -80,12 +80,14 @@ 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_ptrace_sandbox;
|
||||
int tunable_require_ssl_reuse;
|
||||
int tunable_isolate;
|
||||
int tunable_isolate_network;
|
||||
int tunable_ftp_enable;
|
||||
int tunable_http_enable;
|
||||
int tunable_seccomp_sandbox;
|
||||
int tunable_allow_writeable_chroot;
|
||||
|
||||
unsigned int tunable_accept_timeout;
|
||||
unsigned int tunable_connect_timeout;
|
||||
@ -178,7 +180,7 @@ tunables_load_defaults()
|
||||
tunable_use_localtime = 0;
|
||||
tunable_check_shell = 1;
|
||||
tunable_hide_ids = 0;
|
||||
tunable_listen = 0;
|
||||
tunable_listen = 1;
|
||||
tunable_port_promiscuous = 0;
|
||||
tunable_passwd_chroot_enable = 0;
|
||||
tunable_no_anon_password = 0;
|
||||
@ -218,12 +220,14 @@ tunables_load_defaults()
|
||||
tunable_ssl_request_cert = 1;
|
||||
tunable_delete_failed_uploads = 0;
|
||||
tunable_implicit_ssl = 0;
|
||||
tunable_sandbox = 0;
|
||||
tunable_ptrace_sandbox = 0;
|
||||
tunable_require_ssl_reuse = 1;
|
||||
tunable_isolate = 1;
|
||||
tunable_isolate_network = 1;
|
||||
tunable_ftp_enable = 1;
|
||||
tunable_http_enable = 0;
|
||||
tunable_seccomp_sandbox = 1;
|
||||
tunable_allow_writeable_chroot = 0;
|
||||
|
||||
tunable_accept_timeout = 60;
|
||||
tunable_connect_timeout = 60;
|
||||
@ -280,7 +284,7 @@ tunables_load_defaults()
|
||||
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("AES128-SHA: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);
|
||||
|
@ -81,12 +81,14 @@ 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_ptrace_sandbox; /* DEPRECATED ptrace sandbox */
|
||||
extern int tunable_require_ssl_reuse; /* Require re-used data conn */
|
||||
extern int tunable_isolate; /* Use container clone() flags */
|
||||
extern int tunable_isolate_network; /* Use CLONE_NEWNET */
|
||||
extern int tunable_ftp_enable; /* Allow FTP protocol */
|
||||
extern int tunable_http_enable; /* Allow HTTP protocol */
|
||||
extern int tunable_seccomp_sandbox; /* seccomp filter sandbox */
|
||||
extern int tunable_allow_writeable_chroot; /* Allow misconfiguration */
|
||||
|
||||
/* Integer/numeric defines */
|
||||
extern unsigned int tunable_accept_timeout;
|
||||
|
27
twoprocess.c
27
twoprocess.c
@ -27,6 +27,7 @@
|
||||
#include "sysutil.h"
|
||||
#include "sysdeputil.h"
|
||||
#include "sslslave.h"
|
||||
#include "seccompsandbox.h"
|
||||
|
||||
static void drop_all_privs(void);
|
||||
static void handle_sigchld(void* duff);
|
||||
@ -51,16 +52,15 @@ handle_sigchld(void* duff)
|
||||
/* Child died, so we'll do the same! Report it as an error unless the child
|
||||
* exited normally with zero exit code
|
||||
*/
|
||||
if (vsf_sysutil_retval_is_error(vsf_sysutil_wait_get_retval(&wait_retval)) ||
|
||||
!vsf_sysutil_wait_exited_normally(&wait_retval) ||
|
||||
vsf_sysutil_wait_get_exitcode(&wait_retval) != 0)
|
||||
{
|
||||
if (vsf_sysutil_retval_is_error(vsf_sysutil_wait_get_retval(&wait_retval)))
|
||||
{
|
||||
die("waiting for child");
|
||||
}
|
||||
else if (!vsf_sysutil_wait_exited_normally(&wait_retval))
|
||||
{
|
||||
die("child died");
|
||||
}
|
||||
else
|
||||
{
|
||||
vsf_sysutil_exit(0);
|
||||
}
|
||||
vsf_sysutil_exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -132,6 +132,9 @@ vsf_two_process_start(struct vsf_session* p_sess)
|
||||
}
|
||||
}
|
||||
drop_all_privs();
|
||||
seccomp_sandbox_init();
|
||||
seccomp_sandbox_setup_prelogin(p_sess);
|
||||
seccomp_sandbox_lockdown();
|
||||
init_connection(p_sess);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
@ -426,6 +429,10 @@ common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
|
||||
{
|
||||
secutil_option |= VSF_SECUTIL_OPTION_CHANGE_EUID;
|
||||
}
|
||||
if (!was_anon && tunable_allow_writeable_chroot)
|
||||
{
|
||||
secutil_option |= VSF_SECUTIL_OPTION_ALLOW_WRITEABLE_ROOT;
|
||||
}
|
||||
calculate_chdir_dir(was_anon, &userdir_str, &chroot_str, &chdir_str,
|
||||
p_user_str, p_orig_user_str);
|
||||
vsf_secutil_change_credentials(p_user_str, &userdir_str, &chroot_str,
|
||||
@ -439,6 +446,9 @@ common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
|
||||
str_free(&chdir_str);
|
||||
str_free(&userdir_str);
|
||||
p_sess->is_anonymous = anon;
|
||||
seccomp_sandbox_init();
|
||||
seccomp_sandbox_setup_postlogin(p_sess);
|
||||
seccomp_sandbox_lockdown();
|
||||
process_post_login(p_sess);
|
||||
bug("should not get here: common_do_login");
|
||||
}
|
||||
@ -448,6 +458,7 @@ common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
|
||||
{
|
||||
ssl_comm_channel_set_producer_context(p_sess);
|
||||
}
|
||||
/* The seccomp sandbox lockdown for the priv parent is done inside here */
|
||||
vsf_priv_parent_postlogin(p_sess);
|
||||
bug("should not get here in common_do_login");
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ die(const char* p_text)
|
||||
#ifdef DIE_DEBUG
|
||||
bug(p_text);
|
||||
#endif
|
||||
vsf_sysutil_exit(1);
|
||||
vsf_sysutil_exit(2);
|
||||
}
|
||||
|
||||
void
|
||||
@ -39,7 +39,7 @@ bug(const char* p_text)
|
||||
(void) vsf_sysutil_write_loop(VSFTP_COMMAND_FD, p_text,
|
||||
vsf_sysutil_strlen(p_text));
|
||||
(void) vsf_sysutil_write_loop(VSFTP_COMMAND_FD, "\r\n", 2);
|
||||
vsf_sysutil_exit(1);
|
||||
vsf_sysutil_exit(2);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -16,6 +16,7 @@ if find_func pam_start sysdeputil.o; then
|
||||
locate_library /lib/libpam.so.0 && echo "/lib/libpam.so.0";
|
||||
locate_library /usr/lib/libpam.so && echo "-lpam";
|
||||
locate_library /usr/lib64/libpam.so && echo "-lpam";
|
||||
locate_library /lib/x86_64-linux-gnu/libpam.so.0 && echo "-lpam";
|
||||
# HP-UX ends shared libraries with .sl
|
||||
locate_library /usr/lib/libpam.sl && echo "-lpam";
|
||||
# AIX ends shared libraries with .a
|
||||
@ -24,6 +25,7 @@ else
|
||||
locate_library /lib/libcrypt.so && echo "-lcrypt";
|
||||
locate_library /usr/lib/libcrypt.so && echo "-lcrypt";
|
||||
locate_library /usr/lib64/libcrypt.so && echo "-lcrypt";
|
||||
locate_library /lib/x86_64-linux-gnu/libcrypt.so && echo "-lcrypt";
|
||||
fi
|
||||
|
||||
# Look for the dynamic linker library. Needed by older RedHat when
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef VSF_VERSION_H
|
||||
#define VSF_VERSION_H
|
||||
|
||||
#define VSF_VERSION "2.3.5"
|
||||
#define VSF_VERSION "3.0.0"
|
||||
|
||||
#endif /* VSF_VERSION_H */
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user