1
0
mirror of https://github.com/InfrastructureServices/vsftpd.git synced 2025-04-19 01:24:02 +03:00
vsftpd/ftppolicy.c
2013-06-13 13:20:40 +02:00

329 lines
9.2 KiB
C

/*
* 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);
/* 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);
/* 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);
/* Other pathname-based metadata queries. */
ptrace_sandbox_permit_file_stats(p_sandbox);
ptrace_sandbox_permit_readlink(p_sandbox);
/* 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 ||
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);
}
/* Set up network permissions according to config and session. */
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);
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)
{
int ret;
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;
}
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)
{
int ret;
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);
}
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)
{
int ret;
unsigned long arg2;
unsigned long arg3;
(void) p_arg;
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)
{
int ret;
unsigned long arg2;
unsigned long arg3;
(void) p_arg;
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;
}