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:
parent
fa545eaa75
commit
562d5b3c42
1
AUDIT
1
AUDIT
@ -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
|
||||
|
58
Changelog
58
Changelog
@ -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
53
FAQ
@ -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.
|
||||
|
||||
|
||||
|
2
Makefile
2
Makefile
@ -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
8
README
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
20
TODO
@ -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
7
dummyinc/crypt.h
Normal 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
7
dummyinc/utmpx.h
Normal 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 */
|
||||
|
34
ftpcmdio.c
34
ftpcmdio.c
@ -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
|
||||
|
23
ftpcmdio.h
23
ftpcmdio.h
@ -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
|
||||
|
11
ftpcodes.h
11
ftpcodes.h
@ -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
|
||||
|
46
ftpdataio.c
46
ftpdataio.c
@ -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
178
ipv6parse.c
Normal 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
14
ipv6parse.h
Normal 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 */
|
||||
|
76
logging.c
76
logging.c
@ -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
21
ls.c
@ -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
54
main.c
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
23
parseconf.c
23
parseconf.c
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
351
postlogin.c
351
postlogin.c
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
23
privops.c
23
privops.c
@ -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;
|
||||
}
|
||||
|
||||
|
13
secutil.c
13
secutil.c
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
186
standalone.c
186
standalone.c
@ -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
7
str.c
@ -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);
|
||||
}
|
||||
|
170
sysdeputil.c
170
sysdeputil.c
@ -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 */
|
||||
|
||||
|
6
sysstr.c
6
sysstr.c
@ -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);
|
||||
}
|
||||
|
||||
|
2
sysstr.h
2
sysstr.h
@ -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
569
sysutil.c
@ -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);
|
||||
}
|
||||
|
||||
|
63
sysutil.h
63
sysutil.h
@ -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);
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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 */
|
||||
|
||||
|
18
tunables.c
18
tunables.c
@ -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;
|
||||
|
14
tunables.h
14
tunables.h
@ -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 */
|
||||
|
||||
|
38
twoprocess.c
38
twoprocess.c
@ -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, "/./");
|
||||
|
10
utility.c
10
utility.c
@ -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)
|
||||
{
|
||||
|
10
utility.h
10
utility.h
@ -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
|
||||
|
@ -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
|
||||
|
1
vsftpd.8
1
vsftpd.8
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
126
vsftpd.conf.5
126
vsftpd.conf.5
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user