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

Updated to v1.2.0

This commit is contained in:
Dag Wieers 2003-05-29 00:00:00 +02:00
parent fa545eaa75
commit 562d5b3c42
45 changed files with 1906 additions and 391 deletions

1
AUDIT
View File

@ -12,6 +12,7 @@ filestr.c 3
ftpcmdio.c 3
ftpdataio.c 2
hash.c 1
ipv6parse.c 1
logging.c 3
ls.c 2
main.c 3

View File

@ -637,3 +637,61 @@ rather than a partial byte count!
At this point: 1.1.3 package released
-------------------------------------
- Eliminate crypt() not defined warning.
- "grep -q" is not standard to redirect to /dev/null instead.
- Make banned_email_file work second time around.
- Add force_dot_files to work around broken clients. The behaviour when
enabled is very wu-ftpd like.
- Implement SITE HELP - should work around IE bug?
- Update README, vsftpd.conf with references to read the manual page!
- Log revamp: add dual_log_enable to log to xferlog AND vsftpd.log.
- Log revamp: add syslog_enable to log vsftpd.log to syslog().
- Add "background" option to background the listener process.
- Fix warning is vsftpd.8 man page, Bill Nottingham <notting@redhat.com>.
- Fix tcp wrappers support to NOT emit loads of Bad file descriptor messages
to the system log.
- Add ability to make bandwidth limiter smoother by using e.g.
trans_chunk_size=8192.
- Add ability for virtual users to use local privs non anon privs, via
virtual_use_local_privs=YES.
- Fix sendfile() fallback on FreeBSD, thanks to Adam Stroud
<adstro@stny.rr.com>.
- Add pam_session support, as well as utmp and wtmp logging for local logins
(when using a PAM build). Tested pam_limits maxlogins works.
- Ensure the source IP address for PORT connects is always the same as the
control connection local IP address. Previously it was not when NOT using
connect_from_port_20 in the presence of multiple local IP addresses.
- Oops - make max_per_ip and max_clients work with the two process model
when both connect_from_port_20 and chown_uploads are false.
- Initial IPv6 support (EPSV only).
- Add EPRT support to IPv6.
- Fix "ls .file" to list .file even if the ls -a flag is not present. Noted
by and thanks to Sean Millichamp <sean@enertronllc.com>.
- Better error messages for config file parse fail: include setting name.
- Fix bug in str_split_text where text is greater than 1 character long!
- Make it build on Solaris8 - switch from utmp to utmpx and handle missing
LOG_FTP.
- Always check for VSFTPD_LOAD_CONF environment variable.
- Implement HELP properly (should help broken clients).
- Fix FreeBSD build (no utmpx.h, so disable feature).
- Fix chown_uploads.
- "Guess fix" for FreeBSD reported bug. I reckon FreeBSD is returning -EINTR
from a blocking close but still closing the fd, despite the error return. So
cater for this. Reported by Drew Vogel <dvogel@intercarve.net>.
- Add download_enable and dirlist_enable. Useful in conjunction with the
per-user config stuff.
- Add chmod_enable.
- Implement STRU and MODE for _old_, broken clients!
- Log connects.
- Fix 500 OOPS with chown_uploads and an APPE command.
- Improve some error messages: die -> die2 for more information.
- Repair max_per_ip (problem comparing IPv4 addresses).
- Make chown_uploads work with virtual users.
- Chmod files to 0600 before chown_uploads kicks in.
- Add STOU support.
- Add cmds_allowed config parameter.
- Add some FAQ entries.
At this point: v1.2.0 released!
===============================

53
FAQ
View File

@ -5,6 +5,11 @@ Q) Can I restrict users to their home directories?
A) Yes. You are probably after the setting:
chroot_local_user=YES
Q) Why don't symlinks work with chroot_local_user=YES?
A) This is a consequence of how chroot() security works. As alternatives,
look into hard links, or if you have a modern Linux, see the powerful
"mount --bind".
Q) Does vsftpd support a limit on the number of users connected?
A1) Yes, indirectly. vsftpd is an inetd-based service. If use the popular
"xinetd" as your inetd, this supports per-service per-IP connection limits.
@ -61,10 +66,12 @@ careful in this area. But, the system's libc might want to open locale
config files or other settings...
Q) Help! Uploaded files are appearing with permissions -rw-------.
A) Depending on if this is an upload by a local user or an anonymous user,
A1) Depending on if this is an upload by a local user or an anonymous user,
use "local_umask" or "anon_umask" to change this. For example, use
"anon_umask=022" to give anonymously uploaded files permissions
-rw-r--r--. Note that the "0" before the "22" is important.
A2) Also see the vsftpd.conf.5 man page for the new "file_open_mode"
parameter.
Q) Help! How do I integrate with LDAP users and logins?
A) Use vsftpd's PAM integration to do this, and have PAM authenticate against
@ -100,3 +107,47 @@ A) If this is for an anonymous login, check that the home directory for the
user "ftp" is correct. If you are using the config setting "anon_root", check
that is correct too.
Q) Help! vsftpd is reporting times as GMT times and not local times!
A) This behaviour can be changed with the setting "use_localtime=YES".
Q) Help! Can I disable certain FTP commands?
A) Yes. There are some individual settings (e.g. dirlist_enable) or you can
specify a complete set of allowed commands with "cmds_allowed".
Q) Help! Can I change the port that vsftpd runs on?
A1) Yes. If you are running vsftpd in standalone mode, use the "listen_port"
directive in vsftpd.conf.
A2) Yes. If you are running vsftpd from an inetd or xinetd program, this
becomes an inetd or xinetd problem. You must change the inetd or xinetd
configuration files (perhaps /etc/inetd.conf or /etc/xinetd.d/vsftpd)
Q) Help! Will vsftpd authenticate against an LDAP server? What about a
MySQL server?
A) Yes. vsftpd uses PAM for authentication, so you need to configure PAM
to use pam_ldap or pam_mysql modules. This may involve installing the PAM
modules and then editing the PAM config file (perhaps /etc/pam.d/vsftpd).
Q) Help! Does vsftpd support per-IP limits?
A1) Yes. If you are running vsftpd standalone, there is a "max_per_ip"
setting.
A2) Yes. If you are running vsftpd via xinetd, there is an xinetd config
variable "per_source".
Q) Help! Does vsftpd support bandwidth limiting?
A) Yes. See vsftpd.conf.5 man page and investigate settings such as
"anon_max_rate" and "local_max_rate".
Q) Help! Does vsftpd support IP-based access control?
A1) Yes. vsftpd can integrate with tcp_wrappers (if built with this support).
It is enabled with the setting "tcp_wrappers=YES".
A2) Yes. vsftpd can be run from xinetd, which supports tcp_wrappers
integration.
Q) Help! Does vsftpd support IPv6?
A) Yes, as of version 1.2.0. Read the vsftpd.conf.5 man page.
Q) Blah.. blah..
A) For a good idea of what vsftpd can do, read the vsftpd.conf.5 man page
and the EXAMPLES.

View File

@ -13,7 +13,7 @@ OBJS = main.o utility.o prelogin.o ftpcmdio.o postlogin.o privsock.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 hash.o \
tcpwrap.o \
tcpwrap.o ipv6parse.o \
sysutil.o sysdeputil.o
.c.o:

8
README
View File

@ -1,4 +1,4 @@
This is vsftpd, version 1.1.3
This is vsftpd, version 1.2.0
Author: Chris Evans
Contact: chris@scary.beasts.org
@ -24,3 +24,9 @@ Installation
Please see the INSTALL file.
Configuration
=============
All configuration options are documented in the manual page vsftpd.conf.5.
Various example configurations are discussed in the EXAMPLE directory.

View File

@ -1,6 +1,6 @@
Summary: vsftpd - Very Secure Ftp Daemon
Name: vsftpd
Version: 1.1.3
Version: 1.2.0
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.3
Version: 1.2.0
Release: rh7_2
Copyright: GPL
Group: System Environment/Daemons

20
TODO
View File

@ -1,32 +1,38 @@
CRITICAL
========
- Improve FAQ, docs.
- Integrated test suite (I'm so lazy..)
NOT SO CRITICAL
===============
- separate out xferlog and vsftpd.log
- lock the files being modified?
- PASV auto address guessing?
- option to chroot to home dir and THEN apply init_dir
- pasv_addr_file option (patch from Adam Luter)
- Sweedish characters showing as ? in the log.
- Limits on GIDs allowed to authenticate?
- separate upload/download max rates
- select() is assuming Linux behaviour (not threatening stability)
- IPv6 support
- some FTP clients are trying to use MDTM to _set_ timestamps?
- smoother bandwidth limiter
- add example global bandwidth limiting.
- put anon FTP users in wtmp too?
- switch to new signal model
ON THE BACK BURNER
==================
- pam_session - I don't think it buys me wtmp
- lock the files being modified?
- repeated PORT problem?
- transfer stats
- Small race: signal might come in just before we start a blocking call
- wtmp support (should be done through pam_session support?)
- OpenSSL support
- log logout (pam session support provides this for locals)
NOT PLANNED
===========
- syslog() support - I don't want to encourage the broken beast
- telnet strings (no demand)
- better pattern matching in "ls" (no demand)
- "Minimal" build support

7
dummyinc/crypt.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef VSF_DUMMYINC_CRYPT_H
#define VSF_DUMMYINC_CRYPT_H
extern char* crypt(const char*, const char*);
#endif /* VSF_DUMMYINC_CRYPT_H */

7
dummyinc/utmpx.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef VSF_DUMMYINC_UTMPX_H
#define VSF_DUMMYINC_UTMPX_H
#undef VSF_SYSDEP_HAVE_UTMPX
#endif /* VSF_DUMMYINC_UTMPX_H */

View File

@ -22,7 +22,7 @@
/* Internal functions */
static void ftp_getline(struct mystr* p_str);
static void ftp_write_text_common(struct vsf_session* p_sess, int status,
const char* p_text, int noblock);
const char* p_text, int noblock, char sep);
static void ftp_write_str_common(struct vsf_session* p_sess, int status,
char sep, const struct mystr* p_str,
int noblock);
@ -47,24 +47,48 @@ handle_alarm_timeout(void* p_private)
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, 0, ' ');
}
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, '-');
}
void
vsf_cmdio_write_raw(struct vsf_session* p_sess, const char* p_text)
{
static struct mystr s_the_str;
int retval;
str_alloc_text(&s_the_str, p_text);
if (tunable_log_ftp_protocol)
{
vsf_log_line(p_sess, kVSFLogEntryFTPOutput, &s_the_str);
}
retval = str_netfd_write(&s_the_str, VSFTP_COMMAND_FD);
if (retval != 0)
{
die("str_netfd_write");
}
}
void
vsf_cmdio_write_noblock(struct vsf_session* p_sess, int status,
const char* p_text)
{
ftp_write_text_common(p_sess, status, p_text, 1);
ftp_write_text_common(p_sess, status, p_text, 1, ' ');
}
static void
ftp_write_text_common(struct vsf_session* p_sess, int status,
const char* p_text, int noblock)
const char* p_text, int noblock, 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, ' ', &s_the_str, noblock);
ftp_write_str_common(p_sess, status, sep, &s_the_str, noblock);
}
void

View File

@ -22,6 +22,29 @@ void vsf_cmdio_sock_setup(void);
void vsf_cmdio_write(struct vsf_session* p_sess, int status,
const char* p_text);
/* vsf_cmdio_write_hyphen()
* PURPOSE
* Write a response to the FTP control connection, with a hyphen '-'
* continuation indicator.
* PARAMETERS
* p_sess - the current session object
* status - the status code to report
* p_text - the text to report
*/
void vsf_cmdio_write_hyphen(struct vsf_session* p_sess, int status,
const char* p_text);
/* vsf_cmdio_write_raw()
* PURPOSE
* Write a raw response to the FTP control connection. A status code is
* not prepended, and it is also the client's responsibility to include
* newline characters if required.
* PARAMETERS
* p_sess - the current session object
* p_text - the text to report
*/
void vsf_cmdio_write_raw(struct vsf_session* p_sess, const char* p_text);
/* vsf_cmdio_write_noblock()
* PURPOSE
* The same as vsf_cmdio_write(), apart from the fact we _guarantee_ not to

View File

@ -6,10 +6,16 @@
#define FTP_NOOPOK 200
#define FTP_TYPEOK 200
#define FTP_PORTOK 200
#define FTP_EPRTOK 200
#define FTP_UMASKOK 200
#define FTP_CHMODOK 200
#define FTP_EPSVALLOK 200
#define FTP_STRUOK 200
#define FTP_MODEOK 200
#define FTP_SIZEOK 213
#define FTP_MDTMOK 213
#define FTP_SITEHELP 214
#define FTP_HELP 214
#define FTP_SYSTOK 215
#define FTP_GREET 220
#define FTP_GOODBYE 221
@ -17,6 +23,7 @@
#define FTP_TRANSFEROK 226
#define FTP_ABOROK 226
#define FTP_PASVOK 227
#define FTP_EPSVOK 229
#define FTP_LOGINOK 230
#define FTP_CWDOK 250
#define FTP_RMDIROK 250
@ -39,9 +46,11 @@
#define FTP_BADSENDFILE 451
#define FTP_BADCMD 500
#define FTP_BADHELP 502
#define FTP_NEEDUSER 503
#define FTP_NEEDRNFR 503
#define FTP_BADSTRU 504
#define FTP_BADMODE 504
#define FTP_EPSVBAD 522
#define FTP_LOGINERR 530
#define FTP_FILEFAIL 550
#define FTP_NOPERM 550

View File

@ -62,8 +62,8 @@ vsf_ftpdataio_dispose_transfer_fd(struct vsf_session* p_sess)
if (vsf_sysutil_retval_is_error(retval))
{
/* Do it again without blocking. */
vsf_sysutil_deactivate_linger(p_sess->data_fd);
vsf_sysutil_close(p_sess->data_fd);
vsf_sysutil_deactivate_linger_failok(p_sess->data_fd);
(void) vsf_sysutil_close_failok(p_sess->data_fd);
}
p_sess->data_fd = -1;
}
@ -72,8 +72,6 @@ int
vsf_ftpdataio_get_pasv_fd(struct vsf_session* p_sess)
{
static struct vsf_sysutil_sockaddr* s_p_accept_addr = 0;
struct vsf_sysutil_ipv4addr cmd_conn_addr;
struct vsf_sysutil_ipv4addr remote_addr;
int remote_fd = vsf_sysutil_accept_timeout(p_sess->pasv_listen_fd,
&s_p_accept_addr,
tunable_accept_timeout);
@ -87,12 +85,10 @@ vsf_ftpdataio_get_pasv_fd(struct vsf_session* p_sess)
* Reject the connection if it wasn't from the same IP as the
* control connection.
*/
cmd_conn_addr = vsf_sysutil_sockaddr_get_ipaddr(p_sess->p_remote_addr);
remote_addr = vsf_sysutil_sockaddr_get_ipaddr(s_p_accept_addr);
if (!tunable_pasv_promiscuous)
{
if (vsf_sysutil_memcmp(cmd_conn_addr.data, remote_addr.data,
sizeof(cmd_conn_addr)) != 0)
if (!vsf_sysutil_sockaddr_addr_equal(p_sess->p_remote_addr,
s_p_accept_addr))
{
vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Security: Bad IP connecting.");
vsf_sysutil_close(remote_fd);
@ -121,7 +117,18 @@ vsf_ftpdataio_get_port_fd(struct vsf_session* p_sess)
}
else
{
remote_fd = vsf_sysutil_get_ipv4_sock();
remote_fd = vsf_sysutil_get_ipsock(p_sess->p_port_sockaddr);
if (vsf_sysutil_sockaddr_same_family(p_sess->p_port_sockaddr,
p_sess->p_local_addr))
{
static struct vsf_sysutil_sockaddr* s_p_addr;
vsf_sysutil_sockaddr_clone(&s_p_addr, p_sess->p_local_addr);
retval = vsf_sysutil_bind(remote_fd, s_p_addr);
if (retval != 0)
{
die("vsf_sysutil_bind");
}
}
}
retval = vsf_sysutil_connect_timeout(remote_fd, p_sess->p_port_sockaddr,
tunable_connect_timeout);
@ -406,6 +413,12 @@ do_file_send_ascii(struct vsf_session* p_sess, int net_fd, int file_fd)
static char* p_readbuf;
static char* p_asciibuf;
struct vsf_transfer_ret ret_struct = { 0, 0 };
unsigned int chunk_size = VSFTP_DATA_BUFSIZE;
if (tunable_trans_chunk_size < VSFTP_DATA_BUFSIZE &&
tunable_trans_chunk_size >= 4096)
{
chunk_size = tunable_trans_chunk_size;
}
if (p_readbuf == 0)
{
/* NOTE!! * 2 factor because we can double the data by doing our ASCII
@ -417,7 +430,7 @@ do_file_send_ascii(struct vsf_session* p_sess, int net_fd, int file_fd)
while (1)
{
unsigned int num_to_write;
int retval = vsf_sysutil_read(file_fd, p_readbuf, VSFTP_DATA_BUFSIZE);
int retval = vsf_sysutil_read(file_fd, p_readbuf, chunk_size);
if (vsf_sysutil_retval_is_error(retval))
{
vsf_cmdio_write(p_sess, FTP_BADSENDFILE, "Failure reading local file.");
@ -478,6 +491,11 @@ do_file_send_binary(struct vsf_session* p_sess, int net_fd, int file_fd)
if (p_sess->bw_rate_max)
{
chunk_size = VSFTP_DATA_BUFSIZE;
if (tunable_trans_chunk_size < VSFTP_DATA_BUFSIZE &&
tunable_trans_chunk_size >= 4096)
{
chunk_size = tunable_trans_chunk_size;
}
}
else
{
@ -511,13 +529,19 @@ do_file_recv(struct vsf_session* p_sess, int net_fd, int file_fd, int is_ascii)
static char* p_recvbuf;
unsigned int num_to_write;
struct vsf_transfer_ret ret_struct = { 0, 0 };
unsigned int chunk_size = VSFTP_DATA_BUFSIZE;
if (p_recvbuf == 0)
{
vsf_secbuf_alloc(&p_recvbuf, VSFTP_DATA_BUFSIZE);
}
if (tunable_trans_chunk_size < VSFTP_DATA_BUFSIZE &&
tunable_trans_chunk_size >= 4096)
{
chunk_size = tunable_trans_chunk_size;
}
while (1)
{
int retval = vsf_sysutil_read(net_fd, p_recvbuf, VSFTP_DATA_BUFSIZE);
int retval = vsf_sysutil_read(net_fd, p_recvbuf, chunk_size);
if (vsf_sysutil_retval_is_error(retval))
{
vsf_cmdio_write(p_sess, FTP_BADSENDNET,

178
ipv6parse.c Normal file
View File

@ -0,0 +1,178 @@
/*
* Part of Very Secure FTPd
* Licence: GPL
* Author: Chris Evans
* ipv6parse.c
*
* A routine to parse ipv6 addresses. I'm paranoid and don't want to use
* inet_pton.
*/
#include "ipv6parse.h"
#include "sysutil.h"
#include "str.h"
static int ipv6_parse_main(struct mystr* p_out_str,
const struct mystr* p_in_str);
static int ipv6_parse_hex(struct mystr* p_out_str,
const struct mystr* p_in_str);
static int ipv4_parse_dotquad(struct mystr* p_out_str,
const struct mystr* p_in_str);
const unsigned char*
vsf_sysutil_parse_ipv6(const struct mystr* p_str)
{
static struct mystr s_ret;
static struct mystr s_rhs_ret;
static struct mystr s_lhs_str;
static struct mystr s_rhs_str;
unsigned int lhs_len;
unsigned int rhs_len;
str_empty(&s_ret);
str_empty(&s_rhs_ret);
str_copy(&s_lhs_str, p_str);
str_split_text(&s_lhs_str, &s_rhs_str, "::");
if (!ipv6_parse_main(&s_ret, &s_lhs_str))
{
return 0;
}
if (!ipv6_parse_main(&s_rhs_ret, &s_rhs_str))
{
return 0;
}
lhs_len = str_getlen(&s_ret);
rhs_len = str_getlen(&s_rhs_ret);
if (lhs_len + rhs_len > 16)
{
return 0;
}
if (rhs_len > 0)
{
unsigned int add_nulls = 16 - (lhs_len + rhs_len);
while (add_nulls--)
{
str_append_char(&s_ret, '\0');
}
str_append_str(&s_ret, &s_rhs_ret);
}
return str_getbuf(&s_ret);
}
static int
ipv6_parse_main(struct mystr* p_out_str, const struct mystr* p_in_str)
{
static struct mystr s_lhs_str;
static struct mystr s_rhs_str;
struct str_locate_result loc_ret;
str_copy(&s_lhs_str, p_in_str);
while (!str_isempty(&s_lhs_str))
{
str_split_char(&s_lhs_str, &s_rhs_str, ':');
if (str_isempty(&s_lhs_str))
{
return 0;
}
loc_ret = str_locate_char(&s_lhs_str, '.');
if (loc_ret.found)
{
if (!ipv4_parse_dotquad(p_out_str, &s_lhs_str))
{
return 0;
}
}
else if (!ipv6_parse_hex(p_out_str, &s_lhs_str))
{
return 0;
}
str_copy(&s_lhs_str, &s_rhs_str);
}
return 1;
}
static int
ipv6_parse_hex(struct mystr* p_out_str, const struct mystr* p_in_str)
{
unsigned int len = str_getlen(p_in_str);
unsigned int i;
unsigned int val = 0;
for (i=0; i<len; ++i)
{
int ch = vsf_sysutil_toupper(str_get_char_at(p_in_str, i));
if (ch >= '0' && ch <= '9')
{
ch -= '0';
}
else if (ch >= 'A' && ch <= 'F')
{
ch -= 'A';
ch += 10;
}
else
{
return 0;
}
val <<= 4;
val |= ch;
if (val > 0xFFFF)
{
return 0;
}
}
str_append_char(p_out_str, (val >> 8));
str_append_char(p_out_str, (val & 0xFF));
return 1;
}
static int
ipv4_parse_dotquad(struct mystr* p_out_str, const struct mystr* p_in_str)
{
unsigned int len = str_getlen(p_in_str);
unsigned int i;
unsigned int val = 0;
unsigned int final_val = 0;
int seen_char = 0;
int dots = 0;
for (i=0; i<len; ++i)
{
int ch = str_get_char_at(p_in_str, i);
if (ch == '.')
{
if (!seen_char || dots == 3)
{
return 0;
}
seen_char = 0;
dots++;
final_val <<= 8;
final_val |= val;
val = 0;
}
else if (ch >= '0' && ch <= '9')
{
ch -= '0';
val *= 10;
val += ch;
if (val > 255)
{
return 0;
}
seen_char = 1;
}
else
{
return 0;
}
}
if (dots != 3 || !seen_char)
{
return 0;
}
final_val <<= 8;
final_val |= val;
str_append_char(p_out_str, (final_val >> 24));
str_append_char(p_out_str, ((final_val >> 16) & 0xFF));
str_append_char(p_out_str, ((final_val >> 8) & 0xFF));
str_append_char(p_out_str, (final_val & 0xFF));
return 1;
}

14
ipv6parse.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef VSF_IPV6PARSE_H
#define VSF_IPV6PARSE_H
struct mystr;
/* Effectively doing the same sort of job as inet_pton. Since inet_pton does
* a non-trivial amount of parsing, we'll do it ourselves for maximum security
* and safety.
*/
const unsigned char* vsf_sysutil_parse_ipv6(const struct mystr* p_str);
#endif /* VSF_IPV6PARSE_H */

View File

@ -25,21 +25,37 @@ static void vsf_log_do_log_vsftpd_format(struct vsf_session* p_sess,
const struct mystr* p_log_str);
static void vsf_log_do_log_wuftpd_format(struct vsf_session* p_sess,
struct mystr* p_str, int succeeded);
static void vsf_log_do_log_to_file(int fd, struct mystr* p_str);
void
vsf_log_init(struct vsf_session* p_sess)
{
int retval;
if (!tunable_xferlog_enable)
if (!tunable_xferlog_enable && !tunable_dual_log_enable)
{
return;
}
retval = vsf_sysutil_create_or_open_file(tunable_xferlog_file, 0600);
if (vsf_sysutil_retval_is_error(retval))
if (tunable_dual_log_enable || tunable_xferlog_std_format)
{
die("failed to open ftp log file");
retval = vsf_sysutil_create_or_open_file(tunable_xferlog_file, 0600);
if (vsf_sysutil_retval_is_error(retval))
{
die2("failed to open xferlog log file:", tunable_xferlog_file);
}
p_sess->xferlog_fd = retval;
}
if (tunable_dual_log_enable || !tunable_xferlog_std_format)
{
if (!tunable_syslog_enable)
{
retval = vsf_sysutil_create_or_open_file(tunable_vsftpd_log_file, 0600);
if (vsf_sysutil_retval_is_error(retval))
{
die2("failed to open vsftpd log file:", tunable_vsftpd_log_file);
}
p_sess->vsftpd_log_fd = retval;
}
}
p_sess->log_fd = retval;
}
static int
@ -88,30 +104,44 @@ vsf_log_common(struct vsf_session* p_sess, int succeeded,
enum EVSFLogEntryType what, const struct mystr* p_str)
{
static struct mystr s_log_str;
int retval;
if (p_sess->log_fd == -1 || (tunable_xferlog_std_format &&
!vsf_log_type_is_transfer(what)))
/* Handle xferlog line if appropriate */
if (p_sess->xferlog_fd != -1 && vsf_log_type_is_transfer(what))
{
return;
vsf_log_do_log_wuftpd_format(p_sess, &s_log_str, succeeded);
vsf_log_do_log_to_file(p_sess->xferlog_fd, &s_log_str);
}
retval = vsf_sysutil_lock_file(p_sess->log_fd);
/* Handle vsftpd.log line if appropriate */
if (p_sess->vsftpd_log_fd != -1)
{
vsf_log_do_log_vsftpd_format(p_sess, &s_log_str, succeeded, what, p_str);
vsf_log_do_log_to_file(p_sess->vsftpd_log_fd, &s_log_str);
}
/* Handle syslog() line if appropriate */
if (tunable_syslog_enable)
{
int severe = 0;
vsf_log_do_log_vsftpd_format(p_sess, &s_log_str, succeeded, what, p_str);
if (what == kVSFLogEntryLogin && !succeeded)
{
severe = 1;
}
str_syslog(&s_log_str, severe);
}
}
static void
vsf_log_do_log_to_file(int fd, struct mystr* p_str)
{
int retval = vsf_sysutil_lock_file(fd);
if (vsf_sysutil_retval_is_error(retval))
{
return;
}
if (tunable_xferlog_std_format)
{
vsf_log_do_log_wuftpd_format(p_sess, &s_log_str, succeeded);
}
else
{
vsf_log_do_log_vsftpd_format(p_sess, &s_log_str, succeeded, what, p_str);
}
str_replace_unprintable(&s_log_str, '?');
str_append_char(&s_log_str, '\n');
/* Write it! Ignore write failure; maybe the disk filled or something */
(void) str_write_loop(&s_log_str, p_sess->log_fd);
vsf_sysutil_unlock_file(p_sess->log_fd);
str_replace_unprintable(p_str, '?');
str_append_char(p_str, '\n');
/* Ignore write failure; maybe the disk filled etc. */
(void) str_write_loop(p_str, fd);
vsf_sysutil_unlock_file(fd);
}
static void

21
ls.c
View File

@ -56,6 +56,11 @@ vsf_ls_populate_dir_list(struct mystr_list* p_list,
{
do_stat = 1;
}
/* If the filter starts with a . then implicitly enable -a */
if (!str_isempty(p_filter_str) && str_get_char_at(p_filter_str, 0) == '.')
{
a_option = 1;
}
/* "Normalise" the incoming base directory string by making sure it
* ends in a '/' if it is nonempty
*/
@ -78,6 +83,7 @@ vsf_ls_populate_dir_list(struct mystr_list* p_list,
}
while (1)
{
int len;
static struct mystr s_next_filename_str;
static struct mystr s_next_path_and_filename_str;
static struct vsf_sysutil_statbuf* s_p_statbuf;
@ -86,10 +92,19 @@ vsf_ls_populate_dir_list(struct mystr_list* p_list,
{
break;
}
if (!a_option && str_getlen(&s_next_filename_str) > 0 &&
str_get_char_at(&s_next_filename_str, 0) == '.')
len = str_getlen(&s_next_filename_str);
if (len > 0 && str_get_char_at(&s_next_filename_str, 0) == '.')
{
continue;
if (!a_option && !tunable_force_dot_files)
{
continue;
}
if (!a_option &&
((len == 2 && str_get_char_at(&s_next_filename_str, 1) == '.') ||
len == 1))
{
continue;
}
}
/* If we have an ls option which is a filter, apply it */
if (!str_isempty(p_filter_str))

54
main.c
View File

@ -41,15 +41,15 @@ main(int argc, const char* argv[])
/* Login */
1, INIT_MYSTR, INIT_MYSTR,
/* Protocol state */
0, 1, INIT_MYSTR, 0,
0, 1, INIT_MYSTR, 0, 0,
/* Session state */
0,
/* Userids */
-1, -1,
-1, -1, -1,
/* Pre-chroot() cache */
INIT_MYSTR, INIT_MYSTR, INIT_MYSTR, 1,
/* Logging */
-1, INIT_MYSTR, 0, 0, 0, INIT_MYSTR, 0,
-1, -1, INIT_MYSTR, 0, 0, 0, INIT_MYSTR, 0,
/* Buffers */
INIT_MYSTR, INIT_MYSTR,
/* Parent <-> child comms */
@ -92,7 +92,7 @@ main(int argc, const char* argv[])
}
else if (config_specified)
{
die("vsftpd: cannot open specified config file");
die2("vsftpd: cannot open config file:", p_config_name);
}
vsf_sysutil_free(p_statbuf);
}
@ -101,7 +101,7 @@ main(int argc, const char* argv[])
/* Warning -- warning -- may nuke argv, environ */
vsf_sysutil_setproctitle_init(argc, argv);
}
if (tunable_listen)
if (tunable_listen || tunable_listen_ipv6)
{
/* Standalone mode */
struct vsf_client_launch ret = vsf_standalone_main();
@ -121,7 +121,7 @@ main(int argc, const char* argv[])
*/
vsf_log_init(&the_session);
str_alloc_text(&the_session.remote_ip_str,
vsf_sysutil_inet_ntoa(the_session.p_remote_addr));
vsf_sysutil_inet_ntop(the_session.p_remote_addr));
/* Set up options on the command socket */
vsf_cmdio_sock_setup();
if (tunable_setproctitle_enable)
@ -134,9 +134,10 @@ main(int argc, const char* argv[])
*/
if (tunable_tcp_wrappers)
{
char* p_load_conf;
the_session.tcp_wrapper_ok = vsf_tcp_wrapper_ok(the_session.p_remote_addr);
p_load_conf = vsf_sysutil_getenv("VSFTPD_LOAD_CONF");
the_session.tcp_wrapper_ok = vsf_tcp_wrapper_ok(VSFTP_COMMAND_FD);
}
{
const char* p_load_conf = vsf_sysutil_getenv("VSFTPD_LOAD_CONF");
if (p_load_conf)
{
vsf_parseconf_load_file(p_load_conf, 1);
@ -148,7 +149,7 @@ main(int argc, const char* argv[])
tunable_banned_email_file, VSFTP_CONF_FILE_MAX);
if (vsf_sysutil_retval_is_error(retval))
{
die("cannot open banned e-mail list file");
die2("cannot open banned e-mail list file:", tunable_banned_email_file);
}
}
if (tunable_banner_file)
@ -157,7 +158,7 @@ main(int argc, const char* argv[])
VSFTP_CONF_FILE_MAX);
if (vsf_sysutil_retval_is_error(retval))
{
die("cannot open banner file");
die2("cannot open banner file:", tunable_banner_file);
}
}
/* Special case - can force one process model if we've got a setup
@ -244,19 +245,32 @@ session_init(struct vsf_session* p_sess)
vsf_sysutil_getpwnam(tunable_ftp_username);
if (p_user == 0)
{
die("vsftpd: cannot locate user specified in 'ftp_username'");
die2("vsftpd: cannot locate user specified in 'ftp_username':",
tunable_ftp_username);
}
p_sess->anon_ftp_uid = vsf_sysutil_user_getuid(p_user);
if (tunable_chown_uploads)
}
if (tunable_guest_enable)
{
const struct vsf_sysutil_user* p_user =
vsf_sysutil_getpwnam(tunable_guest_username);
if (p_user == 0)
{
p_user = vsf_sysutil_getpwnam(tunable_chown_username);
if (p_user == 0)
{
die("vsftpd: cannot locate user specified in 'chown_username'");
}
p_sess->anon_upload_chown_uid = vsf_sysutil_user_getuid(p_user);
die2("vsftpd: cannot locate user specified in 'guest_username':",
tunable_guest_username);
}
p_sess->guest_user_uid = vsf_sysutil_user_getuid(p_user);
}
if (tunable_chown_uploads)
{
const struct vsf_sysutil_user* p_user =
vsf_sysutil_getpwnam(tunable_chown_username);
if (p_user == 0)
{
die2("vsftpd: cannot locate user specified in 'chown_username':",
tunable_chown_username);
}
p_sess->anon_upload_chown_uid = vsf_sysutil_user_getuid(p_user);
}
}

View File

@ -73,6 +73,16 @@ parseconf_bool_array[] =
{ "no_anon_password", &tunable_no_anon_password },
{ "tcp_wrappers", &tunable_tcp_wrappers },
{ "use_sendfile", &tunable_use_sendfile },
{ "force_dot_files", &tunable_force_dot_files },
{ "listen_ipv6", &tunable_listen_ipv6 },
{ "dual_log_enable", &tunable_dual_log_enable },
{ "syslog_enable", &tunable_syslog_enable },
{ "background", &tunable_background },
{ "virtual_use_local_privs", &tunable_virtual_use_local_privs },
{ "session_support", &tunable_session_support },
{ "download_enable", &tunable_download_enable },
{ "dirlist_enable", &tunable_dirlist_enable },
{ "chmod_enable", &tunable_chmod_enable },
{ 0, 0 }
};
@ -98,6 +108,7 @@ parseconf_uint_array[] =
{ "max_clients", &tunable_max_clients },
{ "file_open_mode", &tunable_file_open_mode },
{ "max_per_ip", &tunable_max_per_ip },
{ "trans_chunk_size", &tunable_trans_chunk_size },
{ 0, 0 }
};
@ -112,6 +123,7 @@ parseconf_str_array[] =
{ "ftp_username", &tunable_ftp_username },
{ "chown_username", &tunable_chown_username },
{ "xferlog_file", &tunable_xferlog_file },
{ "vsftpd_log_file", &tunable_vsftpd_log_file },
{ "message_file", &tunable_message_file },
{ "nopriv_user", &tunable_nopriv_user },
{ "ftpd_banner", &tunable_ftpd_banner },
@ -126,6 +138,8 @@ parseconf_str_array[] =
{ "pasv_address", &tunable_pasv_address },
{ "listen_address", &tunable_listen_address },
{ "user_config_dir", &tunable_user_config_dir },
{ "listen_address6", &tunable_listen_address6 },
{ "cmds_allowed", &tunable_cmds_allowed },
{ 0, 0 }
};
@ -167,7 +181,7 @@ vsf_parseconf_load_file(const char* p_filename, int errs_fatal)
{
if (errs_fatal)
{
die("cannot open config file");
die2("cannot open config file:", p_filename);
}
else
{
@ -198,7 +212,7 @@ handle_config_setting(struct mystr* p_setting_str, struct mystr* p_value_str,
{
if (errs_fatal)
{
die("missing value in config file");
die2("missing value in config file for: ", str_getbuf(p_setting_str));
}
else
{
@ -228,7 +242,8 @@ handle_config_setting(struct mystr* p_setting_str, struct mystr* p_value_str,
}
else if (errs_fatal)
{
die("bad bool value in config file");
die2("bad bool value in config file for: ",
str_getbuf(p_setting_str));
}
return;
}
@ -279,7 +294,7 @@ handle_config_setting(struct mystr* p_setting_str, struct mystr* p_value_str,
}
if (errs_fatal)
{
die("unrecognised variable in config file");
die2("unrecognised variable in config file: ", str_getbuf(p_setting_str));
}
}

View File

@ -21,11 +21,12 @@
#include "sysutil.h"
#include "logging.h"
#include "sysdeputil.h"
#include "ipv6parse.h"
/* Private local functions */
static void handle_pwd(struct vsf_session* p_sess);
static void handle_cwd(struct vsf_session* p_sess);
static void handle_pasv(struct vsf_session* p_sess);
static void handle_pasv(struct vsf_session* p_sess, int is_epsv);
static void handle_retr(struct vsf_session* p_sess);
static void handle_cdup(struct vsf_session* p_sess);
static void handle_list(struct vsf_session* p_sess);
@ -47,6 +48,9 @@ static void handle_site_chmod(struct vsf_session* p_sess,
struct mystr* p_arg_str);
static void handle_site_umask(struct vsf_session* p_sess,
struct mystr* p_arg_str);
static void handle_eprt(struct vsf_session* p_sess);
static void handle_help(struct vsf_session* p_sess);
static void handle_stou(struct vsf_session* p_sess);
static int pasv_active(struct vsf_session* p_sess);
static int port_active(struct vsf_session* p_sess);
@ -57,7 +61,10 @@ static void prepend_path_to_filename(struct mystr* p_str);
static int get_remote_transfer_fd(struct vsf_session* p_sess);
static int dispose_remote_transfer_fd(struct vsf_session* p_sess);
static void handle_sigurg(void* p_private);
static void handle_upload_common(struct vsf_session* p_sess, int is_append);
static void handle_upload_common(struct vsf_session* p_sess, int is_append,
int is_unique);
static void get_unique_filename(struct mystr* p_outstr,
const struct mystr* p_base);
void
process_post_login(struct vsf_session* p_sess)
@ -79,9 +86,10 @@ process_post_login(struct vsf_session* p_sess)
}
/* Handle any login message */
vsf_banner_dir_changed(p_sess, FTP_LOGINOK);
vsf_cmdio_write(p_sess, FTP_LOGINOK, "Login successful. Have fun.");
vsf_cmdio_write(p_sess, FTP_LOGINOK, "Login successful.");
while(1)
{
int cmd_ok = 1;
if (tunable_setproctitle_enable)
{
vsf_sysutil_setproctitle("IDLE");
@ -103,7 +111,32 @@ process_post_login(struct vsf_session* p_sess)
vsf_sysutil_setproctitle_str(&proctitle_str);
str_free(&proctitle_str);
}
if (str_equal_text(&p_sess->ftp_cmd_str, "QUIT"))
/* Test command against the allowed list.. */
if (tunable_cmds_allowed)
{
static struct mystr s_src_str;
static struct mystr s_rhs_str;
str_alloc_text(&s_src_str, tunable_cmds_allowed);
while (1)
{
str_split_char(&s_src_str, &s_rhs_str, ',');
if (str_isempty(&s_src_str))
{
cmd_ok = 0;
break;
}
else if (str_equal(&s_src_str, &p_sess->ftp_cmd_str))
{
break;
}
str_copy(&s_src_str, &s_rhs_str);
}
}
if (!cmd_ok)
{
vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
}
else if (str_equal_text(&p_sess->ftp_cmd_str, "QUIT"))
{
vsf_cmdio_write(p_sess, FTP_GOODBYE, "Goodbye.");
vsf_sysutil_exit(0);
@ -124,11 +157,18 @@ process_post_login(struct vsf_session* p_sess)
handle_cdup(p_sess);
}
else if (tunable_pasv_enable &&
!p_sess->epsv_all &&
str_equal_text(&p_sess->ftp_cmd_str, "PASV"))
{
handle_pasv(p_sess);
handle_pasv(p_sess, 0);
}
else if (str_equal_text(&p_sess->ftp_cmd_str, "RETR"))
else if (tunable_pasv_enable &&
str_equal_text(&p_sess->ftp_cmd_str, "EPSV"))
{
handle_pasv(p_sess, 1);
}
else if (tunable_download_enable &&
str_equal_text(&p_sess->ftp_cmd_str, "RETR"))
{
handle_retr(p_sess);
}
@ -142,9 +182,10 @@ process_post_login(struct vsf_session* p_sess)
}
else if (str_equal_text(&p_sess->ftp_cmd_str, "HELP"))
{
vsf_cmdio_write(p_sess, FTP_BADHELP, "Sorry, I don't have help.");
handle_help(p_sess);
}
else if (str_equal_text(&p_sess->ftp_cmd_str, "LIST"))
else if (tunable_dirlist_enable &&
str_equal_text(&p_sess->ftp_cmd_str, "LIST"))
{
handle_list(p_sess);
}
@ -153,6 +194,7 @@ process_post_login(struct vsf_session* p_sess)
handle_type(p_sess);
}
else if (tunable_port_enable &&
!p_sess->epsv_all &&
str_equal_text(&p_sess->ftp_cmd_str, "PORT"))
{
handle_port(p_sess);
@ -199,7 +241,8 @@ process_post_login(struct vsf_session* p_sess)
{
handle_rnto(p_sess);
}
else if (str_equal_text(&p_sess->ftp_cmd_str, "NLST"))
else if (tunable_dirlist_enable &&
str_equal_text(&p_sess->ftp_cmd_str, "NLST"))
{
handle_nlst(p_sess);
}
@ -226,6 +269,39 @@ process_post_login(struct vsf_session* p_sess)
{
handle_mdtm(p_sess);
}
else if (tunable_port_enable &&
str_equal_text(&p_sess->ftp_cmd_str, "EPRT"))
{
handle_eprt(p_sess);
}
else if (str_equal_text(&p_sess->ftp_cmd_str, "STRU"))
{
str_upper(&p_sess->ftp_arg_str);
if (str_equal_text(&p_sess->ftp_arg_str, "F"))
{
vsf_cmdio_write(p_sess, FTP_STRUOK, "Structure set to F.");
}
else
{
vsf_cmdio_write(p_sess, FTP_BADSTRU, "Bad STRU command.");
}
}
else if (str_equal_text(&p_sess->ftp_cmd_str, "MODE"))
{
str_upper(&p_sess->ftp_arg_str);
if (str_equal_text(&p_sess->ftp_arg_str, "S"))
{
vsf_cmdio_write(p_sess, FTP_MODEOK, "Mode set to S.");
}
else
{
vsf_cmdio_write(p_sess, FTP_BADMODE, "Bad MODE command.");
}
}
else if (str_equal_text(&p_sess->ftp_cmd_str, "STOU"))
{
handle_stou(p_sess);
}
else if (str_equal_text(&p_sess->ftp_cmd_str, "PASV") ||
str_equal_text(&p_sess->ftp_cmd_str, "PORT") ||
str_equal_text(&p_sess->ftp_cmd_str, "STOR") ||
@ -237,7 +313,13 @@ process_post_login(struct vsf_session* p_sess)
str_equal_text(&p_sess->ftp_cmd_str, "RNFR") ||
str_equal_text(&p_sess->ftp_cmd_str, "RNTO") ||
str_equal_text(&p_sess->ftp_cmd_str, "SITE") ||
str_equal_text(&p_sess->ftp_cmd_str, "APPE"))
str_equal_text(&p_sess->ftp_cmd_str, "APPE") ||
str_equal_text(&p_sess->ftp_cmd_str, "EPSV") ||
str_equal_text(&p_sess->ftp_cmd_str, "EPRT") ||
str_equal_text(&p_sess->ftp_cmd_str, "RETR") ||
str_equal_text(&p_sess->ftp_cmd_str, "LIST") ||
str_equal_text(&p_sess->ftp_cmd_str, "NLST") ||
str_equal_text(&p_sess->ftp_cmd_str, "STOU"))
{
vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
}
@ -340,20 +422,43 @@ pasv_cleanup(struct vsf_session* p_sess)
}
static void
handle_pasv(struct vsf_session* p_sess)
handle_pasv(struct vsf_session* p_sess, int is_epsv)
{
static struct mystr s_pasv_res_str;
static struct vsf_sysutil_sockaddr* s_p_sockaddr;
struct vsf_sysutil_ipv4port listen_port;
struct vsf_sysutil_ipv4addr listen_ipaddr;
int bind_retries = 10;
unsigned short the_port = 0;
int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr);
if (is_epsv && !str_isempty(&p_sess->ftp_arg_str))
{
int argval;
str_upper(&p_sess->ftp_arg_str);
if (str_equal_text(&p_sess->ftp_arg_str, "ALL"))
{
p_sess->epsv_all = 1;
vsf_cmdio_write(p_sess, FTP_EPSVALLOK, "EPSV ALL ok.");
return;
}
argval = vsf_sysutil_atoi(str_getbuf(&p_sess->ftp_arg_str));
if (!is_ipv6 || argval != 2)
{
vsf_cmdio_write(p_sess, FTP_EPSVBAD, "Bad network protocol.");
return;
}
}
pasv_cleanup(p_sess);
port_cleanup(p_sess);
p_sess->pasv_listen_fd = vsf_sysutil_get_ipv4_sock();
if (is_epsv && is_ipv6)
{
p_sess->pasv_listen_fd = vsf_sysutil_get_ipv6_sock();
}
else
{
p_sess->pasv_listen_fd = vsf_sysutil_get_ipv4_sock();
}
while (--bind_retries)
{
int retval;
unsigned short the_port;
double scaled_port;
/* IPPORT_RESERVED */
unsigned short min_port = 1024;
@ -373,12 +478,8 @@ handle_pasv(struct vsf_session* p_sess)
scaled_port += ((double) the_port / (double) 65535) *
((double) max_port - min_port);
the_port = (unsigned short) scaled_port;
vsf_sysutil_sockaddr_alloc_ipv4(&s_p_sockaddr);
vsf_sysutil_sockaddr_set_port(s_p_sockaddr,
vsf_sysutil_ipv4port_from_int(the_port));
/* Bind to same address we got the incoming connect on */
vsf_sysutil_sockaddr_set_ipaddr(s_p_sockaddr,
vsf_sysutil_sockaddr_get_ipaddr(p_sess->p_local_addr));
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);
if (!vsf_sysutil_retval_is_error(retval))
{
@ -395,34 +496,40 @@ handle_pasv(struct vsf_session* p_sess)
die("vsf_sysutil_bind");
}
vsf_sysutil_listen(p_sess->pasv_listen_fd, 1);
/* Get the address of the bound socket, for the port */
vsf_sysutil_getsockname(p_sess->pasv_listen_fd, &s_p_sockaddr);
if (is_epsv)
{
str_alloc_text(&s_pasv_res_str, "Entering Extended Passive Mode (|||");
str_append_ulong(&s_pasv_res_str, (unsigned long) the_port);
str_append_text(&s_pasv_res_str, "|)");
vsf_cmdio_write_str(p_sess, FTP_EPSVOK, &s_pasv_res_str);
return;
}
if (tunable_pasv_address != 0)
{
/* Report passive address as specified in configuration */
if (vsf_sysutil_inet_aton(tunable_pasv_address, &listen_ipaddr) == 0)
if (vsf_sysutil_inet_aton(tunable_pasv_address, s_p_sockaddr) == 0)
{
die("invalid pasv_address");
}
}
str_alloc_text(&s_pasv_res_str, "Entering Passive Mode (");
if (!is_ipv6)
{
str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntop(s_p_sockaddr));
}
else
{
/* Use address of bound socket for passive address */
listen_ipaddr = vsf_sysutil_sockaddr_get_ipaddr(s_p_sockaddr);
const void* p_v4addr = vsf_sysutil_sockaddr_ipv6_v4(s_p_sockaddr);
if (p_v4addr)
{
str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntoa(p_v4addr));
}
}
listen_port = vsf_sysutil_sockaddr_get_port(s_p_sockaddr);
str_alloc_text(&s_pasv_res_str, "Entering Passive Mode (");
str_append_ulong(&s_pasv_res_str, listen_ipaddr.data[0]);
str_replace_char(&s_pasv_res_str, '.', ',');
str_append_text(&s_pasv_res_str, ",");
str_append_ulong(&s_pasv_res_str, listen_ipaddr.data[1]);
str_append_ulong(&s_pasv_res_str, the_port >> 8);
str_append_text(&s_pasv_res_str, ",");
str_append_ulong(&s_pasv_res_str, listen_ipaddr.data[2]);
str_append_text(&s_pasv_res_str, ",");
str_append_ulong(&s_pasv_res_str, listen_ipaddr.data[3]);
str_append_text(&s_pasv_res_str, ",");
str_append_ulong(&s_pasv_res_str, listen_port.data[0]);
str_append_text(&s_pasv_res_str, ",");
str_append_ulong(&s_pasv_res_str, listen_port.data[1]);
str_append_ulong(&s_pasv_res_str, the_port & 255);
str_append_text(&s_pasv_res_str, ")");
vsf_cmdio_write_str(p_sess, FTP_PASVOK, &s_pasv_res_str);
}
@ -661,12 +768,9 @@ static void
handle_port(struct vsf_session* p_sess)
{
static struct mystr s_tmp_str;
struct vsf_sysutil_ipv4addr the_addr;
struct vsf_sysutil_ipv4port the_port;
unsigned short the_port;
unsigned char vals[6];
int i;
struct vsf_sysutil_ipv4addr remote_addr =
vsf_sysutil_sockaddr_get_ipaddr(p_sess->p_remote_addr);
pasv_cleanup(p_sess);
port_cleanup(p_sess);
str_copy(&s_tmp_str, &p_sess->ftp_arg_str);
@ -696,16 +800,19 @@ handle_port(struct vsf_session* p_sess)
*/
str_copy(&s_tmp_str, &s_rhs_comma_str);
}
vsf_sysutil_memcpy(the_addr.data, vals, sizeof(the_addr.data));
vsf_sysutil_memcpy(the_port.data, &vals[4], sizeof(the_port.data));
the_port = vals[4] << 8;
the_port |= vals[5];
vsf_sysutil_sockaddr_alloc_ipv4(&p_sess->p_port_sockaddr);
vsf_sysutil_sockaddr_set_ipv4addr(p_sess->p_port_sockaddr, vals);
vsf_sysutil_sockaddr_set_port(p_sess->p_port_sockaddr, the_port);
/* SECURITY:
* 1) Reject requests not connecting to the control socket IP
* 2) Reject connects to privileged ports
*/
if (!tunable_port_promiscuous)
{
if (vsf_sysutil_memcmp(the_addr.data, remote_addr.data,
sizeof(the_addr.data)) != 0 ||
if (!vsf_sysutil_sockaddr_addr_equal(p_sess->p_remote_addr,
p_sess->p_port_sockaddr) ||
vsf_sysutil_is_port_reserved(the_port))
{
vsf_cmdio_write(p_sess, FTP_BADCMD, "Illegal PORT command.");
@ -713,9 +820,6 @@ handle_port(struct vsf_session* p_sess)
return;
}
}
vsf_sysutil_sockaddr_alloc_ipv4(&p_sess->p_port_sockaddr);
vsf_sysutil_sockaddr_set_port(p_sess->p_port_sockaddr, the_port);
vsf_sysutil_sockaddr_set_ipaddr(p_sess->p_port_sockaddr, the_addr);
vsf_cmdio_write(p_sess, FTP_PORTOK,
"PORT command successful. Consider using PASV.");
}
@ -723,12 +827,14 @@ handle_port(struct vsf_session* p_sess)
static void
handle_stor(struct vsf_session* p_sess)
{
handle_upload_common(p_sess, 0);
handle_upload_common(p_sess, 0, 0);
}
static void
handle_upload_common(struct vsf_session* p_sess, int is_append)
handle_upload_common(struct vsf_session* p_sess, int is_append, int is_unique)
{
static struct mystr s_filename;
struct mystr* p_filename;
struct vsf_transfer_ret trans_ret;
int new_file_fd;
int remote_fd;
@ -740,24 +846,30 @@ handle_upload_common(struct vsf_session* p_sess, int is_append)
vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Use PORT or PASV first.");
return;
}
p_filename = &p_sess->ftp_arg_str;
if (is_unique)
{
get_unique_filename(&s_filename, p_filename);
p_filename = &s_filename;
}
/* NOTE - actual file permissions will be governed by the tunable umask */
/* XXX - do we care about race between create and chown() of anonymous
* upload?
*/
if (p_sess->is_anonymous && !tunable_anon_other_write_enable)
if (is_unique || (p_sess->is_anonymous && !tunable_anon_other_write_enable))
{
new_file_fd = str_create(&p_sess->ftp_arg_str);
new_file_fd = str_create(p_filename);
}
else
{
/* For non-anonymous, allow open() to overwrite or append existing files */
if (!is_append && offset == 0)
{
new_file_fd = str_create_overwrite(&p_sess->ftp_arg_str);
new_file_fd = str_create_overwrite(p_filename);
}
else
{
new_file_fd = str_create_append(&p_sess->ftp_arg_str);
new_file_fd = str_create_append(p_filename);
}
}
if (vsf_sysutil_retval_is_error(new_file_fd))
@ -768,6 +880,7 @@ handle_upload_common(struct vsf_session* p_sess, int is_append)
/* Are we required to chown() this file for security? */
if (p_sess->is_anonymous && tunable_chown_uploads)
{
vsf_sysutil_fchmod(new_file_fd, 0600);
if (tunable_one_process_model)
{
vsf_one_process_chown_upload(p_sess, new_file_fd);
@ -787,8 +900,18 @@ handle_upload_common(struct vsf_session* p_sess, int is_append)
{
goto port_pasv_cleanup_out;
}
vsf_cmdio_write(p_sess, FTP_DATACONN,
"Ok to send data.");
if (is_unique)
{
struct mystr resp_str = INIT_MYSTR;
str_alloc_text(&resp_str, "FILE: ");
str_append_str(&resp_str, p_filename);
vsf_cmdio_write_str(p_sess, FTP_DATACONN, &resp_str);
str_free(&resp_str);
}
else
{
vsf_cmdio_write(p_sess, FTP_DATACONN, "Ok to send data.");
}
vsf_log_start_entry(p_sess, kVSFLogEntryUpload);
str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
prepend_path_to_filename(&p_sess->log_str);
@ -1079,6 +1202,7 @@ handle_site(struct vsf_session* p_sess)
str_split_char(&p_sess->ftp_arg_str, &s_site_args_str, ' ');
str_upper(&p_sess->ftp_arg_str);
if (tunable_write_enable &&
tunable_chmod_enable &&
str_equal_text(&p_sess->ftp_arg_str, "CHMOD"))
{
handle_site_chmod(p_sess, &s_site_args_str);
@ -1087,6 +1211,10 @@ handle_site(struct vsf_session* p_sess)
{
handle_site_umask(p_sess, &s_site_args_str);
}
else if (str_equal_text(&p_sess->ftp_arg_str, "HELP"))
{
vsf_cmdio_write(p_sess, FTP_SITEHELP, "CHMOD UMASK HELP");
}
else
{
vsf_cmdio_write(p_sess, FTP_BADCMD, "Unknown SITE command.");
@ -1149,7 +1277,7 @@ handle_site_umask(struct vsf_session* p_sess, struct mystr* p_arg_str)
static void
handle_appe(struct vsf_session* p_sess)
{
handle_upload_common(p_sess, 1);
handle_upload_common(p_sess, 1, 0);
}
static void
@ -1172,3 +1300,112 @@ handle_mdtm(struct vsf_session* p_sess)
}
}
static void
handle_eprt(struct vsf_session* p_sess)
{
static struct mystr s_part1_str;
static struct mystr s_part2_str;
int proto;
int port;
const unsigned char* p_raw_addr;
int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr);
port_cleanup(p_sess);
pasv_cleanup(p_sess);
str_copy(&s_part1_str, &p_sess->ftp_arg_str);
str_split_char(&s_part1_str, &s_part2_str, '|');
if (!str_isempty(&s_part1_str))
{
goto bad_eprt;
}
/* Split out the protocol and check it */
str_split_char(&s_part2_str, &s_part1_str, '|');
proto = str_atoi(&s_part2_str);
if (!is_ipv6 || proto != 2)
{
vsf_cmdio_write(p_sess, FTP_BADCMD, "Bad EPRT protocol.");
return;
}
/* Split out address and parse it */
str_split_char(&s_part1_str, &s_part2_str, '|');
p_raw_addr = vsf_sysutil_parse_ipv6(&s_part1_str);
if (!p_raw_addr)
{
goto bad_eprt;
}
/* Split out port and parse it */
str_split_char(&s_part2_str, &s_part1_str, '|');
if (!str_isempty(&s_part1_str) || str_isempty(&s_part2_str))
{
goto bad_eprt;
}
port = str_atoi(&s_part2_str);
if (port < 0 || port > 65535)
{
goto bad_eprt;
}
vsf_sysutil_sockaddr_alloc_ipv6(&p_sess->p_port_sockaddr);
vsf_sysutil_sockaddr_set_ipv6addr(p_sess->p_port_sockaddr, p_raw_addr);
vsf_sysutil_sockaddr_set_port(p_sess->p_port_sockaddr, (unsigned short)port);
/* SECURITY:
* 1) Reject requests not connecting to the control socket IP
* 2) Reject connects to privileged ports
*/
if (!tunable_port_promiscuous)
{
if (!vsf_sysutil_sockaddr_addr_equal(p_sess->p_remote_addr,
p_sess->p_port_sockaddr) ||
vsf_sysutil_is_port_reserved(port))
{
vsf_cmdio_write(p_sess, FTP_BADCMD, "Illegal EPRT command.");
port_cleanup(p_sess);
return;
}
}
vsf_cmdio_write(p_sess, FTP_EPRTOK,
"EPRT command successful. Consider using EPSV.");
return;
bad_eprt:
vsf_cmdio_write(p_sess, FTP_BADCMD, "Bad EPRT command.");
}
static void
handle_help(struct vsf_session* p_sess)
{
vsf_cmdio_write_hyphen(p_sess, FTP_HELP,
"The following commands are implemented.");
vsf_cmdio_write_raw(p_sess,
" ABOR APPE CDUP CWD DELE EPRT EPSV HELP LIST MDTM MKD MODE NLST NOOP\r\n");
vsf_cmdio_write_raw(p_sess,
" PASS PASV PORT PWD QUIT REST RETR RMD RNFR RNTO SITE SIZE STOR STOU\r\n");
vsf_cmdio_write_raw(p_sess,
" STRU SYST TYPE USER XCUP XCWD XMKD XPWD XRMD\r\n");
vsf_cmdio_write(p_sess, FTP_HELP, "Help OK.");
}
static void
handle_stou(struct vsf_session* p_sess)
{
handle_upload_common(p_sess, 0, 1);
}
static void
get_unique_filename(struct mystr* p_outstr, const struct mystr* p_base_str)
{
/* Use silly wu-ftpd algorithm for compatibility */
static struct vsf_sysutil_statbuf* s_p_statbuf;
unsigned int suffix = 1;
int retval;
while (1)
{
str_copy(p_outstr, p_base_str);
str_append_char(p_outstr, '.');
str_append_ulong(p_outstr, suffix);
retval = str_stat(p_outstr, &s_p_statbuf);
if (vsf_sysutil_retval_is_error(retval))
{
return;
}
++suffix;
}
}

View File

@ -41,8 +41,11 @@ vsf_priv_parent_postlogin(struct vsf_session* p_sess)
static void
process_post_login_req(struct vsf_session* p_sess)
{
char cmd;
vsf_sysutil_unblock_sig(kVSFSysUtilSigCHLD);
/* Blocks */
char cmd = priv_sock_get_cmd(p_sess);
cmd = priv_sock_get_cmd(p_sess);
vsf_sysutil_block_sig(kVSFSysUtilSigCHLD);
if (tunable_chown_uploads && cmd == PRIV_SOCK_CHOWN)
{
cmd_process_chown(p_sess);
@ -53,7 +56,7 @@ process_post_login_req(struct vsf_session* p_sess)
}
else
{
die("bad post login request");
die("bad request in process_post_login_req");
}
}
@ -67,8 +70,14 @@ minimize_privilege(struct vsf_session* p_sess)
* In some happy circumstances, we can exit and be done with root
* altogether.
*/
if (!(tunable_chown_uploads && p_sess->is_anonymous) &&
!tunable_connect_from_port_20)
if (!p_sess->is_anonymous && tunable_session_support)
{
/* Need to hang around to update logs, utmp, wtmp etc. on logout.
* Need to keep privs to do this. */
return;
}
if (!tunable_chown_uploads && !tunable_connect_from_port_20 &&
!tunable_max_per_ip && !tunable_max_clients)
{
/* Cool. We're outta here. */
vsf_sysutil_exit(0);
@ -79,7 +88,7 @@ minimize_privilege(struct vsf_session* p_sess)
struct mystr dir_str = INIT_MYSTR;
str_alloc_text(&user_str, tunable_nopriv_user);
str_alloc_text(&dir_str, tunable_secure_chroot_dir);
if (tunable_chown_uploads && p_sess->is_anonymous)
if (tunable_chown_uploads)
{
caps |= kCapabilityCAP_CHOWN;
}

View File

@ -74,6 +74,7 @@ emit_greeting(struct vsf_session* p_sess)
vsf_cmdio_write_noblock(p_sess, FTP_IP_DENY, "Service not available.");
vsf_sysutil_exit(0);
}
vsf_log_line(p_sess, kVSFLogEntryConnection, &str_log_line);
if (!str_isempty(&p_sess->banner_str))
{
vsf_banner_write(p_sess, &p_sess->banner_str, FTP_GREET);

View File

@ -35,15 +35,11 @@ int
vsf_privop_get_ftp_port_sock(struct vsf_session* p_sess)
{
static struct vsf_sysutil_sockaddr* p_sockaddr;
struct vsf_sysutil_ipv4port the_port;
int retval;
int s = vsf_sysutil_get_ipv4_sock();
int s = vsf_sysutil_get_ipsock(p_sess->p_local_addr);
vsf_sysutil_activate_reuseaddr(s);
vsf_sysutil_sockaddr_alloc_ipv4(&p_sockaddr);
the_port = vsf_sysutil_ipv4port_from_int(tunable_ftp_data_port);
vsf_sysutil_sockaddr_set_port(p_sockaddr, the_port);
vsf_sysutil_sockaddr_set_ipaddr(p_sockaddr,
vsf_sysutil_sockaddr_get_ipaddr(p_sess->p_local_addr));
vsf_sysutil_sockaddr_clone(&p_sockaddr, p_sess->p_local_addr);
vsf_sysutil_sockaddr_set_port(p_sockaddr, tunable_ftp_data_port);
retval = vsf_sysutil_bind(s, p_sockaddr);
if (retval != 0)
{
@ -57,12 +53,19 @@ vsf_privop_do_file_chown(struct vsf_session* p_sess, int fd)
{
static struct vsf_sysutil_statbuf* s_p_statbuf;
vsf_sysutil_fstat(fd, &s_p_statbuf);
/* Do nothing if it is already owned by the desired user. */
if (vsf_sysutil_statbuf_get_uid(s_p_statbuf) ==
p_sess->anon_upload_chown_uid)
{
return;
}
/* Drop it like a hot potato unless it's a regular file owned by
* the the anonymous ftp user
*/
if (p_sess->anon_ftp_uid == -1 || p_sess->anon_upload_chown_uid == -1 ||
if (p_sess->anon_upload_chown_uid == -1 ||
!vsf_sysutil_statbuf_is_regfile(s_p_statbuf) ||
vsf_sysutil_statbuf_get_uid(s_p_statbuf) != p_sess->anon_ftp_uid)
(vsf_sysutil_statbuf_get_uid(s_p_statbuf) != p_sess->anon_ftp_uid &&
vsf_sysutil_statbuf_get_uid(s_p_statbuf) != p_sess->guest_user_uid))
{
die("invalid fd in cmd_process_chown");
}
@ -144,7 +147,6 @@ handle_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
{
result = handle_local_login(p_sess, p_user_str, p_pass_str);
}
str_free(&p_sess->banned_email_str);
return result;
}
}
@ -173,6 +175,7 @@ handle_anonymous_login(struct vsf_session* p_sess,
setup_username_globals(p_sess, &ftp_username_str);
str_free(&ftp_username_str);
}
str_free(&p_sess->banned_email_str);
return kVSFLoginAnon;
}

View File

@ -26,10 +26,7 @@ vsf_secutil_change_credentials(const struct mystr* p_user_str,
p_user = str_getpwnam(p_user_str);
if (p_user == 0)
{
struct mystr death_str = INIT_MYSTR;
str_alloc_text(&death_str, "str_getpwnam: ");
str_append_str(&death_str, p_user_str);
die(str_getbuf(&death_str));
die2("cannot locate user entry:", str_getbuf(p_user_str));
}
{
struct mystr dir_str = INIT_MYSTR;
@ -69,7 +66,11 @@ vsf_secutil_change_credentials(const struct mystr* p_user_str,
vsf_sysutil_seteuid(p_user);
}
retval = str_chdir(&dir_str);
if (retval == 0 && p_ext_dir_str && !str_isempty(p_ext_dir_str))
if (retval != 0)
{
die2("cannot change directory:", str_getbuf(&dir_str));
}
if (p_ext_dir_str && !str_isempty(p_ext_dir_str))
{
retval = str_chdir(p_ext_dir_str);
/* Failure on the extra directory is OK as long as we're not in
@ -82,7 +83,7 @@ vsf_secutil_change_credentials(const struct mystr* p_user_str,
}
if (retval != 0)
{
die("chdir");
die2("cannot change directory:", str_getbuf(p_ext_dir_str));
}
if (options & VSF_SECUTIL_OPTION_CHANGE_EUID)
{

View File

@ -40,12 +40,14 @@ struct vsf_session
int is_ascii;
struct mystr rnfr_filename_str;
int abor_received;
int epsv_all;
/* Details of FTP session state */
struct mystr_list* p_visited_dir_list;
/* Details of userids which are interesting to us */
int anon_ftp_uid;
int guest_user_uid;
int anon_upload_chown_uid;
/* Things we need to cache before we chroot() */
@ -55,7 +57,8 @@ struct vsf_session
int tcp_wrapper_ok;
/* Logging related details */
int log_fd;
int xferlog_fd;
int vsftpd_log_fd;
struct mystr remote_ip_str;
unsigned long log_type;
long log_start_sec;

View File

@ -16,19 +16,19 @@
#include "utility.h"
#include "defs.h"
#include "hash.h"
#include "str.h"
#include "ipv6parse.h"
static int s_reload_needed;
static unsigned int s_children;
static struct hash* s_p_ip_count_hash;
static struct hash* s_p_pid_ip_hash;
static unsigned int s_ipaddr_size;
static void handle_sigchld(void* p_private);
static void handle_sigchld(int duff);
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);
static unsigned int handle_ip_count(void* p_raw_addr);
static void drop_ip_count(void* p_raw_addr);
static unsigned int hash_ip(unsigned int buckets, void* p_key);
static unsigned int hash_pid(unsigned int buckets, void* p_key);
@ -36,65 +36,118 @@ 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 listen_sock = -1;
int retval;
s_p_ip_count_hash = hash_alloc(256, sizeof(struct vsf_sysutil_ipv4addr),
s_ipaddr_size = vsf_sysutil_get_ipaddr_size();
if (tunable_listen && tunable_listen_ipv6)
{
die("run two copies of vsftpd for IPv4 and IPv6");
}
if (tunable_background)
{
int forkret = vsf_sysutil_fork();
if (forkret > 0)
{
/* Parent, just exit */
vsf_sysutil_exit(0);
}
vsf_sysutil_make_session_leader();
}
if (tunable_listen)
{
listen_sock = vsf_sysutil_get_ipv4_sock();
}
else
{
listen_sock = vsf_sysutil_get_ipv6_sock();
}
vsf_sysutil_activate_reuseaddr(listen_sock);
s_p_ip_count_hash = hash_alloc(256, s_ipaddr_size,
sizeof(unsigned int), hash_ip);
s_p_pid_ip_hash = hash_alloc(256, sizeof(int),
sizeof(struct vsf_sysutil_ipv4addr), hash_pid);
s_ipaddr_size, hash_pid);
if (tunable_setproctitle_enable)
{
vsf_sysutil_setproctitle("LISTENER");
}
vsf_sysutil_install_sighandler(kVSFSysUtilSigCHLD, handle_sigchld, 0);
vsf_sysutil_install_async_sighandler(kVSFSysUtilSigCHLD, handle_sigchld);
vsf_sysutil_install_async_sighandler(kVSFSysUtilSigHUP, handle_sighup);
vsf_sysutil_activate_reuseaddr(listen_sock);
vsf_sysutil_sockaddr_alloc_ipv4(&p_sockaddr);
vsf_sysutil_sockaddr_set_port(
p_sockaddr, vsf_sysutil_ipv4port_from_int(tunable_listen_port));
if (!tunable_listen_address ||
vsf_sysutil_inet_aton(tunable_listen_address, &listen_ipaddr) == 0)
if (tunable_listen)
{
listen_ipaddr = vsf_sysutil_sockaddr_get_any();
struct vsf_sysutil_sockaddr* p_sockaddr = 0;
vsf_sysutil_sockaddr_alloc_ipv4(&p_sockaddr);
vsf_sysutil_sockaddr_set_port(p_sockaddr, tunable_listen_port);
if (!tunable_listen_address)
{
vsf_sysutil_sockaddr_set_any(p_sockaddr);
}
else
{
if (!vsf_sysutil_inet_aton(tunable_listen_address, p_sockaddr))
{
die2("bad listen_address: ", tunable_listen_address);
}
}
retval = vsf_sysutil_bind(listen_sock, p_sockaddr);
vsf_sysutil_free(p_sockaddr);
if (vsf_sysutil_retval_is_error(retval))
{
die("could not bind listening IPv4 socket");
}
}
vsf_sysutil_sockaddr_set_ipaddr(p_sockaddr, listen_ipaddr);
retval = vsf_sysutil_bind(listen_sock, p_sockaddr);
vsf_sysutil_free(p_sockaddr);
if (vsf_sysutil_retval_is_error(retval))
else
{
die("could not bind listening socket");
struct vsf_sysutil_sockaddr* p_sockaddr = 0;
vsf_sysutil_sockaddr_alloc_ipv6(&p_sockaddr);
vsf_sysutil_sockaddr_set_port(p_sockaddr, tunable_listen_port);
if (!tunable_listen_address6)
{
vsf_sysutil_sockaddr_set_any(p_sockaddr);
}
else
{
struct mystr addr_str = INIT_MYSTR;
const unsigned char* p_raw_addr;
str_alloc_text(&addr_str, tunable_listen_address6);
p_raw_addr = vsf_sysutil_parse_ipv6(&addr_str);
str_free(&addr_str);
if (!p_raw_addr)
{
die2("bad listen_address6: ", tunable_listen_address6);
}
vsf_sysutil_sockaddr_set_ipv6addr(p_sockaddr, p_raw_addr);
}
retval = vsf_sysutil_bind(listen_sock, p_sockaddr);
vsf_sysutil_free(p_sockaddr);
if (vsf_sysutil_retval_is_error(retval))
{
die("could not bind listening IPv6 socket");
}
}
vsf_sysutil_listen(listen_sock, VSFTP_LISTEN_BACKLOG);
while (1)
{
struct vsf_client_launch child_info;
static struct vsf_sysutil_sockaddr* p_accept_addr;
void* p_raw_addr;
int new_child;
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;
do_reload();
}
int new_client_sock;
vsf_sysutil_unblock_sig(kVSFSysUtilSigCHLD);
vsf_sysutil_unblock_sig(kVSFSysUtilSigHUP);
new_client_sock = vsf_sysutil_accept_timeout(
listen_sock, &p_accept_addr, 0);
vsf_sysutil_block_sig(kVSFSysUtilSigCHLD);
vsf_sysutil_block_sig(kVSFSysUtilSigHUP);
if (vsf_sysutil_retval_is_error(new_client_sock))
{
continue;
}
ip_addr = vsf_sysutil_sockaddr_get_ipaddr(p_accept_addr);
++s_children;
child_info.num_children = s_children;
child_info.num_this_ip = handle_ip_count(&ip_addr);
child_info.num_this_ip = 0;
p_raw_addr = vsf_sysutil_sockaddr_get_raw_addr(p_accept_addr);
child_info.num_this_ip = handle_ip_count(p_raw_addr);
new_child = vsf_sysutil_fork_failok();
if (new_child != 0)
{
@ -102,13 +155,13 @@ vsf_standalone_main(void)
vsf_sysutil_close(new_client_sock);
if (new_child > 0)
{
hash_add_entry(s_p_pid_ip_hash, (void*)&new_child, (void*)&ip_addr);
hash_add_entry(s_p_pid_ip_hash, (void*)&new_child, p_raw_addr);
}
else
{
/* fork() failed, clear up! */
--s_children;
drop_ip_count(&ip_addr);
drop_ip_count(p_raw_addr);
}
/* Fall through to while() loop and accept() again */
}
@ -139,11 +192,11 @@ prepare_child(int new_client_sock)
}
static void
drop_ip_count(struct vsf_sysutil_ipv4addr* p_ip)
drop_ip_count(void* p_raw_addr)
{
unsigned int count;
unsigned int* p_count =
(unsigned int*)hash_lookup_entry(s_p_ip_count_hash, (void*)p_ip);
(unsigned int*)hash_lookup_entry(s_p_ip_count_hash, p_raw_addr);
if (!p_count)
{
bug("IP address missing from hash");
@ -157,25 +210,25 @@ drop_ip_count(struct vsf_sysutil_ipv4addr* p_ip)
*p_count = count;
if (!count)
{
hash_free_entry(s_p_ip_count_hash, (void*)p_ip);
hash_free_entry(s_p_ip_count_hash, p_raw_addr);
}
}
static void
handle_sigchld(void* p_private)
handle_sigchld(int duff)
{
unsigned int reap_one = 1;
(void) p_private;
(void) duff;
while (reap_one)
{
reap_one = (unsigned int)vsf_sysutil_wait_reap_one();
if (reap_one)
{
struct vsf_sysutil_ipv4addr* p_ip;
struct vsf_sysutil_ipaddr* p_ip;
/* Account total number of instances */
--s_children;
/* Account per-IP limit */
p_ip = (struct vsf_sysutil_ipv4addr*)
p_ip = (struct vsf_sysutil_ipaddr*)
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);
@ -186,16 +239,7 @@ handle_sigchld(void* p_private)
static void
handle_sighup(int duff)
{
/* WARNING - async handler. Must not call anything which might have
* re-entrancy issues
*/
(void) duff;
s_reload_needed = 1;
}
static void
do_reload(void)
{
/* We don't crash the out the listener if an invalid config was added */
vsf_parseconf_load_file(0, 0);
}
@ -203,11 +247,19 @@ do_reload(void)
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];
const unsigned char* p_raw_ip = (const unsigned char*)p_key;
unsigned int val = 0;
int shift = 24;
unsigned int i;
for (i = 0; i < s_ipaddr_size; ++i)
{
val ^= p_raw_ip[i] << shift;
shift -= 8;
if (shift < 0)
{
shift = 24;
}
}
return val % buckets;
}
@ -219,15 +271,15 @@ hash_pid(unsigned int buckets, void* p_key)
}
static unsigned int
handle_ip_count(struct vsf_sysutil_ipv4addr* p_accept_addr)
handle_ip_count(void* p_ipaddr)
{
unsigned int* p_count =
(unsigned int*)hash_lookup_entry(s_p_ip_count_hash, (void*)p_accept_addr);
(unsigned int*)hash_lookup_entry(s_p_ip_count_hash, p_ipaddr);
unsigned int count;
if (!p_count)
{
count = 1;
hash_add_entry(s_p_ip_count_hash, (void*)p_accept_addr, (void*)&count);
hash_add_entry(s_p_ip_count_hash, p_ipaddr, (void*)&count);
}
else
{

7
str.c
View File

@ -373,6 +373,7 @@ str_split_text_common(struct mystr* p_src, struct mystr* p_rhs,
{
struct str_locate_result locate_result;
unsigned int indexx;
unsigned int search_len = vsf_sysutil_strlen(p_text);
if (is_reverse)
{
locate_result = str_locate_text_reverse(p_src, p_text);
@ -388,13 +389,13 @@ str_split_text_common(struct mystr* p_src, struct mystr* p_rhs,
return;
}
indexx = locate_result.index;
if (indexx + vsf_sysutil_strlen(p_text) > p_src->len)
if (indexx + search_len > p_src->len)
{
bug("indexx invalid in str_split_text");
}
/* Build rhs */
private_str_alloc_memchunk(p_rhs, p_src->p_buf + indexx + 1,
p_src->len - indexx - 1);
private_str_alloc_memchunk(p_rhs, p_src->p_buf + indexx + search_len,
p_src->len - indexx - search_len);
/* Build lhs */
str_trunc(p_src, indexx);
}

View File

@ -28,6 +28,7 @@
#include <sys/param.h>
#include <sys/uio.h>
/* Configuration.. here are the possibilities */
#undef VSF_SYSDEP_HAVE_CAPABILITIES
#undef VSF_SYSDEP_HAVE_SETKEEPCAPS
@ -43,6 +44,10 @@
#define VSF_SYSDEP_HAVE_SHADOW
#define VSF_SYSDEP_HAVE_USERSHELL
#define VSF_SYSDEP_HAVE_LIBCAP
#define VSF_SYSDEP_HAVE_UTMPX
#define __USE_GNU
#include <utmpx.h>
/* BEGIN config */
#if defined(__linux__) && !defined(__ia64__) && !defined(__s390__)
@ -110,6 +115,7 @@
#include <shadow.h>
#include <pwd.h>
#include <unistd.h>
#include <crypt.h>
#endif
/* Prefer libcap based capabilities over raw syscall capabilities */
@ -161,6 +167,9 @@ static int do_sendfile(const int out_fd, const int in_fd,
unsigned int num_send, filesize_t start_pos);
static void vsf_sysutil_setproctitle_internal(const char* p_text);
static struct mystr s_proctitle_prefix_str;
static void vsf_insert_uwtmp(const struct mystr* p_user_str,
const struct mystr* p_host_str);
static void vsf_remove_uwtmp(void);
#ifndef VSF_SYSDEP_HAVE_PAM
int
@ -230,9 +239,11 @@ vsf_sysdep_check_auth(const struct mystr* p_user_str,
#else /* VSF_SYSDEP_HAVE_PAM */
static pam_handle_t* s_pamh;
static struct mystr s_pword_str;
static int pam_conv_func(int nmsg, const struct pam_message** p_msg,
struct pam_response** p_reply, void* p_addata);
static void vsf_auth_shutdown(void);
int
vsf_sysdep_check_auth(const struct mystr* p_user_str,
@ -240,55 +251,93 @@ vsf_sysdep_check_auth(const struct mystr* p_user_str,
const struct mystr* p_remote_host)
{
int retval;
pam_handle_t* pamh = 0;
struct pam_conv the_conv =
{
&pam_conv_func,
0
};
if (s_pamh != 0)
{
bug("vsf_sysdep_check_auth");
}
str_copy(&s_pword_str, p_pass_str);
retval = pam_start(tunable_pam_service_name,
str_getbuf(p_user_str), &the_conv, &pamh);
str_getbuf(p_user_str), &the_conv, &s_pamh);
if (retval != PAM_SUCCESS)
{
pam_end(pamh, 0);
s_pamh = 0;
return 0;
}
#ifdef PAM_RHOST
retval = pam_set_item(pamh, PAM_RHOST, str_getbuf(p_remote_host));
#ifdef PAM_RHOST
retval = pam_set_item(s_pamh, PAM_RHOST, str_getbuf(p_remote_host));
if (retval != PAM_SUCCESS)
{
pam_end(pamh, 0);
(void) pam_end(s_pamh, 0);
s_pamh = 0;
return 0;
}
#endif
retval = pam_authenticate(pamh, 0);
#endif
retval = pam_authenticate(s_pamh, 0);
if (retval != PAM_SUCCESS)
{
pam_end(pamh, 0);
(void) pam_end(s_pamh, 0);
s_pamh = 0;
return 0;
}
retval = pam_acct_mgmt(pamh, 0);
retval = pam_acct_mgmt(s_pamh, 0);
if (retval != PAM_SUCCESS)
{
pam_end(pamh, 0);
(void) pam_end(s_pamh, 0);
s_pamh = 0;
return 0;
}
retval = pam_setcred(pamh, PAM_ESTABLISH_CRED);
retval = pam_setcred(s_pamh, PAM_ESTABLISH_CRED);
if (retval != PAM_SUCCESS)
{
pam_end(pamh, 0);
(void) pam_end(s_pamh, 0);
s_pamh = 0;
return 0;
}
retval = pam_end(pamh, PAM_SUCCESS);
if (!tunable_session_support)
{
/* You're in already! */
(void) pam_end(s_pamh, 0);
s_pamh = 0;
return 1;
}
/* Must do this BEFORE opening a session for pam_limits to count us */
vsf_insert_uwtmp(p_user_str, p_remote_host);
retval = pam_open_session(s_pamh, 0);
if (retval != PAM_SUCCESS)
{
vsf_remove_uwtmp();
(void) pam_setcred(s_pamh, PAM_DELETE_CRED);
(void) pam_end(s_pamh, 0);
s_pamh = 0;
return 0;
}
/* It worked, cool */
/* We MUST ensure the PAM session, utmp, wtmp etc. are cleaned up, however
* we exit.
*/
vsf_sysutil_set_exit_func(vsf_auth_shutdown);
/* You're in dude */
return 1;
}
static void
vsf_auth_shutdown(void)
{
if (s_pamh == 0)
{
bug("vsf_auth_shutdown");
}
(void) pam_close_session(s_pamh, 0);
(void) pam_setcred(s_pamh, PAM_DELETE_CRED);
(void) pam_end(s_pamh, 0);
s_pamh = 0;
vsf_remove_uwtmp();
}
static int
pam_conv_func(int nmsg, const struct pam_message** p_msg,
struct pam_response** p_reply, void* p_addata)
@ -602,8 +651,13 @@ static int do_sendfile(const int out_fd, const int in_fd,
s_runtime_sendfile_works = 1;
}
}
if (!vsf_sysutil_retval_is_error(retval))
{
return retval;
}
if (s_runtime_sendfile_works &&
vsf_sysutil_get_error() != kVSFSysUtilErrINVAL)
vsf_sysutil_get_error() != kVSFSysUtilErrINVAL &&
vsf_sysutil_get_error() != kVSFSysUtilErrOPNOTSUPP)
{
return retval;
}
@ -977,3 +1031,87 @@ vsf_sysutil_recv_fd(int sock_fd)
#endif /* !VSF_SYSDEP_NEED_OLD_FD_PASSING */
#ifndef VSF_SYSDEP_HAVE_UTMPX
static void
vsf_insert_uwtmp(const struct mystr* p_user_str,
const struct mystr* p_host_str)
{
(void) p_user_str;
(void) p_host_str;
}
static void
vsf_remove_uwtmp(void)
{
}
#else /* !VSF_SYSDEP_HAVE_UTMPX */
/* IMHO, the pam_unix module REALLY should be doing this in its SM component */
/* Statics */
static int s_uwtmp_inserted;
static struct utmpx s_utent;
static void
vsf_insert_uwtmp(const struct mystr* p_user_str,
const struct mystr* p_host_str)
{
if (sizeof(s_utent.ut_line) < 16)
{
return;
}
if (s_uwtmp_inserted)
{
bug("vsf_insert_uwtmp");
}
{
struct mystr line_str = INIT_MYSTR;
str_alloc_text(&line_str, "vsftpd:");
str_append_ulong(&line_str, vsf_sysutil_getpid());
if (str_getlen(&line_str) >= sizeof(s_utent.ut_line))
{
str_free(&line_str);
return;
}
vsf_sysutil_strcpy(s_utent.ut_line, str_getbuf(&line_str),
sizeof(s_utent.ut_line));
str_free(&line_str);
}
s_uwtmp_inserted = 1;
s_utent.ut_type = USER_PROCESS;
s_utent.ut_pid = vsf_sysutil_getpid();
vsf_sysutil_strcpy(s_utent.ut_user, str_getbuf(p_user_str),
sizeof(s_utent.ut_user));
vsf_sysutil_strcpy(s_utent.ut_host, str_getbuf(p_host_str),
sizeof(s_utent.ut_host));
vsf_sysutil_update_cached_time();
s_utent.ut_tv.tv_sec = vsf_sysutil_get_cached_time_sec();
setutxent();
(void) pututxline(&s_utent);
endutxent();
updwtmpx(WTMPX_FILE, &s_utent);
}
static void
vsf_remove_uwtmp(void)
{
if (!s_uwtmp_inserted)
{
return;
}
s_uwtmp_inserted = 0;
s_utent.ut_type = DEAD_PROCESS;
vsf_sysutil_memclr(s_utent.ut_user, sizeof(s_utent.ut_user));
vsf_sysutil_memclr(s_utent.ut_host, sizeof(s_utent.ut_host));
s_utent.ut_tv.tv_sec = 0;
setutxent();
(void) pututxline(&s_utent);
endutxent();
vsf_sysutil_update_cached_time();
s_utent.ut_tv.tv_sec = vsf_sysutil_get_cached_time_sec();
updwtmpx(WTMPX_FILE, &s_utent);
}
#endif /* !VSF_SYSDEP_HAVE_UTMPX */

View File

@ -168,3 +168,9 @@ str_getpwnam(const struct mystr* p_user_str)
return vsf_sysutil_getpwnam(str_getbuf(p_user_str));
}
void
str_syslog(const struct mystr* p_str, int severe)
{
vsf_sysutil_syslog(str_getbuf(p_str), severe);
}

View File

@ -33,5 +33,7 @@ void str_next_dirent(struct mystr* p_filename_str,
struct vsf_sysutil_user* str_getpwnam(const struct mystr* p_user_str);
void str_syslog(const struct mystr* p_str, int severe);
#endif /* VSF_SYSSTR_H */

569
sysutil.c
View File

@ -47,6 +47,7 @@
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <limits.h>
#include <syslog.h>
/* Private variables to this file */
/* Current umask() */
@ -55,6 +56,8 @@ static unsigned int s_current_umask;
static struct timeval s_current_time;
/* Current pid */
static int s_current_pid = -1;
/* Exit function */
static exitfunc_t s_exit_func;
/* Our internal signal handling implementation details */
static struct vsf_sysutil_sig_details
@ -69,6 +72,16 @@ static vsf_context_io_t s_io_handler;
static void* s_p_io_handler_private;
static int s_io_handler_running;
struct vsf_sysutil_sockaddr
{
union
{
struct sockaddr u_sockaddr;
struct sockaddr_in u_sockaddr_in;
struct sockaddr_in6 u_sockaddr_in6;
} u;
};
/* File locals */
static void vsf_sysutil_common_sighandler(int signum);
static int vsf_sysutil_translate_sig(const enum EVSFSysUtilSignal sig);
@ -78,6 +91,8 @@ static int vsf_sysutil_translate_memprot(
static int vsf_sysutil_translate_openmode(
const enum EVSFSysUtilOpenMode mode);
static void vsf_sysutil_alloc_statbuf(struct vsf_sysutil_statbuf** p_ptr);
static void vsf_sysutil_sockaddr_alloc(
struct vsf_sysutil_sockaddr** p_sockptr);
static void
vsf_sysutil_common_sighandler(int signum)
@ -201,6 +216,7 @@ vsf_sysutil_install_async_sighandler(const enum EVSFSysUtilSignal sig,
int realsig = vsf_sysutil_translate_sig(sig);
s_sig_details[realsig].p_private = NULL;
s_sig_details[realsig].sync_sig_handler = NULL;
vsf_sysutil_block_sig(sig);
vsf_sysutil_set_sighandler(realsig, handler);
}
@ -211,10 +227,10 @@ vsf_sysutil_set_sighandler(int sig, void (*p_handlefunc)(int))
struct sigaction sigact;
vsf_sysutil_memclr(&sigact, sizeof(sigact));
sigact.sa_handler = p_handlefunc;
retval = sigemptyset(&sigact.sa_mask);
retval = sigfillset(&sigact.sa_mask);
if (retval != 0)
{
die("sigemptyset");
die("sigfillset");
}
retval = sigaction(sig, &sigact, NULL);
if (retval != 0)
@ -223,6 +239,51 @@ vsf_sysutil_set_sighandler(int sig, void (*p_handlefunc)(int))
}
}
void
vsf_sysutil_block_sig(const enum EVSFSysUtilSignal sig)
{
sigset_t sset;
int retval;
int realsig = vsf_sysutil_translate_sig(sig);
retval = sigemptyset(&sset);
if (retval != 0)
{
die("sigemptyset");
}
retval = sigaddset(&sset, realsig);
if (retval != 0)
{
die("sigaddset");
}
retval = sigprocmask(SIG_BLOCK, &sset, NULL);
if (retval != 0)
{
die("sigprocmask");
}
}
void
vsf_sysutil_unblock_sig(const enum EVSFSysUtilSignal sig)
{
sigset_t sset;
int retval;
int realsig = vsf_sysutil_translate_sig(sig);
retval = sigemptyset(&sset);
if (retval != 0)
{
die("sigemptyset");
}
retval = sigaddset(&sset, realsig);
if (retval != 0)
{
die("sigaddset");
}
retval = sigprocmask(SIG_UNBLOCK, &sset, NULL);
if (retval != 0)
{
die("sigprocmask");
}
}
void
vsf_sysutil_install_io_handler(vsf_context_io_t handler, void* p_private)
{
@ -440,7 +501,15 @@ vsf_sysutil_getpid(void)
int
vsf_sysutil_fork(void)
{
int retval = vsf_sysutil_fork_failok();
/* Child does NOT inherit exit function */
exitfunc_t curr_func = s_exit_func;
int retval;
s_exit_func = 0;
retval = vsf_sysutil_fork_failok();
if (retval != 0)
{
s_exit_func = curr_func;
}
if (retval < 0)
{
die("fork");
@ -459,9 +528,22 @@ vsf_sysutil_fork_failok(void)
return retval;
}
void
vsf_sysutil_set_exit_func(exitfunc_t exitfunc)
{
s_exit_func = exitfunc;
}
void
vsf_sysutil_exit(int exit_code)
{
if (s_exit_func)
{
exitfunc_t curr_func = s_exit_func;
/* Prevent recursion */
s_exit_func = 0;
(*curr_func)();
}
_exit(exit_code);
}
@ -605,18 +687,13 @@ vsf_sysutil_activate_linger(int fd)
}
void
vsf_sysutil_deactivate_linger(int fd)
vsf_sysutil_deactivate_linger_failok(int fd)
{
int retval;
struct linger the_linger;
the_linger.l_onoff = 0;
the_linger.l_linger = 0;
retval = setsockopt(fd, SOL_SOCKET, SO_LINGER, &the_linger,
sizeof(the_linger));
if (retval != 0)
{
die("setsockopt");
}
(void) setsockopt(fd, SOL_SOCKET, SO_LINGER, &the_linger,
sizeof(the_linger));
}
void
@ -762,18 +839,19 @@ vsf_sysutil_octal_to_uint(const char* p_str)
int seen_non_zero_digit = 0;
while (*p_str != '\0')
{
if (!isdigit(*p_str) || *p_str > '7')
int digit = *p_str;
if (!isdigit(digit) || digit > '7')
{
break;
}
if (*p_str != '0')
if (digit != '0')
{
seen_non_zero_digit = 1;
}
if (seen_non_zero_digit)
{
result <<= 3;
result += ( *p_str - '0' );
result += (digit - '0');
}
p_str++;
}
@ -915,6 +993,17 @@ vsf_sysutil_memcpy(void* p_dest, const void* p_src, const unsigned int size)
memcpy(p_dest, p_src, size);
}
void
vsf_sysutil_strcpy(char* p_dest, const char* p_src, unsigned int maxsize)
{
if (maxsize == 0)
{
return;
}
strncpy(p_dest, p_src, maxsize);
p_dest[maxsize - 1] = '\0';
}
int
vsf_sysutil_memcmp(const void* p_src1, const void* p_src2, unsigned int size)
{
@ -1325,6 +1414,16 @@ vsf_sysutil_fchown(const int fd, const int uid, const int gid)
}
}
void
vsf_sysutil_fchmod(const int fd, unsigned int mode)
{
mode = mode & 0777;
if (fchmod(fd, mode))
{
die("fchmod");
}
}
int
vsf_sysutil_chmod(const char* p_filename, unsigned int mode)
{
@ -1410,6 +1509,9 @@ vsf_sysutil_get_error(void)
case EINVAL:
retval = kVSFSysUtilErrINVAL;
break;
case EOPNOTSUPP:
retval = kVSFSysUtilErrOPNOTSUPP;
break;
}
return retval;
}
@ -1425,6 +1527,17 @@ vsf_sysutil_get_ipv4_sock(void)
return retval;
}
int
vsf_sysutil_get_ipv6_sock(void)
{
int retval = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (retval < 0)
{
die("socket");
}
return retval;
}
struct vsf_sysutil_socketpair_retval
vsf_sysutil_unix_dgram_socketpair(void)
{
@ -1443,8 +1556,21 @@ vsf_sysutil_unix_dgram_socketpair(void)
int
vsf_sysutil_bind(int fd, const struct vsf_sysutil_sockaddr* p_sockptr)
{
struct sockaddr* p_sockaddr = (struct sockaddr*) p_sockptr;
return bind(fd, p_sockaddr, sizeof(struct sockaddr_in));
const struct sockaddr* p_sockaddr = &p_sockptr->u.u_sockaddr;
int len = 0;
if (p_sockaddr->sa_family == AF_INET)
{
len = sizeof(struct sockaddr_in);
}
else if (p_sockaddr->sa_family == AF_INET6)
{
len = sizeof(struct sockaddr_in6);
}
else
{
die("can only support ipv4 and ipv6 currently");
}
return bind(fd, p_sockaddr, len);
}
void
@ -1461,7 +1587,7 @@ int
vsf_sysutil_accept_timeout(int fd, struct vsf_sysutil_sockaddr** p_sockptr,
unsigned int wait_seconds)
{
struct sockaddr_in remote_addr;
struct vsf_sysutil_sockaddr remote_addr;
int retval;
fd_set accept_fdset;
struct timeval timeout;
@ -1487,7 +1613,7 @@ vsf_sysutil_accept_timeout(int fd, struct vsf_sysutil_sockaddr** p_sockptr,
return -1;
}
}
retval = accept(fd, (struct sockaddr*) &remote_addr, &socklen);
retval = accept(fd, &remote_addr.u.u_sockaddr, &socklen);
vsf_sysutil_check_pending_actions(kVSFSysUtilUnknown, 0, 0);
if (retval < 0)
{
@ -1498,14 +1624,26 @@ vsf_sysutil_accept_timeout(int fd, struct vsf_sysutil_sockaddr** p_sockptr,
{
return -1;
}
if (remote_addr.sin_family != AF_INET)
if (remote_addr.u.u_sockaddr.sa_family != AF_INET &&
remote_addr.u.u_sockaddr.sa_family != AF_INET6)
{
die("can only support ipv4 currently");
die("can only support ipv4 and ipv6 currently");
}
if (p_sockptr)
{
*p_sockptr = vsf_sysutil_malloc(sizeof(remote_addr));
vsf_sysutil_memcpy(*p_sockptr, &remote_addr, sizeof(remote_addr));
vsf_sysutil_sockaddr_alloc(p_sockptr);
if (remote_addr.u.u_sockaddr.sa_family == AF_INET)
{
vsf_sysutil_memclr(&remote_addr.u.u_sockaddr_in.sin_zero,
sizeof(remote_addr.u.u_sockaddr_in.sin_zero));
vsf_sysutil_memcpy(*p_sockptr, &remote_addr.u.u_sockaddr_in,
sizeof(remote_addr.u.u_sockaddr_in));
}
else
{
vsf_sysutil_memcpy(*p_sockptr, &remote_addr.u.u_sockaddr_in6,
sizeof(remote_addr.u.u_sockaddr_in6));
}
}
return retval;
}
@ -1514,14 +1652,26 @@ int
vsf_sysutil_connect_timeout(int fd, const struct vsf_sysutil_sockaddr* p_addr,
unsigned int wait_seconds)
{
const struct sockaddr_in* p_sockaddr = (const struct sockaddr_in*) p_addr;
unsigned int addrlen = sizeof(*p_sockaddr);
const struct sockaddr* p_sockaddr = &p_addr->u.u_sockaddr;
unsigned int addrlen = 0;
int retval;
if (p_sockaddr->sa_family == AF_INET)
{
addrlen = sizeof(p_addr->u.u_sockaddr_in);
}
else if (p_sockaddr->sa_family == AF_INET6)
{
addrlen = sizeof(p_addr->u.u_sockaddr_in6);
}
else
{
die("can only support ipv4 and ipv6 currently");
}
if (wait_seconds > 0)
{
vsf_sysutil_activate_noblock(fd);
}
retval = connect(fd, (const struct sockaddr*)p_sockaddr, addrlen);
retval = connect(fd, p_sockaddr, addrlen);
if (retval < 0 && errno == EINPROGRESS)
{
fd_set connect_fdset;
@ -1561,41 +1711,51 @@ vsf_sysutil_connect_timeout(int fd, const struct vsf_sysutil_sockaddr* p_addr,
void
vsf_sysutil_getsockname(int fd, struct vsf_sysutil_sockaddr** p_sockptr)
{
struct sockaddr_in the_addr;
struct vsf_sysutil_sockaddr the_addr;
int retval;
unsigned int socklen = sizeof(the_addr);
vsf_sysutil_sockaddr_clear(p_sockptr);
retval = getsockname(fd, (struct sockaddr*) &the_addr, &socklen);
retval = getsockname(fd, &the_addr.u.u_sockaddr, &socklen);
if (retval != 0)
{
die("getsockname");
}
if (the_addr.sin_family != AF_INET)
if (the_addr.u.u_sockaddr.sa_family != AF_INET &&
the_addr.u.u_sockaddr.sa_family != AF_INET6)
{
die("can only support ipv4 currently");
die("can only support ipv4 and ipv6 currently");
}
*p_sockptr = vsf_sysutil_malloc(sizeof(the_addr));
vsf_sysutil_memcpy(*p_sockptr, &the_addr, sizeof(the_addr));
vsf_sysutil_sockaddr_alloc(p_sockptr);
if (socklen > sizeof(the_addr))
{
socklen = sizeof(the_addr);
}
vsf_sysutil_memcpy(*p_sockptr, &the_addr, socklen);
}
void
vsf_sysutil_getpeername(int fd, struct vsf_sysutil_sockaddr** p_sockptr)
{
struct sockaddr_in the_addr;
struct vsf_sysutil_sockaddr the_addr;
int retval;
unsigned int socklen = sizeof(the_addr);
vsf_sysutil_sockaddr_clear(p_sockptr);
retval = getpeername(fd, (struct sockaddr*) &the_addr, &socklen);
retval = getpeername(fd, &the_addr.u.u_sockaddr, &socklen);
if (retval != 0)
{
die("getpeername");
}
if (the_addr.sin_family != AF_INET)
if (the_addr.u.u_sockaddr.sa_family != AF_INET &&
the_addr.u.u_sockaddr.sa_family != AF_INET6)
{
die("can only support ipv4 currently");
die("can only support ipv4 and ipv6 currently");
}
*p_sockptr = vsf_sysutil_malloc(sizeof(the_addr));
vsf_sysutil_memcpy(*p_sockptr, &the_addr, sizeof(the_addr));
vsf_sysutil_sockaddr_alloc(p_sockptr);
if (socklen > sizeof(the_addr))
{
socklen = sizeof(the_addr);
}
vsf_sysutil_memcpy(*p_sockptr, &the_addr, socklen);
}
void
@ -1618,77 +1778,264 @@ vsf_sysutil_sockaddr_clear(struct vsf_sysutil_sockaddr** p_sockptr)
}
}
static void
vsf_sysutil_sockaddr_alloc(struct vsf_sysutil_sockaddr** p_sockptr)
{
vsf_sysutil_sockaddr_clear(p_sockptr);
*p_sockptr = vsf_sysutil_malloc(sizeof(**p_sockptr));
vsf_sysutil_memclr(*p_sockptr, sizeof(**p_sockptr));
}
void
vsf_sysutil_sockaddr_alloc_ipv4(struct vsf_sysutil_sockaddr** p_sockptr)
{
struct sockaddr_in new_addr;
vsf_sysutil_sockaddr_clear(p_sockptr);
*p_sockptr = vsf_sysutil_malloc(sizeof(new_addr));
vsf_sysutil_memclr(&new_addr, sizeof(new_addr));
new_addr.sin_family = AF_INET;
vsf_sysutil_memcpy(*p_sockptr, &new_addr, sizeof(new_addr));
vsf_sysutil_sockaddr_alloc(p_sockptr);
(*p_sockptr)->u.u_sockaddr.sa_family = AF_INET;
}
void
vsf_sysutil_sockaddr_set_ipaddr(struct vsf_sysutil_sockaddr* p_sockptr,
struct vsf_sysutil_ipv4addr the_addr)
vsf_sysutil_sockaddr_alloc_ipv6(struct vsf_sysutil_sockaddr** p_sockptr)
{
struct sockaddr_in* p_sockaddr = (struct sockaddr_in*) p_sockptr;
vsf_sysutil_memcpy(&p_sockaddr->sin_addr.s_addr, the_addr.data,
sizeof(p_sockaddr->sin_addr.s_addr));
vsf_sysutil_sockaddr_alloc(p_sockptr);
(*p_sockptr)->u.u_sockaddr.sa_family = AF_INET6;
}
struct vsf_sysutil_ipv4addr
vsf_sysutil_sockaddr_get_ipaddr(const struct vsf_sysutil_sockaddr* p_sockptr)
void
vsf_sysutil_sockaddr_clone(struct vsf_sysutil_sockaddr** p_sockptr,
const struct vsf_sysutil_sockaddr* p_src)
{
struct vsf_sysutil_ipv4addr retval;
const struct sockaddr_in* p_sockaddr = (const struct sockaddr_in*) p_sockptr;
vsf_sysutil_memcpy(retval.data, &p_sockaddr->sin_addr.s_addr,
sizeof(retval.data));
return retval;
struct vsf_sysutil_sockaddr* p_sockaddr = 0;
vsf_sysutil_sockaddr_alloc(p_sockptr);
p_sockaddr = *p_sockptr;
if (p_src->u.u_sockaddr.sa_family == AF_INET)
{
p_sockaddr->u.u_sockaddr.sa_family = AF_INET;
vsf_sysutil_memcpy(&p_sockaddr->u.u_sockaddr_in.sin_addr,
&p_src->u.u_sockaddr_in.sin_addr,
sizeof(p_sockaddr->u.u_sockaddr_in.sin_addr));
}
else if (p_src->u.u_sockaddr.sa_family == AF_INET6)
{
p_sockaddr->u.u_sockaddr.sa_family = AF_INET6;
vsf_sysutil_memcpy(&p_sockaddr->u.u_sockaddr_in6.sin6_addr,
&p_src->u.u_sockaddr_in6.sin6_addr,
sizeof(p_sockaddr->u.u_sockaddr_in6.sin6_addr));
}
else
{
die("can only support ipv4 and ipv6 currently");
}
}
struct vsf_sysutil_ipv4addr
vsf_sysutil_sockaddr_get_any(void)
int
vsf_sysutil_sockaddr_addr_equal(const struct vsf_sysutil_sockaddr* p1,
const struct vsf_sysutil_sockaddr* p2)
{
struct vsf_sysutil_ipv4addr retval;
vsf_sysutil_memclr(&retval, sizeof(retval));
return retval;
int family1 = p1->u.u_sockaddr.sa_family;
int family2 = p2->u.u_sockaddr.sa_family;
if (family1 != family2)
{
if (family1 == AF_INET && family2 == AF_INET6)
{
const void* p_ipv4_addr = vsf_sysutil_sockaddr_ipv6_v4(p2);
if (p_ipv4_addr &&
!vsf_sysutil_memcmp(p_ipv4_addr, &p1->u.u_sockaddr_in.sin_addr,
sizeof(p1->u.u_sockaddr_in.sin_addr)))
{
return 1;
}
}
else if (family1 == AF_INET6 && family2 == AF_INET)
{
const void* p_ipv4_addr = vsf_sysutil_sockaddr_ipv6_v4(p1);
if (p_ipv4_addr &&
!vsf_sysutil_memcmp(p_ipv4_addr, &p2->u.u_sockaddr_in.sin_addr,
sizeof(p2->u.u_sockaddr_in.sin_addr)))
{
return 1;
}
}
return 0;
}
if (family1 == AF_INET)
{
if (vsf_sysutil_memcmp(&p1->u.u_sockaddr_in.sin_addr,
&p2->u.u_sockaddr_in.sin_addr,
sizeof(p1->u.u_sockaddr_in.sin_addr)) == 0)
{
return 1;
}
}
else if (family1 == AF_INET6)
{
if (vsf_sysutil_memcmp(&p1->u.u_sockaddr_in6.sin6_addr,
&p2->u.u_sockaddr_in6.sin6_addr,
sizeof(p1->u.u_sockaddr_in6.sin6_addr)) == 0)
{
return 1;
}
}
return 0;
}
struct vsf_sysutil_ipv4port
vsf_sysutil_sockaddr_get_port(const struct vsf_sysutil_sockaddr* p_sockptr)
int
vsf_sysutil_sockaddr_is_ipv6(const struct vsf_sysutil_sockaddr* p_sockaddr)
{
struct vsf_sysutil_ipv4port retval;
const struct sockaddr_in* p_sockaddr = (const struct sockaddr_in*) p_sockptr;
vsf_sysutil_memcpy(retval.data, &p_sockaddr->sin_port, sizeof(retval.data));
return retval;
if (p_sockaddr->u.u_sockaddr.sa_family == AF_INET6)
{
return 1;
}
return 0;
}
struct vsf_sysutil_ipv4port
vsf_sysutil_ipv4port_from_int(unsigned int port)
int
vsf_sysutil_sockaddr_same_family(const struct vsf_sysutil_sockaddr* p1,
const struct vsf_sysutil_sockaddr* p2)
{
struct vsf_sysutil_ipv4port retval;
unsigned short netorder_port = htons(port);
vsf_sysutil_memcpy(retval.data, &netorder_port, sizeof(retval.data));
return retval;
if (p1->u.u_sockaddr.sa_family == p2->u.u_sockaddr.sa_family)
{
return 1;
}
return 0;
}
void
vsf_sysutil_sockaddr_set_ipv4addr(struct vsf_sysutil_sockaddr* p_sockptr,
const unsigned char* p_raw)
{
if (p_sockptr->u.u_sockaddr.sa_family == AF_INET)
{
vsf_sysutil_memcpy(&p_sockptr->u.u_sockaddr_in.sin_addr, p_raw,
sizeof(p_sockptr->u.u_sockaddr_in.sin_addr));
}
else
{
bug("bad family");
}
}
void
vsf_sysutil_sockaddr_set_ipv6addr(struct vsf_sysutil_sockaddr* p_sockptr,
const unsigned char* p_raw)
{
if (p_sockptr->u.u_sockaddr.sa_family == AF_INET6)
{
vsf_sysutil_memcpy(&p_sockptr->u.u_sockaddr_in6.sin6_addr, p_raw,
sizeof(p_sockptr->u.u_sockaddr_in6.sin6_addr));
}
else
{
bug("bad family");
}
}
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 };
const unsigned char* p_addr_start;
if (p_addr->u.u_sockaddr.sa_family != AF_INET6)
{
return 0;
}
if (vsf_sysutil_memcmp(pattern, &p_addr->u.u_sockaddr_in6.sin6_addr, 12))
{
return 0;
}
p_addr_start = (const unsigned char*)&p_addr->u.u_sockaddr_in6.sin6_addr;
return &p_addr_start[12];
}
void*
vsf_sysutil_sockaddr_get_raw_addr(struct vsf_sysutil_sockaddr* p_sockptr)
{
if (p_sockptr->u.u_sockaddr.sa_family == AF_INET)
{
return &p_sockptr->u.u_sockaddr_in.sin_addr;
}
else if (p_sockptr->u.u_sockaddr.sa_family == AF_INET6)
{
return &p_sockptr->u.u_sockaddr_in6.sin6_addr;
}
else
{
bug("bad family");
}
return 0;
}
unsigned int
vsf_sysutil_get_ipaddr_size(void)
{
struct vsf_sysutil_sockaddr addr;
unsigned int size = sizeof(addr.u.u_sockaddr_in.sin_addr);
unsigned int size2 = sizeof(addr.u.u_sockaddr_in6.sin6_addr);
if (size2 > size)
{
size = size2;
}
return size;
}
int
vsf_sysutil_get_ipsock(const struct vsf_sysutil_sockaddr* p_addr)
{
if (p_addr->u.u_sockaddr.sa_family == AF_INET)
{
return vsf_sysutil_get_ipv4_sock();
}
else if (p_addr->u.u_sockaddr.sa_family == AF_INET6)
{
return vsf_sysutil_get_ipv6_sock();
}
else
{
bug("bad family");
}
return -1;
}
void
vsf_sysutil_sockaddr_set_any(struct vsf_sysutil_sockaddr* p_sockaddr)
{
if (p_sockaddr->u.u_sockaddr.sa_family == AF_INET)
{
vsf_sysutil_memclr(&p_sockaddr->u.u_sockaddr_in.sin_addr,
sizeof(p_sockaddr->u.u_sockaddr_in.sin_addr));
}
else if (p_sockaddr->u.u_sockaddr.sa_family == AF_INET6)
{
vsf_sysutil_memclr(&p_sockaddr->u.u_sockaddr_in6.sin6_addr,
sizeof(p_sockaddr->u.u_sockaddr_in6.sin6_addr));
}
else
{
bug("bad family");
}
}
void
vsf_sysutil_sockaddr_set_port(struct vsf_sysutil_sockaddr* p_sockptr,
struct vsf_sysutil_ipv4port the_port)
unsigned short the_port)
{
struct sockaddr_in* p_sockaddrin = (struct sockaddr_in*) p_sockptr;
vsf_sysutil_memcpy(&p_sockaddrin->sin_port, the_port.data,
sizeof(p_sockaddrin->sin_port));
if (p_sockptr->u.u_sockaddr.sa_family == AF_INET)
{
p_sockptr->u.u_sockaddr_in.sin_port = htons(the_port);
}
else if (p_sockptr->u.u_sockaddr.sa_family == AF_INET6)
{
p_sockptr->u.u_sockaddr_in6.sin6_port = htons(the_port);
}
else
{
bug("bad family");
}
}
int
vsf_sysutil_is_port_reserved(const struct vsf_sysutil_ipv4port the_port)
vsf_sysutil_is_port_reserved(unsigned short the_port)
{
unsigned short netorder_port;
vsf_sysutil_memcpy(&netorder_port, the_port.data, sizeof(netorder_port));
if (ntohs(netorder_port) < IPPORT_RESERVED)
if (the_port < IPPORT_RESERVED)
{
return 1;
}
@ -1696,19 +2043,50 @@ vsf_sysutil_is_port_reserved(const struct vsf_sysutil_ipv4port the_port)
}
const char*
vsf_sysutil_inet_ntoa(const struct vsf_sysutil_sockaddr* p_sockptr)
vsf_sysutil_inet_ntop(const struct vsf_sysutil_sockaddr* p_sockptr)
{
const struct sockaddr_in* p_sockaddr = (const struct sockaddr_in*) p_sockptr;
return inet_ntoa(p_sockaddr->sin_addr);
const struct sockaddr* p_sockaddr = &p_sockptr->u.u_sockaddr;
if (p_sockaddr->sa_family == AF_INET)
{
return inet_ntoa(p_sockptr->u.u_sockaddr_in.sin_addr);
}
else if (p_sockaddr->sa_family == AF_INET6)
{
static char inaddr_buf[64];
const char* p_ret = inet_ntop(AF_INET6,
&p_sockptr->u.u_sockaddr_in6.sin6_addr,
inaddr_buf, sizeof(inaddr_buf));
inaddr_buf[sizeof(inaddr_buf) - 1] = '\0';
if (p_ret == NULL)
{
inaddr_buf[0] = '\0';
}
return inaddr_buf;
}
else
{
die("can only support ipv4 and ipv6 currently");
}
}
const char*
vsf_sysutil_inet_ntoa(const void* p_raw_addr)
{
return inet_ntoa(*((struct in_addr*)p_raw_addr));
}
int
vsf_sysutil_inet_aton(const char* p_text, struct vsf_sysutil_ipv4addr* p_addr)
vsf_sysutil_inet_aton(const char* p_text, struct vsf_sysutil_sockaddr* p_addr)
{
struct in_addr sin_addr;
if (p_addr->u.u_sockaddr.sa_family != AF_INET)
{
bug("bad family");
}
if (inet_aton(p_text, &sin_addr))
{
vsf_sysutil_memcpy(p_addr, &sin_addr.s_addr, sizeof(*p_addr));
vsf_sysutil_memcpy(&p_addr->u.u_sockaddr_in.sin_addr,
&sin_addr, sizeof(p_addr->u.u_sockaddr_in.sin_addr));
return 1;
}
else
@ -2046,3 +2424,24 @@ vsf_sysutil_getenv(const char* p_var)
return getenv(p_var);
}
void
vsf_sysutil_openlog(void)
{
int facility = LOG_DAEMON;
#ifdef LOG_FTP
facility = LOG_FTP;
#endif
openlog("vsftpd", LOG_NDELAY, facility);
}
void
vsf_sysutil_syslog(const char* p_text, int severe)
{
int prio = LOG_INFO;
if (severe)
{
prio = LOG_WARNING;
}
syslog(prio, "%s", p_text);
}

View File

@ -15,7 +15,8 @@ enum EVSFSysUtilError
kVSFSysUtilErrADDRINUSE,
kVSFSysUtilErrNOSYS,
kVSFSysUtilErrINTR,
kVSFSysUtilErrINVAL
kVSFSysUtilErrINVAL,
kVSFSysUtilErrOPNOTSUPP
};
enum EVSFSysUtilError vsf_sysutil_get_error(void);
@ -48,6 +49,8 @@ void vsf_sysutil_install_io_handler(vsf_context_io_t handler, void* p_private);
void vsf_sysutil_uninstall_io_handler(void);
void vsf_sysutil_check_pending_actions(
const enum EVSFSysUtilInterruptContext context, int retval, int fd);
void vsf_sysutil_block_sig(const enum EVSFSysUtilSignal sig);
void vsf_sysutil_unblock_sig(const enum EVSFSysUtilSignal sig);
/* Alarm setting/clearing utility functions */
void vsf_sysutil_set_alarm(const unsigned int trigger_seconds);
@ -129,6 +132,7 @@ const char* vsf_sysutil_statbuf_get_sortkey_mtime(
int vsf_sysutil_chmod(const char* p_filename, unsigned int mode);
void vsf_sysutil_fchown(const int fd, const int uid, const int gid);
void vsf_sysutil_fchmod(const int fd, unsigned int mode);
int vsf_sysutil_readlink(const char* p_filename, char* p_dest,
unsigned int bufsiz);
@ -176,6 +180,7 @@ char* vsf_sysutil_strdup(const char* p_str);
void vsf_sysutil_memclr(void* p_dest, unsigned int size);
void vsf_sysutil_memcpy(void* p_dest, const void* p_src,
const unsigned int size);
void vsf_sysutil_strcpy(char* p_dest, const char* p_src, unsigned int maxsize);
int vsf_sysutil_memcmp(const void* p_src1, const void* p_src2,
unsigned int size);
int vsf_sysutil_strcmp(const char* p_src1, const char* p_src2);
@ -193,14 +198,6 @@ int vsf_sysutil_isalnum(int the_char);
/* Socket handling */
struct vsf_sysutil_sockaddr;
struct vsf_sysutil_ipv4addr
{
unsigned char data[4];
};
struct vsf_sysutil_ipv4port
{
unsigned char data[2];
};
struct vsf_sysutil_socketpair_retval
{
int socket_one;
@ -208,18 +205,33 @@ struct vsf_sysutil_socketpair_retval
};
void vsf_sysutil_sockaddr_clear(struct vsf_sysutil_sockaddr** p_sockptr);
void vsf_sysutil_sockaddr_alloc_ipv4(struct vsf_sysutil_sockaddr** p_sockptr);
void vsf_sysutil_sockaddr_set_ipaddr(struct vsf_sysutil_sockaddr* p_sockptr,
struct vsf_sysutil_ipv4addr the_addr);
struct vsf_sysutil_ipv4addr vsf_sysutil_sockaddr_get_ipaddr(
const struct vsf_sysutil_sockaddr* p_sockptr);
struct vsf_sysutil_ipv4addr vsf_sysutil_sockaddr_get_any(void);
struct vsf_sysutil_ipv4port vsf_sysutil_ipv4port_from_int(unsigned int port);
void vsf_sysutil_sockaddr_alloc_ipv6(struct vsf_sysutil_sockaddr** p_sockptr);
void vsf_sysutil_sockaddr_clone(
struct vsf_sysutil_sockaddr** p_sockptr,
const struct vsf_sysutil_sockaddr* p_src);
int vsf_sysutil_sockaddr_addr_equal(const struct vsf_sysutil_sockaddr* p1,
const struct vsf_sysutil_sockaddr* p2);
int vsf_sysutil_sockaddr_is_ipv6(
const struct vsf_sysutil_sockaddr* p_sockaddr);
int vsf_sysutil_sockaddr_same_family(
const struct vsf_sysutil_sockaddr* p1,
const struct vsf_sysutil_sockaddr* p2);
void vsf_sysutil_sockaddr_set_ipv4addr(struct vsf_sysutil_sockaddr* p_sockptr,
const unsigned char* p_raw);
void vsf_sysutil_sockaddr_set_ipv6addr(struct vsf_sysutil_sockaddr* p_sockptr,
const unsigned char* p_raw);
void vsf_sysutil_sockaddr_set_any(struct vsf_sysutil_sockaddr* p_sockaddr);
void vsf_sysutil_sockaddr_set_port(struct vsf_sysutil_sockaddr* p_sockptr,
struct vsf_sysutil_ipv4port the_port);
struct vsf_sysutil_ipv4port vsf_sysutil_sockaddr_get_port(
const struct vsf_sysutil_sockaddr* p_sockptr);
int vsf_sysutil_is_port_reserved(const struct vsf_sysutil_ipv4port);
unsigned short the_port);
int vsf_sysutil_is_port_reserved(unsigned short port);
int vsf_sysutil_get_ipsock(const struct vsf_sysutil_sockaddr* p_sockaddr);
unsigned int vsf_sysutil_get_ipaddr_size(void);
void* vsf_sysutil_sockaddr_get_raw_addr(
struct vsf_sysutil_sockaddr* p_sockaddr);
const void* vsf_sysutil_sockaddr_ipv6_v4(
const struct vsf_sysutil_sockaddr* p_sockaddr);
int vsf_sysutil_get_ipv4_sock(void);
int vsf_sysutil_get_ipv6_sock(void);
struct vsf_sysutil_socketpair_retval
vsf_sysutil_unix_dgram_socketpair(void);
int vsf_sysutil_bind(int fd, const struct vsf_sysutil_sockaddr* p_sockptr);
@ -239,17 +251,18 @@ void vsf_sysutil_set_nodelay(int fd);
void vsf_sysutil_activate_sigurg(int fd);
void vsf_sysutil_activate_oobinline(int fd);
void vsf_sysutil_activate_linger(int fd);
void vsf_sysutil_deactivate_linger(int fd);
void vsf_sysutil_deactivate_linger_failok(int fd);
void vsf_sysutil_activate_noblock(int fd);
void vsf_sysutil_deactivate_noblock(int fd);
/* This does SHUT_RDWR */
void vsf_sysutil_shutdown_failok(int fd);
int vsf_sysutil_recv_peek(const int fd, void* p_buf, unsigned int len);
const char* vsf_sysutil_inet_ntoa(
const char* vsf_sysutil_inet_ntop(
const struct vsf_sysutil_sockaddr* p_sockptr);
const char* vsf_sysutil_inet_ntoa(const void* p_raw_addr);
int vsf_sysutil_inet_aton(
const char* p_text, struct vsf_sysutil_ipv4addr* p_addr);
const char* p_text, struct vsf_sysutil_sockaddr* p_addr);
/* User database queries etc. */
struct vsf_sysutil_user;
@ -278,6 +291,12 @@ void vsf_sysutil_qsort(void* p_base, unsigned int num_elem,
unsigned int elem_size,
int (*p_compar)(const void *, const void *));
char* vsf_sysutil_getenv(const char* p_var);
typedef void (*exitfunc_t)(void);
void vsf_sysutil_set_exit_func(exitfunc_t exitfunc);
/* Syslogging (bah) */
void vsf_sysutil_openlog(void);
void vsf_sysutil_syslog(const char* p_text, int severe);
/* Credentials handling */
int vsf_sysutil_running_as_root(void);

View File

@ -13,9 +13,9 @@
#ifndef VSF_BUILD_TCPWRAPPERS
int
vsf_tcp_wrapper_ok(const struct vsf_sysutil_sockaddr* p_addr)
vsf_tcp_wrapper_ok(int remote_fd)
{
(void) p_addr;
(void) remote_fd;
return 1;
}
@ -28,10 +28,10 @@ int deny_severity = LOG_WARNING;
int allow_severity = LOG_INFO;
int
vsf_tcp_wrapper_ok(const struct vsf_sysutil_sockaddr* p_addr)
vsf_tcp_wrapper_ok(int remote_fd)
{
struct request_info req;
request_init(&req, RQ_DAEMON, "vsftpd", RQ_CLIENT_SIN, (void*)p_addr, 0);
request_init(&req, RQ_DAEMON, "vsftpd", RQ_FILE, remote_fd, 0);
fromhost(&req);
if (!hosts_access(&req))
{

View File

@ -1,9 +1,7 @@
#ifndef VSF_TCPWRAP_H
#define VSF_TCPWRAP_H
struct vsf_sysutil_sockaddr;
int vsf_tcp_wrapper_ok(const struct vsf_sysutil_sockaddr* p_addr);
int vsf_tcp_wrapper_ok(int remote_fd);
#endif /* VSF_TCPWRAP_H */

View File

@ -45,6 +45,16 @@ int tunable_passwd_chroot_enable = 0;
int tunable_no_anon_password = 0;
int tunable_tcp_wrappers = 0;
int tunable_use_sendfile = 1;
int tunable_force_dot_files = 0;
int tunable_listen_ipv6 = 0;
int tunable_dual_log_enable = 0;
int tunable_syslog_enable = 0;
int tunable_background = 0;
int tunable_virtual_use_local_privs = 0;
int tunable_session_support = 0;
int tunable_download_enable = 1;
int tunable_dirlist_enable = 1;
int tunable_chmod_enable = 1;
unsigned int tunable_accept_timeout = 60;
unsigned int tunable_connect_timeout = 60;
@ -64,13 +74,14 @@ unsigned int tunable_max_clients = 0;
/* -rw-rw-rw- */
unsigned int tunable_file_open_mode = 0666;
unsigned int tunable_max_per_ip = 0;
unsigned int tunable_trans_chunk_size = 0;
const char* tunable_secure_chroot_dir = "/usr/share/empty";
const char* tunable_ftp_username = "ftp";
const char* tunable_chown_username = "root";
const char* tunable_xferlog_file = "/var/log/vsftpd.log";
const char* tunable_xferlog_file = "/var/log/xferlog";
const char* tunable_vsftpd_log_file = "/var/log/vsftpd.log";
const char* tunable_message_file = ".message";
/* XXX -> "secure"? */
const char* tunable_nopriv_user = "nobody";
const char* tunable_ftpd_banner = 0;
const char* tunable_banned_email_file = "/etc/vsftpd.banned_emails";
@ -84,4 +95,5 @@ const char* tunable_banner_file = 0;
const char* tunable_pasv_address = 0;
const char* tunable_listen_address = 0;
const char* tunable_user_config_dir = 0;
const char* tunable_listen_address6 = 0;
const char* tunable_cmds_allowed = 0;

View File

@ -41,6 +41,16 @@ extern int tunable_passwd_chroot_enable; /* chroot() based on passwd */
extern int tunable_no_anon_password; /* Do not ask for anon pword */
extern int tunable_tcp_wrappers; /* Standalone: do tcp wrappers */
extern int tunable_use_sendfile; /* Use sendfile() if we can */
extern int tunable_force_dot_files; /* Show dotfiles without -a */
extern int tunable_listen_ipv6; /* Standalone with IPv6 listen */
extern int tunable_dual_log_enable; /* Log vsftpd.log AND xferlog */
extern int tunable_syslog_enable; /* Use syslog not vsftpd.log */
extern int tunable_background; /* Background listener process */
extern int tunable_virtual_use_local_privs; /* Virtual user => local privs */
extern int tunable_session_support; /* utmp, wtmp, pam_session */
extern int tunable_download_enable; /* Can download anything? */
extern int tunable_dirlist_enable; /* Can see any dirs? */
extern int tunable_chmod_enable; /* Is CHMOD allowed? (local) */
/* Integer/numeric defines */
extern unsigned int tunable_accept_timeout;
@ -58,12 +68,14 @@ 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;
extern unsigned int tunable_trans_chunk_size;
/* String defines */
extern const char* tunable_secure_chroot_dir;
extern const char* tunable_ftp_username;
extern const char* tunable_chown_username;
extern const char* tunable_xferlog_file;
extern const char* tunable_vsftpd_log_file;
extern const char* tunable_message_file;
extern const char* tunable_nopriv_user;
extern const char* tunable_ftpd_banner;
@ -78,6 +90,8 @@ extern const char* tunable_banner_file;
extern const char* tunable_pasv_address;
extern const char* tunable_listen_address;
extern const char* tunable_user_config_dir;
extern const char* tunable_listen_address6;
extern const char* tunable_cmds_allowed;
#endif /* VSF_TUNABLES_H */

View File

@ -38,9 +38,6 @@ static void calculate_chdir_dir(int anon, struct mystr* p_chroot_str,
static void
handle_sigchld(int duff)
{
/* WARNING - async handler. Must not call anything which might have
* re-entrancy issues
*/
struct vsf_sysutil_wait_retval wait_retval = vsf_sysutil_wait();
(void) duff;
/* Child died, so we'll do the same! Report it as an error unless the child
@ -73,8 +70,6 @@ vsf_two_process_start(struct vsf_session* p_sess)
{
process_login_req(p_sess);
}
/* NOTREACHED */
bug("should not get here: vsf_two_process_start");
}
}
/* Child process - time to lose as much privilege as possible and do the
@ -86,7 +81,7 @@ vsf_two_process_start(struct vsf_session* p_sess)
VSFTP_CONF_FILE_MAX);
if (vsf_sysutil_retval_is_error(retval))
{
die("cannot open user list file");
die2("cannot open user list file:", tunable_userlist_file);
}
}
drop_all_privs();
@ -106,7 +101,8 @@ drop_all_privs(void)
struct vsf_sysutil_statbuf* p_statbuf = 0;
if (vsf_sysutil_retval_is_error(str_lstat(&dir_str, &p_statbuf)))
{
die("vsftpd: not found: directory given in 'secure_chroot_dir'");
die2("vsftpd: not found: directory given in 'secure_chroot_dir':",
tunable_secure_chroot_dir);
}
vsf_sysutil_free(p_statbuf);
}
@ -174,8 +170,12 @@ static void
process_login_req(struct vsf_session* p_sess)
{
enum EVSFPrivopLoginResult e_login_result = kVSFLoginNull;
char cmd;
vsf_sysutil_unblock_sig(kVSFSysUtilSigCHLD);
/* Blocks */
if (priv_sock_get_cmd(p_sess) != PRIV_SOCK_LOGIN)
cmd = priv_sock_get_cmd(p_sess);
vsf_sysutil_block_sig(kVSFSysUtilSigCHLD);
if (cmd != PRIV_SOCK_LOGIN)
{
die("bad request");
}
@ -212,7 +212,8 @@ process_login_req(struct vsf_session* p_sess)
VSFTP_CONF_FILE_MAX);
if (vsf_sysutil_retval_is_error(retval))
{
die("cannot open chroot() user list file");
die2("could not open chroot() list file:",
tunable_chroot_list_file);
}
if (str_contains_line(&chroot_list_file, &p_sess->user_str))
{
@ -243,12 +244,16 @@ common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
{
int was_anon = anon;
int newpid;
vsf_sysutil_default_sig(kVSFSysUtilSigCHLD);
vsf_sysutil_install_null_sighandler(kVSFSysUtilSigCHLD);
/* Asks the pre-login child to go away (by exiting) */
priv_sock_send_result(p_sess, PRIV_SOCK_RESULT_OK);
(void) vsf_sysutil_wait();
/* Absorb the SIGCHLD */
vsf_sysutil_unblock_sig(kVSFSysUtilSigCHLD);
/* Handle loading per-user config options */
handle_per_user_config(p_user_str);
/* Set this before we fork */
p_sess->is_anonymous = anon;
vsf_sysutil_install_async_sighandler(kVSFSysUtilSigCHLD, handle_sigchld);
newpid = vsf_sysutil_fork();
if (newpid == 0)
@ -268,10 +273,10 @@ common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
/* Remap to the guest user */
str_alloc_text(&guest_user_str, tunable_guest_username);
p_user_str = &guest_user_str;
/* SECURITY: For now, apply the anonymous restrictions to
* guest users
*/
anon = 1;
if (!tunable_virtual_use_local_privs)
{
anon = 1;
}
}
if (!anon)
{
@ -348,10 +353,7 @@ calculate_chdir_dir(int anon, struct mystr* p_chroot_str,
struct str_locate_result loc_result;
if (p_user == 0)
{
struct mystr death_str = INIT_MYSTR;
str_alloc_text(&death_str, "str_getpwnam: ");
str_append_str(&death_str, p_user_str);
die(str_getbuf(&death_str));
die2("cannot locate user entry:", str_getbuf(p_user_str));
}
str_alloc_text(&homedir_str, vsf_sysutil_user_get_homedir(p_user));
loc_result = str_locate_text(&homedir_str, "/./");

View File

@ -7,6 +7,7 @@
#include "utility.h"
#include "sysutil.h"
#include "str.h"
#include "defs.h"
#define DIE_DEBUG
@ -20,6 +21,15 @@ die(const char* p_text)
vsf_sysutil_exit(1);
}
void
die2(const char* p_text1, const char* p_text2)
{
struct mystr die_str = INIT_MYSTR;
str_alloc_text(&die_str, p_text1);
str_append_text(&die_str, p_text2);
die(str_getbuf(&die_str));
}
void
bug(const char* p_text)
{

View File

@ -12,6 +12,16 @@ struct mystr;
*/
void die(const char* p_text);
/* die2()
* PURPOSE
* Terminate execution of the process, due to an abnormal (but non-bug)
* situation.
* PARAMETERS
* p_text1 - text string describing why the process is exiting
* p_text2 - text to safely concatenate to p_text1
*/
void die2(const char* p_text1, const char* p_text2);
/* bug()
* PURPOSE
* Terminate execution of the process, due to a suspected bug, trying to emit

View File

@ -3,7 +3,7 @@
locate_library() { [ ! "$1*" = "`echo $1*`" ]; }
if (grep -q "#define VSF_BUILD_TCPWRAPPERS" builddefs.h); then
if (grep "#define VSF_BUILD_TCPWRAPPERS" builddefs.h >/dev/null); then
echo "-lwrap";
locate_library /lib/libnsl.so && echo "-lnsl";
fi

View File

@ -23,4 +23,3 @@ may be given on the command line. The default configuration file is
.Pa /etc/vsftpd.conf .
.Sh SEE ALSO
.Xr vsftpd.conf 5

View File

@ -1,9 +1,14 @@
# Example config file /etc/vsftpd.conf
#
# The default compiled in settings are very paranoid. This sample file
# The default compiled in settings are fairly paranoid. This sample file
# loosens things up a bit, to make the ftp daemon more usable.
# Please see vsftpd.conf.5 for all compiled in defaults.
#
# Allow anonymous FTP?
# READ THIS: This example file is NOT an exhaustive list of vsftpd options.
# Please read the vsftpd.conf.5 manual page to get a full idea of vsftpd's
# capabilities.
#
# Allow anonymous FTP? (Beware - allowed by default if you comment this out).
anonymous_enable=YES
#
# Uncomment this to allow local users to log in.

View File

@ -93,12 +93,25 @@ is awkward to handle, so it is disabled by default. Unfortunately, some FTP
clients will hang when cancelling a transfer unless this feature is available,
so you may wish to enable it.
Default: NO
.TP
.B background
When enabled, and vsftpd is started in "listen" mode, vsftpd will background
the listener process. i.e. control will immediately be returned to the shell
which launched vsftpd.
Default: NO
.TP
.B check_shell
Note! This option only has an effect for non-PAM builds of vsftpd. If disabled,
vsftpd will not check /etc/shells for a valid user shell for local logins.
Default: YES
.TP
.B chmod_enable
When enables, allows use of the SITE CHMOD command. NOTE! This only applies
to local users. Anonymous users never get to use SITE CHMOD.
Default: YES
.TP
.B chown_uploads
@ -149,6 +162,11 @@ setting.
Default: NO
.TP
.B dirlist_enable
If set to NO, all directory list commands will give permission denied.
Default: YES
.TP
.B dirmessage_enable
If enabled, users of the FTP server can be shown messages when they first
enter a new directory. By default, a directory is scanned for the
@ -157,6 +175,28 @@ file .message, but that may be overridden with the configuration setting
Default: NO (but the sample config file enables it)
.TP
.B download_enable
If set to NO, all download requests will give permission denied.
Default: YES
.TP
.B dual_log_enable
If enabled, two log files are generated in parallel, going by default to
.BR /var/log/xferlog
and
.BR /var/log/vsftpd.log .
The former is a wu-ftpd style transfer log, parseable by standard tools. The
latter is vsftpd's own style log.
Default: NO
.TP
.B force_dot_files
If activated, files and directories starting with . will be shown in directory
listings even if the "a" flag was not used by the client. This override
exludes the "." and ".." entries.
Default: NO
.TP
.B guest_enable
If enabled, all non-anonymous logins are classed as "guest" logins. A guest
login is remapped to the user specified in the
@ -177,6 +217,13 @@ not be run from an inetd of some kind. Instead, the vsftpd executable is
run once directly. vsftpd itself will then take care of listening for and
handling incoming connections.
Default: NO
.TP
.B listen_ipv6
Like the listen parameter, except vsftpd will listen on an IPv6 socket instead
of an IPv4 one. This parameter and the listen parameter are mutually
exlusive.
Default: NO
.TP
.B local_enable
@ -250,12 +297,29 @@ you know what you are doing!
Default: NO
.TP
.B session_support
This controls whether vsftpd attempts to maintain sessions for logins. If
vsftpd is maintaining sessions, it will try and update utmp and wtmp. It
will also open a pam_session if using PAM to authenticate, and only close
this upon logout. You may wish to disable this if you do not need session
logging, and you wish to give vsftpd more opportunity to run with less
processes and / or less privilege. NOTE - utmp and wtmp support is only
provided with PAM enabled builds.
Default: YES
.TP
.B setproctitle_enable
If enabled, vsftpd will try and show session status information in the system
process listing. In other words, the reported name of the process will change
to reflect what a vsftpd session is doing (idle, downloading etc). You
probably want to leave this off for security purposes.
Default: NO
.TP
.B syslog_enable
If enabled, then any log output which would have gone to /var/log/vsftpd.log
goes to the system log instead. Logging is done under the FTPD facility.
Default: NO
.TP
.B tcp_wrappers
@ -306,6 +370,13 @@ before they are asked for a password. This may be useful in preventing
cleartext passwords being transmitted. See also
.BR userlist_deny .
Default: NO
.TP
.B virtual_use_local_privs
If enabled, virtual users will use the same privileges as local users. By
default, virtual users will use the same privileges as anonymous users, which
tends to be more restrictive (especially in terms of write access).
Default: NO
.TP
.B write_enable
@ -318,14 +389,17 @@ Default: NO
If enabled, a log file will be maintained detailling uploads and downloads.
By default, this file will be placed at /var/log/vsftpd.log, but this location
may be overridden using the configuration setting
.BR xferlog_file .
.BR vsftpd_log_file .
Default: NO (but the sample config file enables it)
.TP
.B xferlog_std_format
If enabled, the transfer log file will be written in standard xferlog format,
as used by wu-ftpd. This is useful because you can reuse existing transfer
statistics generators. The default format is more readable, however.
statistics generators. The default format is more readable, however. The
default location for this style of log file is /var/log/xferlog, but you may
change it with the setting
.BR xferlog_file .
Default: NO
@ -431,6 +505,12 @@ The minimum port to allocate for PASV style data connections. Can be used to
specify a narrow port range to assist firewalling.
Default: 0 (use any port)
.TP
.B trans_chunk_size
You probably don't want to change this, but try setting it to something like
8192 for a much smoother bandwidth limiter.
Default: 0 (let vsftpd pick a sensible setting)
.SH STRING OPTIONS
Below is a list of string options.
@ -478,6 +558,14 @@ is disabled.
Default: /etc/vsftpd.chroot_list
.TP
.B cmds_allowed
This options specifies a comma separated list of allowed FTP commands (post
login. USER, PASS and QUIT are always allowed pre-login). Other
commands are rejected. This is a powerful method of really locking down an
FTP server. Example: cmds_allowed=PASV,RETR,QUIT
Default: (none)
.TP
.B guest_username
See the boolean setting
.BR guest_enable
@ -502,6 +590,13 @@ Default: (none - default vsftpd banner is displayed)
If vsftpd is in standalone mode, the default listen address (of all local
interfaces) may be overridden by this setting. Provide a numeric IP address.
Default: (none)
.TP
.B listen_address6
Like listen_address, but specifies a default listen address for the IPv6
listener (which is used if listen_ipv6 is set). Format is standard IPv6
address format.
Default: (none)
.TP
.B local_root
@ -566,14 +661,31 @@ This option is the name of the file loaded when the
option is active.
Default: /etc/vsftpd.user_list
.TP
.B xferlog_file
This option is the name of the file to which we write the transfer log. The
transfer log is only written if the option
.BR
.B vsftpd_log_file
This option is the name of the file to which we write the vsftpd style
log file. This log is only written if the option
.BR xferlog_enable
is set.
is set, and
.BR xferlog_std_format
is NOT set. Alternatively, it is written if you have set the option
.BR dual_log_enable .
One further complication - if you have set
.BR syslog_enable ,
then this file is not written and output is sent to the system log instead.
Default: /var/log/vsftpd.log
.TP
.B xferlog_file
This option is the name of the file to which we write the wu-ftpd style
transfer log. The transfer log is only written if the option
.BR xferlog_enable
is set, along with
.BR xferlog_std_format .
Alternatively, it is written if you have set the option
.BR dual_log_enable .
Default: /var/log/xferlog
.SH AUTHOR
chris@scary.beasts.org

View File

@ -1,7 +1,7 @@
#ifndef VSF_VERSION_H
#define VSF_VERSION_H
#define VSF_VERSION "1.1.3"
#define VSF_VERSION "1.2.0"
#endif /* VSF_VERSION_H */