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

Updated to v1.1.2

This commit is contained in:
Dag Wieers 2002-10-16 00:00:00 +02:00
parent 14c0cc79c7
commit 0c431cac6a
25 changed files with 362 additions and 43 deletions

3
AUDIT
View File

@ -11,6 +11,7 @@ dirchange.c 3
filestr.c 3
ftpcmdio.c 3
ftpdataio.c 2
hash.c 1
logging.c 3
ls.c 2
main.c 3
@ -25,7 +26,7 @@ privparent.c 3
privsock.c 3
secbuf.c 3
secutil.c 3
standalone.c 2
standalone.c 1
str.c 2
strlist.c 2
sysdeputil.c 2

View File

@ -613,3 +613,13 @@ broken log parsers.
At this point: 1.1.1 package released
-------------------------------------
- Add per-IP connection limits in standalone mode.
- Add logging of refused connect due to global or IP connection limits.
- (Many thanks for testing and suggestions from Rob van Nieuwkerk
<robn@verdi.et.tudelft.nl> and Adrian Reber <adrian@lisas.de>.
- Make connection limit exceeded messages nonblocking.
- Don't exit the listener if fork fails.
At this point: 1.1.2 package released
-------------------------------------

View File

@ -9,7 +9,7 @@ Step 1) Build vsftpd.
Switch to the directory created when you unpacked the vsftpd .tar.gz file.
e.g.:
cd vsftpd-1.1.1
cd vsftpd-1.1.2
Just type "make" (and mail me to fix it if it doesn't build ;-).
This should produce you a vsftpd binary. You can test for this, e.g.:

View File

@ -12,7 +12,7 @@ OBJS = main.o utility.o prelogin.o ftpcmdio.o postlogin.o privsock.o \
tunables.o ftpdataio.o secbuf.o ls.o \
postprivparent.o logging.o str.o netstr.o sysstr.o strlist.o \
banner.o filestr.o parseconf.o secutil.o \
ascii.o oneprocess.o twoprocess.o privops.o standalone.o \
ascii.o oneprocess.o twoprocess.o privops.o standalone.o hash.o \
sysutil.o sysdeputil.o
.c.o:

2
README
View File

@ -1,4 +1,4 @@
This is vsftpd, version 1.1.1
This is vsftpd, version 1.1.2
Author: Chris Evans
Contact: chris@scary.beasts.org

View File

@ -1,6 +1,6 @@
Summary: vsftpd - Very Secure Ftp Daemon
Name: vsftpd
Version: 1.1.1
Version: 1.1.2
Release: rh6_1
Copyright: GPL
Group: System Environment/Daemons

View File

@ -1,6 +1,6 @@
Summary: vsftpd - Very Secure Ftp Daemon
Name: vsftpd
Version: 1.1.1
Version: 1.1.2
Release: rh7_2
Copyright: GPL
Group: System Environment/Daemons

2
TODO
View File

@ -10,7 +10,7 @@ NOT SO CRITICAL
- separate upload/download max rates
- select() is assuming Linux behaviour (not threatening stability)
- IPv6 support
- enhance standalone support (IP filtering, per-IP limits)
- enhance standalone support (IP filtering? - tcp_wrappers?)
ON THE BACK BURNER
==================

View File

@ -32,6 +32,7 @@
#define FTP_IDLE_TIMEOUT 421
#define FTP_DATA_TIMEOUT 421
#define FTP_TOO_MANY_USERS 421
#define FTP_IP_LIMIT 421
#define FTP_BADSENDCONN 425
#define FTP_BADSENDNET 426
#define FTP_BADSENDFILE 451

147
hash.c Normal file
View File

@ -0,0 +1,147 @@
/*
* Part of Very Secure FTPd
* Licence: GPL
* Author: Chris Evans
* hash.c
*
* Routines to handle simple hash table lookups and modifications.
*/
#include "hash.h"
#include "sysutil.h"
#include "utility.h"
struct hash_node
{
void* p_key;
void* p_value;
struct hash_node* p_prev;
struct hash_node* p_next;
};
struct hash
{
unsigned int buckets;
unsigned int key_size;
unsigned int value_size;
hashfunc_t hash_func;
struct hash_node** p_nodes;
};
/* Internal functions */
struct hash_node** hash_get_bucket(struct hash* p_hash, void* p_key);
struct hash_node* hash_get_node_by_key(struct hash* p_hash, void* p_key);
struct hash*
hash_alloc(unsigned int buckets, unsigned int key_size,
unsigned int value_size, hashfunc_t hash_func)
{
unsigned int size;
struct hash* p_hash = vsf_sysutil_malloc(sizeof(*p_hash));
p_hash->buckets = buckets;
p_hash->key_size = key_size;
p_hash->value_size = value_size;
p_hash->hash_func = hash_func;
size = sizeof(struct hash_node*) * buckets;
p_hash->p_nodes = vsf_sysutil_malloc(size);
vsf_sysutil_memclr(p_hash->p_nodes, size);
return p_hash;
}
void*
hash_lookup_entry(struct hash* p_hash, void* p_key)
{
struct hash_node* p_node = hash_get_node_by_key(p_hash, p_key);
if (!p_node)
{
return p_node;
}
return p_node->p_value;
}
void
hash_add_entry(struct hash* p_hash, void* p_key, void* p_value)
{
struct hash_node** p_bucket;
struct hash_node* p_new_node;
if (hash_lookup_entry(p_hash, p_key))
{
bug("duplicate hash key");
}
p_bucket = hash_get_bucket(p_hash, p_key);
p_new_node = vsf_sysutil_malloc(sizeof(*p_new_node));
p_new_node->p_prev = 0;
p_new_node->p_next = 0;
p_new_node->p_key = vsf_sysutil_malloc(p_hash->key_size);
vsf_sysutil_memcpy(p_new_node->p_key, p_key, p_hash->key_size);
p_new_node->p_value = vsf_sysutil_malloc(p_hash->value_size);
vsf_sysutil_memcpy(p_new_node->p_value, p_value, p_hash->value_size);
if (!*p_bucket)
{
*p_bucket = p_new_node;
}
else
{
p_new_node->p_next = *p_bucket;
(*p_bucket)->p_prev = p_new_node;
*p_bucket = p_new_node;
}
}
void
hash_free_entry(struct hash* p_hash, void* p_key)
{
struct hash_node* p_node = hash_get_node_by_key(p_hash, p_key);
if (!p_node)
{
bug("hash node not found");
}
vsf_sysutil_free(p_node->p_key);
vsf_sysutil_free(p_node->p_value);
if (p_node->p_prev)
{
p_node->p_prev->p_next = p_node->p_next;
}
else
{
struct hash_node** p_bucket = hash_get_bucket(p_hash, p_key);
*p_bucket = p_node->p_next;
}
if (p_node->p_next)
{
p_node->p_next->p_prev = p_node->p_prev;
}
vsf_sysutil_free(p_node);
}
struct hash_node**
hash_get_bucket(struct hash* p_hash, void* p_key)
{
unsigned int bucket = (*p_hash->hash_func)(p_hash->buckets, p_key);
if (bucket >= p_hash->buckets)
{
bug("bad bucket lookup");
}
return &(p_hash->p_nodes[bucket]);
}
struct hash_node*
hash_get_node_by_key(struct hash* p_hash, void* p_key)
{
struct hash_node** p_bucket = hash_get_bucket(p_hash, p_key);
struct hash_node* p_node = *p_bucket;
if (!p_node)
{
return p_node;
}
while (p_node != 0 &&
vsf_sysutil_memcmp(p_key, p_node->p_key, p_hash->key_size) != 0)
{
p_node = p_node->p_next;
}
return p_node;
}

15
hash.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef VSFTP_HASH_H
#define VSFTP_HASH_H
struct hash;
typedef unsigned int (*hashfunc_t)(unsigned int, void*);
struct hash* hash_alloc(unsigned int buckets, unsigned int key_size,
unsigned int value_size, hashfunc_t hash_func);
void* hash_lookup_entry(struct hash* p_hash, void* p_key);
void hash_add_entry(struct hash* p_hash, void* p_key, void* p_value);
void hash_free_entry(struct hash* p_hash, void* p_key);
#endif /* VSFTP_HASH_H */

View File

@ -204,7 +204,8 @@ vsf_log_do_log_vsftpd_format(struct vsf_session* p_sess, struct mystr* p_str,
str_append_text(p_str, "] ");
}
/* And the action */
if (what != kVSFLogEntryFTPInput && what != kVSFLogEntryFTPOutput)
if (what != kVSFLogEntryFTPInput && what != kVSFLogEntryFTPOutput &&
what != kVSFLogEntryConnection)
{
if (succeeded)
{
@ -235,6 +236,9 @@ vsf_log_do_log_vsftpd_format(struct vsf_session* p_sess, struct mystr* p_str,
case kVSFLogEntryFTPOutput:
str_append_text(p_str, "FTP response");
break;
case kVSFLogEntryConnection:
str_append_text(p_str, "CONNECT");
break;
default:
bug("bad entry_type in vsf_log_do_log");
break;

View File

@ -13,7 +13,8 @@ enum EVSFLogEntryType
kVSFLogEntryMkdir,
kVSFLogEntryLogin,
kVSFLogEntryFTPInput,
kVSFLogEntryFTPOutput
kVSFLogEntryFTPOutput,
kVSFLogEntryConnection
};
/* vsf_log_init()

6
main.c
View File

@ -54,7 +54,7 @@ main(int argc, const char* argv[])
/* Parent <-> child comms */
0, -1, -1,
/* Number of clients */
-1
0, 0
};
int config_specified = 0;
const char* p_config_name = VSFTP_DEFAULT_CONFIG;
@ -103,7 +103,9 @@ main(int argc, const char* argv[])
if (tunable_listen)
{
/* Standalone mode */
the_session.num_clients = vsf_standalone_main();
struct vsf_client_launch ret = vsf_standalone_main();
the_session.num_clients = ret.num_children;
the_session.num_this_ip = ret.num_this_ip;
}
/* Sanity checks - exit with a graceful error message if our STDIN is not
* a socket. Also check various config options don't collide.

View File

@ -94,6 +94,7 @@ parseconf_uint_array[] =
{ "listen_port", &tunable_listen_port },
{ "max_clients", &tunable_max_clients },
{ "file_open_mode", &tunable_file_open_mode },
{ "max_per_ip", &tunable_max_per_ip },
{ 0, 0 }
};

View File

@ -19,6 +19,7 @@
#include "sysutil.h"
#include "session.h"
#include "banner.h"
#include "logging.h"
/* Functions used */
static void emit_greeting(struct vsf_session* p_sess);
@ -44,14 +45,27 @@ init_connection(struct vsf_session* p_sess)
static void
emit_greeting(struct vsf_session* p_sess)
{
/* Check for client limit (standalone mode only) */
struct mystr str_log_line = INIT_MYSTR;
/* Check for client limits (standalone mode only) */
if (tunable_max_clients > 0 &&
p_sess->num_clients > (int)tunable_max_clients)
p_sess->num_clients > tunable_max_clients)
{
vsf_cmdio_write(p_sess, FTP_TOO_MANY_USERS,
str_alloc_text(&str_log_line, "Connection refused: too many sessions.");
vsf_log_line(p_sess, kVSFLogEntryConnection, &str_log_line);
vsf_cmdio_write_noblock(p_sess, FTP_TOO_MANY_USERS,
"There are too many connected users, please try later.");
vsf_sysutil_exit(0);
}
if (tunable_max_per_ip > 0 &&
p_sess->num_this_ip > tunable_max_per_ip)
{
str_alloc_text(&str_log_line,
"Connection refused: too many sessions for this address.");
vsf_log_line(p_sess, kVSFLogEntryConnection, &str_log_line);
vsf_cmdio_write_noblock(p_sess, FTP_IP_LIMIT,
"There are too many connections from your internet address.");
vsf_sysutil_exit(0);
}
if (!str_isempty(&p_sess->banner_str))
{
vsf_banner_write(p_sess, &p_sess->banner_str, FTP_GREET);

View File

@ -72,7 +72,8 @@ struct vsf_session
int child_fd;
/* Other details */
int num_clients;
unsigned int num_clients;
unsigned int num_this_ip;
};
#endif /* VSF_SESSION_H */

View File

@ -15,28 +15,40 @@
#include "sysdeputil.h"
#include "utility.h"
#include "defs.h"
#include "hash.h"
static int s_reload_needed;
static int s_children;
static unsigned int s_children;
static struct hash* s_p_ip_count_hash;
static struct hash* s_p_pid_ip_hash;
static void handle_sigchld(int duff);
static void handle_sigchld(void* p_private);
static void handle_sighup(int duff);
static void do_reload(void);
static void prepare_child(int sockfd);
static unsigned int handle_ip_count(
struct vsf_sysutil_ipv4addr* p_accept_addr);
static void drop_ip_count(struct vsf_sysutil_ipv4addr* p_ip);
int
static unsigned int hash_ip(unsigned int buckets, void* p_key);
static unsigned int hash_pid(unsigned int buckets, void* p_key);
struct vsf_client_launch
vsf_standalone_main(void)
{
struct vsf_sysutil_sockaddr* p_sockaddr = 0;
struct vsf_sysutil_ipv4addr listen_ipaddr;
int listen_sock = vsf_sysutil_get_ipv4_sock();
int retval;
s_p_ip_count_hash = hash_alloc(256, sizeof(struct vsf_sysutil_ipv4addr),
sizeof(unsigned int), hash_ip);
s_p_pid_ip_hash = hash_alloc(256, sizeof(int),
sizeof(struct vsf_sysutil_ipv4addr), hash_pid);
if (tunable_setproctitle_enable)
{
vsf_sysutil_setproctitle("LISTENER");
}
vsf_sysutil_install_async_sighandler(kVSFSysUtilSigCHLD, handle_sigchld);
vsf_sysutil_install_sighandler(kVSFSysUtilSigCHLD, handle_sigchld, 0);
vsf_sysutil_install_async_sighandler(kVSFSysUtilSigHUP, handle_sighup);
vsf_sysutil_activate_reuseaddr(listen_sock);
@ -61,8 +73,15 @@ vsf_standalone_main(void)
while (1)
{
struct vsf_client_launch child_info;
static struct vsf_sysutil_sockaddr* p_accept_addr;
int new_child;
int new_client_sock = vsf_sysutil_accept_timeout(listen_sock, 0, 0);
struct vsf_sysutil_ipv4addr ip_addr;
/* NOTE - wake up every 10 seconds to make sure we notice child exit
* in a timely manner (the sync signal framework race)
*/
int new_client_sock = vsf_sysutil_accept_timeout(
listen_sock, &p_accept_addr, 10);
if (s_reload_needed)
{
s_reload_needed = 0;
@ -70,18 +89,27 @@ vsf_standalone_main(void)
}
if (vsf_sysutil_retval_is_error(new_client_sock))
{
if (vsf_sysutil_get_error() == kVSFSysUtilErrINTR)
{
continue;
}
die("accept");
continue;
}
ip_addr = vsf_sysutil_sockaddr_get_ipaddr(p_accept_addr);
++s_children;
new_child = vsf_sysutil_fork();
if (new_child)
child_info.num_children = s_children;
child_info.num_this_ip = handle_ip_count(&ip_addr);
new_child = vsf_sysutil_fork_failok();
if (new_child != 0)
{
/* Parent context */
vsf_sysutil_close(new_client_sock);
if (new_child > 0)
{
hash_add_entry(s_p_pid_ip_hash, (void*)&new_child, (void*)&ip_addr);
}
else
{
/* fork() failed, clear up! */
--s_children;
drop_ip_count(&ip_addr);
}
/* Fall through to while() loop and accept() again */
}
else
@ -92,7 +120,7 @@ vsf_standalone_main(void)
/* By returning here we "launch" the child process with the same
* contract as xinetd would provide.
*/
return s_children;
return child_info;
}
}
}
@ -109,21 +137,48 @@ prepare_child(int new_client_sock)
vsf_sysutil_close(new_client_sock);
}
}
static void
handle_sigchld(int duff)
drop_ip_count(struct vsf_sysutil_ipv4addr* p_ip)
{
/* WARNING - async handler. Must not call anything which might have
* re-entrancy issues
*/
int reap_one = 1;
(void) duff;
unsigned int count;
unsigned int* p_count =
(unsigned int*)hash_lookup_entry(s_p_ip_count_hash, (void*)p_ip);
if (!p_count)
{
bug("IP address missing from hash");
}
count = *p_count;
if (!count)
{
bug("zero count for IP address");
}
count--;
*p_count = count;
if (!count)
{
hash_free_entry(s_p_ip_count_hash, (void*)p_ip);
}
}
static void
handle_sigchld(void* p_private)
{
unsigned int reap_one = 1;
(void) p_private;
while (reap_one)
{
reap_one = vsf_sysutil_wait_reap_one();
reap_one = (unsigned int)vsf_sysutil_wait_reap_one();
if (reap_one)
{
struct vsf_sysutil_ipv4addr* p_ip;
/* Account total number of instances */
--s_children;
/* Account per-IP limit */
p_ip = (struct vsf_sysutil_ipv4addr*)
hash_lookup_entry(s_p_pid_ip_hash, (void*)&reap_one);
drop_ip_count(p_ip);
hash_free_entry(s_p_pid_ip_hash, (void*)&reap_one);
}
}
}
@ -144,3 +199,41 @@ do_reload(void)
vsf_parseconf_load_file(0);
}
static unsigned int
hash_ip(unsigned int buckets, void* p_key)
{
struct vsf_sysutil_ipv4addr* p_addr = (struct vsf_sysutil_ipv4addr*)p_key;
unsigned int val = p_addr->data[0] << 24;
val |= p_addr->data[1] << 16;
val |= p_addr->data[2] << 8;
val |= p_addr->data[3];
return val % buckets;
}
static unsigned int
hash_pid(unsigned int buckets, void* p_key)
{
unsigned int* p_pid = (unsigned int*)p_key;
return (*p_pid) % buckets;
}
static unsigned int
handle_ip_count(struct vsf_sysutil_ipv4addr* p_accept_addr)
{
unsigned int* p_count =
(unsigned int*)hash_lookup_entry(s_p_ip_count_hash, (void*)p_accept_addr);
unsigned int count;
if (!p_count)
{
count = 1;
hash_add_entry(s_p_ip_count_hash, (void*)p_accept_addr, (void*)&count);
}
else
{
count = *p_count;
count++;
*p_count = count;
}
return count;
}

View File

@ -1,6 +1,12 @@
#ifndef VSF_STANDALONE_H
#define VSF_STANDALONE_H
struct vsf_client_launch
{
unsigned int num_children;
unsigned int num_this_ip;
};
/* vsf_standalone_main()
* PURPOSE
* This function starts listening on the network for incoming FTP connections.
@ -8,9 +14,10 @@
* descriptor 0, 1 and 2 set to the network socket of the new client.
*
* RETURNS
* Returns the current number of clients.
* Returns a structure representing the current number of clients, and
* instances for this IP addresss.
*/
int vsf_standalone_main(void);
struct vsf_client_launch vsf_standalone_main(void);
#endif /* VSF_STANDALONE_H */

View File

@ -440,12 +440,19 @@ vsf_sysutil_getpid(void)
int
vsf_sysutil_fork(void)
{
int retval = fork();
int retval = vsf_sysutil_fork_failok();
if (retval < 0)
{
die("fork");
}
else if (retval == 0)
return retval;
}
int
vsf_sysutil_fork_failok(void)
{
int retval = fork();
if (retval == 0)
{
s_current_pid = -1;
}
@ -485,8 +492,12 @@ vsf_sysutil_wait_reap_one(void)
/* No more children */
return 0;
}
if (retval < 0)
{
die("waitpid");
}
/* Got one */
return 1;
return retval;
}
int
@ -1477,6 +1488,7 @@ vsf_sysutil_accept_timeout(int fd, struct vsf_sysutil_sockaddr** p_sockptr,
}
}
retval = accept(fd, (struct sockaddr*) &remote_addr, &socklen);
vsf_sysutil_check_pending_actions(kVSFSysUtilUnknown, 0, 0);
if (retval < 0)
{
return retval;

View File

@ -154,6 +154,7 @@ void vsf_sysutil_free(void* p_ptr);
/* Process creation/exit/process handling */
unsigned int vsf_sysutil_getpid(void);
int vsf_sysutil_fork(void);
int vsf_sysutil_fork_failok(void);
void vsf_sysutil_exit(int exit_code);
struct vsf_sysutil_wait_retval
{

View File

@ -61,6 +61,7 @@ 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;
const char* tunable_secure_chroot_dir = "/usr/share/empty";
const char* tunable_ftp_username = "ftp";

View File

@ -55,6 +55,7 @@ extern unsigned int tunable_local_max_rate;
extern unsigned int tunable_listen_port;
extern unsigned int tunable_max_clients;
extern unsigned int tunable_file_open_mode;
extern unsigned int tunable_max_per_ip;
/* String defines */
extern const char* tunable_secure_chroot_dir;

View File

@ -396,6 +396,13 @@ Default: 077
If vsftpd is in standalone mode, this is the maximum number of clients which
may be connected. Any additional clients connecting will get an error message.
Default: 0 (unlimited)
.TP
.B max_per_ip
If vsftpd is in standalone mode, this is the maximum number of clients which
may be connected from the same source internet address. A client will get an
error message if they go over this limit.
Default: 0 (unlimited)
.TP
.B pasv_max_port

View File

@ -1,7 +1,7 @@
#ifndef VSF_VERSION_H
#define VSF_VERSION_H
#define VSF_VERSION "1.1.1"
#define VSF_VERSION "1.1.2"
#endif /* VSF_VERSION_H */