mirror of
https://github.com/InfrastructureServices/vsftpd.git
synced 2025-04-19 01:24:02 +03:00
Updated to v1.2.1
This commit is contained in:
parent
562d5b3c42
commit
8c2b3b01b5
1
AUDIT
1
AUDIT
@ -5,6 +5,7 @@ and 5 being heavily audited by multiple competent people.
|
||||
The important rule is that when a file is changed, the audit status goes back
|
||||
to 1, _unless_ the change(s) are audited very carefully as they go in.
|
||||
|
||||
access.c 2
|
||||
ascii.c 3
|
||||
banner.c 2
|
||||
dirchange.c 3
|
||||
|
48
Changelog
48
Changelog
@ -695,3 +695,51 @@ per-user config stuff.
|
||||
At this point: v1.2.0 released!
|
||||
===============================
|
||||
|
||||
- Apply NetBSD patch to sysdeputil.c to activate a few features. Thanks to
|
||||
Lubomir Sedlacik <salo@netbsd.org>.
|
||||
- Apply fix for broken clients that terminate commands with \r\r\n. Thanks
|
||||
to Andrey Chernomyrdin <andrey@excom.spb.su>.
|
||||
- AIX send_file support, thanks to Tomas Ogren <stric@ing.umu.se>.
|
||||
- Fix typos in vsftpd.conf.5, thanks to SEKINE Tatsuo <tsekine@sdri.co.jp>.
|
||||
- Simple -F flag support to LIST and NLST. Needed for some broken clients.
|
||||
- Add simple ? wildcard in pattern matching.
|
||||
- Make pasv_min_port and pasv_max_port work if they are the same value. Thanks
|
||||
to Marvin Solomon <solomon@cs.wisc.edu>.
|
||||
- Paranoia: ignore user_config_dir if username has a / in it.
|
||||
- Implement stub ALLO command to keep busybox/ftpput happy.
|
||||
- Implement REIN, ACCT and SMNT stubs.
|
||||
- Implement FEAT along with an OPTS stub.
|
||||
- Implement STAT (no-args version).
|
||||
- Implement STAT (file/dir).
|
||||
- Add very simple access control via hide_file and deny_file. These should
|
||||
NOT be used for securing content as they are very dumb! Filesystem permissions
|
||||
are still the recommended way for securing important content.
|
||||
- Allow unsetting of string values with option= (i.e. blank).
|
||||
- Default virtual users to being chroot()'ed to the guest_user's home
|
||||
directory, if virtual_use_local_privs is not set.
|
||||
- Add support for "user_sub_token", where you can set the home directory of
|
||||
guest_user to "/home/virtual/$USER", and "user_sub_token" to "$USER" to
|
||||
have a root directory auto generated based on username logging in, e.g.
|
||||
fred logs in and gets chroot()'ed in /home/virtual/fred.
|
||||
- Fix bug in str_replace_text if replace token matches at end of string.
|
||||
- Recognize P@SW as PASV; works around an SMC router bug.
|
||||
- Accept an async ABOR sequence if it arrives via non-urgent data. Fixes issue
|
||||
with Cisco routers. Thanks to Eddie Corns <E.Corns@ed.ac.uk>.
|
||||
- Implement simple {,} support in pattern matcher (nested not handled). Handy
|
||||
to use with hide_file and deny_file options.
|
||||
(v1.2.1pre2)
|
||||
- Fix port range with pasv_min_port and pasv_max_port to use the full range
|
||||
(the upper limit wasn't being used very often!).
|
||||
- Activate SO_REUSEADDR on passive listen sockets - makes servers with
|
||||
restricted port ranges much more useable!
|
||||
- Add secure_email_list_enable, to provide simple anonymous password control.
|
||||
For some cases, it's better than the hassle of virtual users. Idea thanks to
|
||||
Malcolm O'Callaghan, <mjo@stamps.com>.
|
||||
- Add some FAQ entries.
|
||||
(v1.2.1pre3)
|
||||
- Fix issue with failure to call openlog() before using tcp_wrappers. Part
|
||||
of RH bugzilla #89765. (The more serious part was fixed with v1.2.0).
|
||||
|
||||
At this point: v1.2.1 released!
|
||||
===============================
|
||||
|
||||
|
@ -17,6 +17,11 @@ Whilst logged in as root, create the actual database file like this:
|
||||
|
||||
db_load -T -t hash -f logins.txt /etc/vsftpd_login.db
|
||||
(Requires the Berkeley db program installed).
|
||||
NOTE: Many systems have multiple versions of "db" installed, so you may
|
||||
need to use e.g. db3_load for correct operation. This is known to affect
|
||||
some Debian systems. The core issue is that pam_userdb expects its login
|
||||
database to be a specific db version (often db3, whereas db4 may be installed
|
||||
on your system).
|
||||
|
||||
This will create /etc/vsftpd_login.db. Obviously, you may want to make sure
|
||||
the permissions are restricted:
|
||||
|
30
FAQ
30
FAQ
@ -146,8 +146,36 @@ integration.
|
||||
Q) Help! Does vsftpd support IPv6?
|
||||
A) Yes, as of version 1.2.0. Read the vsftpd.conf.5 man page.
|
||||
|
||||
Q) Help! vsftpd doesn't build, it fails with an error about being unable to
|
||||
find -lcap.
|
||||
A) Install the libcap package and retry the build. Seems to affect Debian
|
||||
users a lot.
|
||||
|
||||
Q) Help! I've put settings in /etc/vsftpd.conf, but they are not taking
|
||||
effect!
|
||||
A) This is affecting some RedHat users - some RedHat versions put the config
|
||||
file in /etc/vsftpd/vsftpd.conf.
|
||||
|
||||
Q) Help! vsftpd doesn't build, it complains about problems with incomplete
|
||||
types in sysutil.c.
|
||||
A) Your system probably doesn't have IPv6 support. Either use a more modern
|
||||
system, use an older vsftpd (e.g. v1.1.3), or wait for a version of vsftpd
|
||||
without this problem!
|
||||
|
||||
Q) Help! I'm getting messages along the lines of 500 OOPS: vsf_sysutil_bind
|
||||
when trying to do downloads (particularly lots of small files).
|
||||
A) vsftpd-1.2.1 should sort this out.
|
||||
|
||||
Q) Help! Does vsftpd support hiding or denying certain files?
|
||||
A) Yes. Look at the hide_file and deny_file options in the manual page.
|
||||
|
||||
Q) Help! Does vsftpd support FXP?
|
||||
A) Yes. An FTP server does not have to do anything special to support FXP.
|
||||
However, you many get tripped up by vsftpd's security precautions on IP
|
||||
addresses. In order to relax these precautions, have a look in the
|
||||
vsftpd.conf.5 for pasv_promiscuous (and the less advisable port_promiscuous).
|
||||
|
||||
Q) Blah.. blah..
|
||||
A) For a good idea of what vsftpd can do, read the vsftpd.conf.5 man page
|
||||
and the EXAMPLES.
|
||||
|
||||
|
||||
|
9
INSTALL
9
INSTALL
@ -98,10 +98,12 @@ drwxr-sr-x 2 0 50 4096 Jul 26 22:58 pub
|
||||
226 Directory send OK.
|
||||
ftp>
|
||||
|
||||
Step 5) Run from an inetd of some kind (optional, recommended)
|
||||
Step 5) Run from an inetd of some kind (optional - standalone mode is now
|
||||
recommended)
|
||||
|
||||
You may want to run the binary from an inetd of some kind, because this can
|
||||
give you extra features - e.g. per-IP connection limiting if you use xinetd.
|
||||
give you extra features - e.g. xinetd has a lot of settings. (Note that
|
||||
vsftpd's inbuilt listener covers most of the more useful xinetd settings).
|
||||
|
||||
5a) If using standard "inetd", you will need to edit /etc/inetd.conf, and add
|
||||
a line such as:
|
||||
@ -146,7 +148,8 @@ Other notes
|
||||
===========
|
||||
|
||||
Tested platforms (well, it builds)
|
||||
- Any modern, well featured platform should work fine!
|
||||
- Any modern, well featured platform should work fine! More recent versions of
|
||||
the platforms listed below should be fine.
|
||||
- RedHat Linux 8.0
|
||||
- RedHat Linux 7.3
|
||||
- RedHat Linux 7.2
|
||||
|
14
Makefile
14
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 ipv6parse.o \
|
||||
tcpwrap.o ipv6parse.o access.o \
|
||||
sysutil.o sysdeputil.o
|
||||
|
||||
.c.o:
|
||||
@ -28,14 +28,14 @@ install:
|
||||
else \
|
||||
$(INSTALL) -m 755 vsftpd /usr/sbin/vsftpd; fi
|
||||
if [ -x /usr/local/man ]; then \
|
||||
$(INSTALL) -D -m 644 vsftpd.8 /usr/local/man/man8/vsftpd.8; \
|
||||
$(INSTALL) -D -m 644 vsftpd.conf.5 /usr/local/man/man5/vsftpd.conf.5; \
|
||||
$(INSTALL) -m 644 vsftpd.8 /usr/local/man/man8/vsftpd.8; \
|
||||
$(INSTALL) -m 644 vsftpd.conf.5 /usr/local/man/man5/vsftpd.conf.5; \
|
||||
elif [ -x /usr/share/man ]; then \
|
||||
$(INSTALL) -D -m 644 vsftpd.8 /usr/share/man/man8/vsftpd.8; \
|
||||
$(INSTALL) -D -m 644 vsftpd.conf.5 /usr/share/man/man5/vsftpd.conf.5; \
|
||||
$(INSTALL) -m 644 vsftpd.8 /usr/share/man/man8/vsftpd.8; \
|
||||
$(INSTALL) -m 644 vsftpd.conf.5 /usr/share/man/man5/vsftpd.conf.5; \
|
||||
else \
|
||||
$(INSTALL) -D -m 644 vsftpd.8 /usr/man/man8/vsftpd.8; \
|
||||
$(INSTALL) -D -m 644 vsftpd.conf.5 /usr/man/man5/vsftpd.conf.5; fi
|
||||
$(INSTALL) -m 644 vsftpd.8 /usr/man/man8/vsftpd.8; \
|
||||
$(INSTALL) -m 644 vsftpd.conf.5 /usr/man/man5/vsftpd.conf.5; fi
|
||||
if [ -x /etc/xinetd.d ]; then \
|
||||
$(INSTALL) -m 644 xinetd.d/vsftpd /etc/xinetd.d/vsftpd; fi
|
||||
|
||||
|
3
README
3
README
@ -1,4 +1,4 @@
|
||||
This is vsftpd, version 1.2.0
|
||||
This is vsftpd, version 1.2.1
|
||||
Author: Chris Evans
|
||||
Contact: chris@scary.beasts.org
|
||||
|
||||
@ -29,4 +29,5 @@ Configuration
|
||||
|
||||
All configuration options are documented in the manual page vsftpd.conf.5.
|
||||
Various example configurations are discussed in the EXAMPLE directory.
|
||||
Frequently asked questions are tackled in the FAQ file.
|
||||
|
||||
|
@ -15,7 +15,7 @@ Neither of these ships with Solaris.
|
||||
|
||||
Luckily, thanks to Mike Batchelor <mikebat@tmcs.net>, you may locate builds
|
||||
of these modules at:
|
||||
ftp://ftp.tmcs.net/pub/PAM-0.75-listfile_shells-sparc-5.8.tar.gz
|
||||
ftp://ftp.tmcs.net/pub/PAM-0.75-listfile+shells-sparc-5.8.tar.gz
|
||||
|
||||
From Mike:
|
||||
"To install, just unpack it in /usr/lib/security, and edit /etc/pam.conf,
|
||||
|
13
TODO
13
TODO
@ -1,17 +1,21 @@
|
||||
CRITICAL
|
||||
========
|
||||
|
||||
- Improve FAQ, docs.
|
||||
- Improve FAQ, docs (ongoing..)
|
||||
- Integrated test suite (I'm so lazy..)
|
||||
|
||||
NOT SO CRITICAL
|
||||
===============
|
||||
|
||||
- OpenSSL support.
|
||||
- Fix for systems with no IPv6 (e.g. Solaris 7).
|
||||
- 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?
|
||||
- Handle SIGINT.
|
||||
- More stuff in the STAT output (session stats, etc. Perhaps even an "admin"
|
||||
mode which dumps session details?).
|
||||
|
||||
- separate upload/download max rates
|
||||
- select() is assuming Linux behaviour (not threatening stability)
|
||||
@ -24,17 +28,14 @@ ON THE BACK BURNER
|
||||
==================
|
||||
|
||||
- lock the files being modified?
|
||||
- repeated PORT problem?
|
||||
- transfer stats
|
||||
- Small race: signal might come in just before we start a blocking call
|
||||
- OpenSSL support
|
||||
- log logout (pam session support provides this for locals)
|
||||
- Limits on GIDs allowed to authenticate?
|
||||
|
||||
NOT PLANNED
|
||||
===========
|
||||
|
||||
- telnet strings (no demand)
|
||||
- better pattern matching in "ls" (no demand)
|
||||
- "Minimal" build support
|
||||
- transparent tar / compression support (no demand)
|
||||
|
||||
|
72
access.c
Normal file
72
access.c
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* access.c
|
||||
*
|
||||
* Routines to do very very simple access control based on filenames.
|
||||
*/
|
||||
|
||||
#include "access.h"
|
||||
#include "ls.h"
|
||||
#include "tunables.h"
|
||||
#include "str.h"
|
||||
|
||||
int
|
||||
vsf_access_check_file(const struct mystr* p_filename_str)
|
||||
{
|
||||
static struct mystr s_access_str;
|
||||
|
||||
if (!tunable_deny_file)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (str_isempty(&s_access_str))
|
||||
{
|
||||
str_alloc_text(&s_access_str, tunable_deny_file);
|
||||
}
|
||||
if (vsf_filename_passes_filter(p_filename_str, &s_access_str))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct str_locate_result loc_res =
|
||||
str_locate_str(p_filename_str, &s_access_str);
|
||||
if (loc_res.found)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
vsf_access_check_file_visible(const struct mystr* p_filename_str)
|
||||
{
|
||||
static struct mystr s_access_str;
|
||||
|
||||
if (!tunable_hide_file)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (str_isempty(&s_access_str))
|
||||
{
|
||||
str_alloc_text(&s_access_str, tunable_hide_file);
|
||||
}
|
||||
if (vsf_filename_passes_filter(p_filename_str, &s_access_str))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct str_locate_result loc_res =
|
||||
str_locate_str(p_filename_str, &s_access_str);
|
||||
if (loc_res.found)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
29
access.h
Normal file
29
access.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef VSF_ACCESS_H
|
||||
#define VSF_ACCESS_H
|
||||
|
||||
struct mystr;
|
||||
|
||||
/* vsf_access_check_file()
|
||||
* PURPOSE
|
||||
* Check whether the current session has permission to access the given
|
||||
* filename.
|
||||
* PARAMETERS
|
||||
* p_filename_str - the filename to check access for
|
||||
* RETURNS
|
||||
* Returns 1 if access is granted, otherwise 0.
|
||||
*/
|
||||
int vsf_access_check_file(const struct mystr* p_filename_str);
|
||||
|
||||
/* vsf_access_check_file_visible()
|
||||
* PURPOSE
|
||||
* Check whether the current session has permission to view the given
|
||||
* filename in directory listings.
|
||||
* PARAMETERS
|
||||
* p_filename_str - the filename to check visibility for
|
||||
* RETURNS
|
||||
* Returns 1 if the file should be visible, otherwise 0.
|
||||
*/
|
||||
int vsf_access_check_file_visible(const struct mystr* p_filename_str);
|
||||
|
||||
#endif /* VSF_ACCESS_H */
|
||||
|
@ -202,9 +202,10 @@ ftp_getline(struct mystr* p_str)
|
||||
/* If the last character is a \r, strip it */
|
||||
{
|
||||
unsigned int len = str_getlen(p_str);
|
||||
if (len > 0 && str_get_char_at(p_str, len - 1) == '\r')
|
||||
while (len > 0 && str_get_char_at(p_str, len - 1) == '\r')
|
||||
{
|
||||
str_trunc(p_str, len - 1);
|
||||
--len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,12 @@
|
||||
#define FTP_EPSVALLOK 200
|
||||
#define FTP_STRUOK 200
|
||||
#define FTP_MODEOK 200
|
||||
#define FTP_ALLOOK 202
|
||||
#define FTP_FEAT 211
|
||||
#define FTP_STATOK 211
|
||||
#define FTP_SIZEOK 213
|
||||
#define FTP_MDTMOK 213
|
||||
#define FTP_STATFILE_OK 213
|
||||
#define FTP_SITEHELP 214
|
||||
#define FTP_HELP 214
|
||||
#define FTP_SYSTOK 215
|
||||
@ -46,6 +50,8 @@
|
||||
#define FTP_BADSENDFILE 451
|
||||
|
||||
#define FTP_BADCMD 500
|
||||
#define FTP_BADOPTS 501
|
||||
#define FTP_COMMANDNOTIMPL 502
|
||||
#define FTP_NEEDUSER 503
|
||||
#define FTP_NEEDRNFR 503
|
||||
#define FTP_BADSTRU 504
|
||||
|
18
ftpdataio.c
18
ftpdataio.c
@ -43,7 +43,7 @@ static int transfer_dir_internal(
|
||||
struct vsf_session* p_sess, const int remote_fd,
|
||||
struct vsf_sysutil_dir* p_dir, const struct mystr* p_base_dir_str,
|
||||
const struct mystr* p_option_str, const struct mystr* p_filter_str,
|
||||
int is_verbose, int is_recurse);
|
||||
int is_verbose);
|
||||
static int write_dir_list(struct mystr_list* p_dir_list, int remote_fd);
|
||||
|
||||
void
|
||||
@ -246,7 +246,7 @@ vsf_ftpdataio_transfer_dir(struct vsf_session* p_sess, const int remote_fd,
|
||||
int is_verbose)
|
||||
{
|
||||
return transfer_dir_internal(p_sess, remote_fd, p_dir, p_base_dir_str,
|
||||
p_option_str, p_filter_str, is_verbose, 0);
|
||||
p_option_str, p_filter_str, is_verbose);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -255,7 +255,7 @@ transfer_dir_internal(struct vsf_session* p_sess, const int remote_fd,
|
||||
const struct mystr* p_base_dir_str,
|
||||
const struct mystr* p_option_str,
|
||||
const struct mystr* p_filter_str,
|
||||
int is_verbose, int is_recurse)
|
||||
int is_verbose)
|
||||
{
|
||||
struct mystr_list dir_list = INIT_STRLIST;
|
||||
struct mystr_list subdir_list = INIT_STRLIST;
|
||||
@ -318,8 +318,7 @@ transfer_dir_internal(struct vsf_session* p_sess, const int remote_fd,
|
||||
break;
|
||||
}
|
||||
retval = transfer_dir_internal(p_sess, remote_fd, p_subdir, &sub_str,
|
||||
p_option_str, p_filter_str,
|
||||
is_verbose, 1);
|
||||
p_option_str, p_filter_str, is_verbose);
|
||||
vsf_sysutil_closedir(p_subdir);
|
||||
if (retval != 0)
|
||||
{
|
||||
@ -334,19 +333,10 @@ transfer_dir_internal(struct vsf_session* p_sess, const int remote_fd,
|
||||
str_free(&dir_prefix_str);
|
||||
if (!failed)
|
||||
{
|
||||
if (!is_recurse)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Directory send OK.");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!is_recurse)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDNET,
|
||||
"Failure writing network stream.");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,10 @@ void
|
||||
vsf_log_init(struct vsf_session* p_sess)
|
||||
{
|
||||
int retval;
|
||||
if (tunable_syslog_enable || tunable_tcp_wrappers)
|
||||
{
|
||||
vsf_sysutil_openlog();
|
||||
}
|
||||
if (!tunable_xferlog_enable && !tunable_dual_log_enable)
|
||||
{
|
||||
return;
|
||||
|
166
ls.c
166
ls.c
@ -8,14 +8,13 @@
|
||||
*/
|
||||
|
||||
#include "ls.h"
|
||||
#include "access.h"
|
||||
#include "str.h"
|
||||
#include "strlist.h"
|
||||
#include "sysstr.h"
|
||||
#include "sysutil.h"
|
||||
#include "tunables.h"
|
||||
|
||||
static int filename_passes_filter(const struct mystr* p_filename_str,
|
||||
const struct mystr* p_filter_str);
|
||||
static void build_dir_line(struct mystr* p_str,
|
||||
const struct mystr* p_filename_str,
|
||||
const struct vsf_sysutil_statbuf* p_stat);
|
||||
@ -35,6 +34,7 @@ vsf_ls_populate_dir_list(struct mystr_list* p_list,
|
||||
int a_option;
|
||||
int r_option;
|
||||
int t_option;
|
||||
int F_option;
|
||||
int do_stat = 0;
|
||||
loc_result = str_locate_char(p_option_str, 'a');
|
||||
a_option = loc_result.found;
|
||||
@ -42,6 +42,8 @@ vsf_ls_populate_dir_list(struct mystr_list* p_list,
|
||||
r_option = loc_result.found;
|
||||
loc_result = str_locate_char(p_option_str, 't');
|
||||
t_option = loc_result.found;
|
||||
loc_result = str_locate_char(p_option_str, 'F');
|
||||
F_option = loc_result.found;
|
||||
loc_result = str_locate_char(p_option_str, 'l');
|
||||
if (loc_result.found)
|
||||
{
|
||||
@ -52,7 +54,7 @@ vsf_ls_populate_dir_list(struct mystr_list* p_list,
|
||||
{
|
||||
r_option = !r_option;
|
||||
}
|
||||
if (is_verbose || t_option || p_subdir_list != 0)
|
||||
if (is_verbose || t_option || F_option || p_subdir_list != 0)
|
||||
{
|
||||
do_stat = 1;
|
||||
}
|
||||
@ -106,10 +108,15 @@ vsf_ls_populate_dir_list(struct mystr_list* p_list,
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* Don't show hidden directory entries */
|
||||
if (!vsf_access_check_file_visible(&s_next_filename_str))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
/* If we have an ls option which is a filter, apply it */
|
||||
if (!str_isempty(p_filter_str))
|
||||
{
|
||||
if (!filename_passes_filter(&s_next_filename_str, p_filter_str))
|
||||
if (!vsf_filename_passes_filter(&s_next_filename_str, p_filter_str))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -146,6 +153,10 @@ vsf_ls_populate_dir_list(struct mystr_list* p_list,
|
||||
str_append_str(&s_final_file_str, &s_temp_str);
|
||||
}
|
||||
}
|
||||
if (F_option && vsf_sysutil_statbuf_is_dir(s_p_statbuf))
|
||||
{
|
||||
str_append_char(&s_final_file_str, '/');
|
||||
}
|
||||
build_dir_line(&dirline_str, &s_final_file_str, s_p_statbuf);
|
||||
}
|
||||
else
|
||||
@ -154,6 +165,17 @@ vsf_ls_populate_dir_list(struct mystr_list* p_list,
|
||||
* but not for LIST
|
||||
*/
|
||||
str_copy(&dirline_str, &s_next_path_and_filename_str);
|
||||
if (F_option)
|
||||
{
|
||||
if (vsf_sysutil_statbuf_is_dir(s_p_statbuf))
|
||||
{
|
||||
str_append_char(&dirline_str, '/');
|
||||
}
|
||||
else if (vsf_sysutil_statbuf_is_symlink(s_p_statbuf))
|
||||
{
|
||||
str_append_char(&dirline_str, '@');
|
||||
}
|
||||
}
|
||||
str_append_text(&dirline_str, "\r\n");
|
||||
}
|
||||
/* Add filename into our sorted list - sorting by filename or time. Also,
|
||||
@ -191,9 +213,9 @@ vsf_ls_populate_dir_list(struct mystr_list* p_list,
|
||||
str_free(&normalised_base_dir_str);
|
||||
}
|
||||
|
||||
static int
|
||||
filename_passes_filter(const struct mystr* p_filename_str,
|
||||
const struct mystr* p_filter_str)
|
||||
int
|
||||
vsf_filename_passes_filter(const struct mystr* p_filename_str,
|
||||
const struct mystr* p_filter_str)
|
||||
{
|
||||
/* A simple routine to match a filename against a pattern.
|
||||
* This routine is used instead of e.g. fnmatch(3), because we should be
|
||||
@ -202,36 +224,45 @@ filename_passes_filter(const struct mystr* p_filename_str,
|
||||
* implementation to be buggy.
|
||||
*
|
||||
* Currently supported pattern(s):
|
||||
* - any number of wildcards, "*"
|
||||
* - any number of wildcards, "*" or "?"
|
||||
* - {,} syntax (not nested)
|
||||
*
|
||||
* Note that pattern matching is only supported within the last path
|
||||
* component. For example, searching for /a/b/? will work, but searching
|
||||
* for /a/?/c will not.
|
||||
*/
|
||||
static struct mystr s_filter_remain_str;
|
||||
static struct mystr s_name_remain_str;
|
||||
static struct mystr s_temp_str;
|
||||
int last_was_wildcard = 1;
|
||||
struct mystr filter_remain_str = INIT_MYSTR;
|
||||
struct mystr name_remain_str = INIT_MYSTR;
|
||||
struct mystr temp_str = INIT_MYSTR;
|
||||
struct mystr brace_list_str = INIT_MYSTR;
|
||||
struct mystr new_filter_str = INIT_MYSTR;
|
||||
int ret = 0;
|
||||
char last_token = 0;
|
||||
int must_match_at_current_pos = 1;
|
||||
str_copy(&s_filter_remain_str, p_filter_str);
|
||||
str_copy(&s_name_remain_str, p_filename_str);
|
||||
str_copy(&filter_remain_str, p_filter_str);
|
||||
str_copy(&name_remain_str, p_filename_str);
|
||||
|
||||
while (!str_isempty(&s_filter_remain_str))
|
||||
while (!str_isempty(&filter_remain_str))
|
||||
{
|
||||
static struct mystr s_match_needed_str;
|
||||
/* Locate next wildcard */
|
||||
/* Locate next special token */
|
||||
struct str_locate_result locate_result =
|
||||
str_locate_char(&s_filter_remain_str, '*');
|
||||
/* Isolate text leading up to wildcard (if any) - needs to be matched */
|
||||
str_locate_chars(&filter_remain_str, "*?{");
|
||||
/* Isolate text leading up to token (if any) - needs to be matched */
|
||||
if (locate_result.found)
|
||||
{
|
||||
unsigned int indexx = locate_result.index;
|
||||
str_left(&s_filter_remain_str, &s_match_needed_str, indexx);
|
||||
str_mid_to_end(&s_filter_remain_str, &s_temp_str, indexx + 1);
|
||||
str_copy(&s_filter_remain_str, &s_temp_str);
|
||||
str_left(&filter_remain_str, &s_match_needed_str, indexx);
|
||||
str_mid_to_end(&filter_remain_str, &temp_str, indexx + 1);
|
||||
str_copy(&filter_remain_str, &temp_str);
|
||||
last_token = locate_result.char_found;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No more wildcards. Must match remaining filter string exactly. */
|
||||
str_copy(&s_match_needed_str, &s_filter_remain_str);
|
||||
str_empty(&s_filter_remain_str);
|
||||
last_was_wildcard = 0;
|
||||
/* No more tokens. Must match remaining filter string exactly. */
|
||||
str_copy(&s_match_needed_str, &filter_remain_str);
|
||||
str_empty(&filter_remain_str);
|
||||
last_token = 0;
|
||||
}
|
||||
if (!str_isempty(&s_match_needed_str))
|
||||
{
|
||||
@ -239,35 +270,92 @@ filename_passes_filter(const struct mystr* p_filename_str,
|
||||
* current position, or we could allow it to start anywhere
|
||||
*/
|
||||
unsigned int indexx;
|
||||
locate_result = str_locate_str(&s_name_remain_str, &s_match_needed_str);
|
||||
locate_result = str_locate_str(&name_remain_str, &s_match_needed_str);
|
||||
if (!locate_result.found)
|
||||
{
|
||||
/* Fail */
|
||||
return 0;
|
||||
goto out;
|
||||
}
|
||||
indexx = locate_result.index;
|
||||
if (must_match_at_current_pos && indexx > 0)
|
||||
{
|
||||
/* Fail */
|
||||
return 0;
|
||||
goto out;
|
||||
}
|
||||
/* Chop matched string out of remainder */
|
||||
str_mid_to_end(&s_name_remain_str, &s_temp_str,
|
||||
str_mid_to_end(&name_remain_str, &temp_str,
|
||||
indexx + str_getlen(&s_match_needed_str));
|
||||
str_copy(&s_name_remain_str, &s_temp_str);
|
||||
str_copy(&name_remain_str, &temp_str);
|
||||
}
|
||||
if (last_token == '?')
|
||||
{
|
||||
if (str_isempty(&name_remain_str))
|
||||
{
|
||||
goto out;
|
||||
}
|
||||
str_right(&name_remain_str, &temp_str, str_getlen(&name_remain_str) - 1);
|
||||
str_copy(&name_remain_str, &temp_str);
|
||||
must_match_at_current_pos = 1;
|
||||
}
|
||||
else if (last_token == '{')
|
||||
{
|
||||
struct str_locate_result end_brace =
|
||||
str_locate_char(&filter_remain_str, '}');
|
||||
struct str_locate_result comma =
|
||||
str_locate_char(&filter_remain_str, ',');
|
||||
must_match_at_current_pos = 1;
|
||||
if (end_brace.found && comma.found && comma.index < end_brace.index)
|
||||
{
|
||||
str_split_char(&filter_remain_str, &temp_str, '}');
|
||||
str_copy(&brace_list_str, &filter_remain_str);
|
||||
str_copy(&filter_remain_str, &temp_str);
|
||||
str_split_char(&brace_list_str, &temp_str, ',');
|
||||
while (!str_isempty(&brace_list_str))
|
||||
{
|
||||
str_copy(&new_filter_str, &brace_list_str);
|
||||
str_append_str(&new_filter_str, &filter_remain_str);
|
||||
if (vsf_filename_passes_filter(&name_remain_str, &new_filter_str))
|
||||
{
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
str_copy(&brace_list_str, &temp_str);
|
||||
str_split_char(&brace_list_str, &temp_str, ',');
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
else if (str_isempty(&name_remain_str) ||
|
||||
str_get_char_at(&name_remain_str, 0) != '{')
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
str_right(&name_remain_str, &temp_str,
|
||||
str_getlen(&name_remain_str) - 1);
|
||||
str_copy(&name_remain_str, &temp_str);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
must_match_at_current_pos = 0;
|
||||
}
|
||||
/* Only the first iteration can require a match at current position -
|
||||
* subsequent iterations will have seen a '*'
|
||||
*/
|
||||
must_match_at_current_pos = 0;
|
||||
}
|
||||
/* Any incoming string left means no match unless we ended on a wildcard */
|
||||
if (!last_was_wildcard && str_getlen(&s_name_remain_str) > 0)
|
||||
/* Any incoming string left means no match unless we ended on the correct
|
||||
* type of wildcard.
|
||||
*/
|
||||
if (str_getlen(&name_remain_str) > 0 && last_token != '*')
|
||||
{
|
||||
return 0;
|
||||
goto out;
|
||||
}
|
||||
/* OK, a match */
|
||||
return 1;
|
||||
ret = 1;
|
||||
out:
|
||||
str_free(&filter_remain_str);
|
||||
str_free(&name_remain_str);
|
||||
str_free(&temp_str);
|
||||
str_free(&brace_list_str);
|
||||
str_free(&new_filter_str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
|
14
ls.h
14
ls.h
@ -27,5 +27,19 @@ void vsf_ls_populate_dir_list(struct mystr_list* p_list,
|
||||
const struct mystr* p_filter_str,
|
||||
int is_verbose);
|
||||
|
||||
/* vsf_filename_passes_filter()
|
||||
* PURPOSE
|
||||
* Determine whether the given filename is matched by the given filter string.
|
||||
* The format of the filter string is a small subset of a regular expression.
|
||||
* Currently, just * and ? are supported.
|
||||
* PARAMETERS
|
||||
* p_filename_str - the filename to match
|
||||
* p_filter_str - the filter to match against
|
||||
* RETURNS
|
||||
* Returns 1 if there is a match, 0 otherwise.
|
||||
*/
|
||||
int vsf_filename_passes_filter(const struct mystr* p_filename_str,
|
||||
const struct mystr* p_filter_str);
|
||||
|
||||
#endif /* VSF_LS_H */
|
||||
|
||||
|
14
main.c
14
main.c
@ -47,7 +47,7 @@ main(int argc, const char* argv[])
|
||||
/* Userids */
|
||||
-1, -1, -1,
|
||||
/* Pre-chroot() cache */
|
||||
INIT_MYSTR, INIT_MYSTR, INIT_MYSTR, 1,
|
||||
INIT_MYSTR, INIT_MYSTR, INIT_MYSTR, INIT_MYSTR, 1,
|
||||
/* Logging */
|
||||
-1, -1, INIT_MYSTR, 0, 0, 0, INIT_MYSTR, 0,
|
||||
/* Buffers */
|
||||
@ -149,7 +149,7 @@ main(int argc, const char* argv[])
|
||||
tunable_banned_email_file, VSFTP_CONF_FILE_MAX);
|
||||
if (vsf_sysutil_retval_is_error(retval))
|
||||
{
|
||||
die2("cannot open banned e-mail list file:", tunable_banned_email_file);
|
||||
die2("cannot open anon e-mail list file:", tunable_banned_email_file);
|
||||
}
|
||||
}
|
||||
if (tunable_banner_file)
|
||||
@ -161,6 +161,16 @@ main(int argc, const char* argv[])
|
||||
die2("cannot open banner file:", tunable_banner_file);
|
||||
}
|
||||
}
|
||||
if (tunable_secure_email_list_enable)
|
||||
{
|
||||
int retval = str_fileread(&the_session.email_passwords_str,
|
||||
tunable_email_password_file,
|
||||
VSFTP_CONF_FILE_MAX);
|
||||
if (vsf_sysutil_retval_is_error(retval))
|
||||
{
|
||||
die2("cannot open email passwords file:", tunable_email_password_file);
|
||||
}
|
||||
}
|
||||
/* Special case - can force one process model if we've got a setup
|
||||
* needing _no_ privs
|
||||
*/
|
||||
|
50
parseconf.c
50
parseconf.c
@ -83,6 +83,7 @@ parseconf_bool_array[] =
|
||||
{ "download_enable", &tunable_download_enable },
|
||||
{ "dirlist_enable", &tunable_dirlist_enable },
|
||||
{ "chmod_enable", &tunable_chmod_enable },
|
||||
{ "secure_email_list_enable", &tunable_secure_email_list_enable },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
@ -140,6 +141,10 @@ parseconf_str_array[] =
|
||||
{ "user_config_dir", &tunable_user_config_dir },
|
||||
{ "listen_address6", &tunable_listen_address6 },
|
||||
{ "cmds_allowed", &tunable_cmds_allowed },
|
||||
{ "hide_file", &tunable_hide_file },
|
||||
{ "deny_file", &tunable_deny_file },
|
||||
{ "user_sub_token", &tunable_user_sub_token },
|
||||
{ "email_password_file", &tunable_email_password_file },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
@ -208,6 +213,32 @@ static void
|
||||
handle_config_setting(struct mystr* p_setting_str, struct mystr* p_value_str,
|
||||
int errs_fatal)
|
||||
{
|
||||
/* Is it a string setting? */
|
||||
{
|
||||
const struct parseconf_str_setting* p_str_setting = parseconf_str_array;
|
||||
while (p_str_setting->p_setting_name != 0)
|
||||
{
|
||||
if (str_equal_text(p_setting_str, p_str_setting->p_setting_name))
|
||||
{
|
||||
/* Got it */
|
||||
const char** p_curr_setting = p_str_setting->p_variable;
|
||||
if (*p_curr_setting)
|
||||
{
|
||||
vsf_sysutil_free((char*)*p_curr_setting);
|
||||
}
|
||||
if (str_isempty(p_value_str))
|
||||
{
|
||||
*p_curr_setting = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
*p_curr_setting = str_strdup(p_value_str);
|
||||
}
|
||||
return;
|
||||
}
|
||||
p_str_setting++;
|
||||
}
|
||||
}
|
||||
if (str_isempty(p_value_str))
|
||||
{
|
||||
if (errs_fatal)
|
||||
@ -273,25 +304,6 @@ handle_config_setting(struct mystr* p_setting_str, struct mystr* p_value_str,
|
||||
p_uint_setting++;
|
||||
}
|
||||
}
|
||||
/* Is it a string setting? */
|
||||
{
|
||||
const struct parseconf_str_setting* p_str_setting = parseconf_str_array;
|
||||
while (p_str_setting->p_setting_name != 0)
|
||||
{
|
||||
if (str_equal_text(p_setting_str, p_str_setting->p_setting_name))
|
||||
{
|
||||
/* Got it */
|
||||
const char** p_curr_setting = p_str_setting->p_variable;
|
||||
if (*p_curr_setting)
|
||||
{
|
||||
vsf_sysutil_free((char*)*p_curr_setting);
|
||||
}
|
||||
*p_curr_setting = str_strdup(p_value_str);
|
||||
return;
|
||||
}
|
||||
p_str_setting++;
|
||||
}
|
||||
}
|
||||
if (errs_fatal)
|
||||
{
|
||||
die2("unrecognised variable in config file: ", str_getbuf(p_setting_str));
|
||||
|
272
postlogin.c
272
postlogin.c
@ -22,6 +22,7 @@
|
||||
#include "logging.h"
|
||||
#include "sysdeputil.h"
|
||||
#include "ipv6parse.h"
|
||||
#include "access.h"
|
||||
|
||||
/* Private local functions */
|
||||
static void handle_pwd(struct vsf_session* p_sess);
|
||||
@ -51,12 +52,15 @@ static void handle_site_umask(struct vsf_session* p_sess,
|
||||
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 void handle_stat(struct vsf_session* p_sess);
|
||||
static void handle_stat_file(struct vsf_session* p_sess);
|
||||
|
||||
static int pasv_active(struct vsf_session* p_sess);
|
||||
static int port_active(struct vsf_session* p_sess);
|
||||
static void pasv_cleanup(struct vsf_session* p_sess);
|
||||
static void port_cleanup(struct vsf_session* p_sess);
|
||||
static void handle_dir_common(struct vsf_session* p_sess, int full_details);
|
||||
static void handle_dir_common(struct vsf_session* p_sess, int full_details,
|
||||
int stat_cmd);
|
||||
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);
|
||||
@ -158,7 +162,8 @@ process_post_login(struct vsf_session* p_sess)
|
||||
}
|
||||
else if (tunable_pasv_enable &&
|
||||
!p_sess->epsv_all &&
|
||||
str_equal_text(&p_sess->ftp_cmd_str, "PASV"))
|
||||
(str_equal_text(&p_sess->ftp_cmd_str, "PASV") ||
|
||||
str_equal_text(&p_sess->ftp_cmd_str, "P@SW")))
|
||||
{
|
||||
handle_pasv(p_sess, 0);
|
||||
}
|
||||
@ -255,7 +260,11 @@ process_post_login(struct vsf_session* p_sess)
|
||||
{
|
||||
handle_site(p_sess);
|
||||
}
|
||||
else if (str_equal_text(&p_sess->ftp_cmd_str, "ABOR"))
|
||||
/* Note - the weird ABOR string is checking for an async ABOR arriving
|
||||
* without a SIGURG condition.
|
||||
*/
|
||||
else if (str_equal_text(&p_sess->ftp_cmd_str, "ABOR") ||
|
||||
str_equal_text(&p_sess->ftp_cmd_str, "\377\364\377\362ABOR"))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_ABOR_NOCONN, "No transfer to ABOR.");
|
||||
}
|
||||
@ -302,6 +311,44 @@ process_post_login(struct vsf_session* p_sess)
|
||||
{
|
||||
handle_stou(p_sess);
|
||||
}
|
||||
else if (str_equal_text(&p_sess->ftp_cmd_str, "ALLO"))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_ALLOOK, "ALLO command ignored.");
|
||||
}
|
||||
else if (str_equal_text(&p_sess->ftp_cmd_str, "REIN"))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_COMMANDNOTIMPL, "REIN not implemented.");
|
||||
}
|
||||
else if (str_equal_text(&p_sess->ftp_cmd_str, "ACCT"))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_COMMANDNOTIMPL, "ACCT not implemented.");
|
||||
}
|
||||
else if (str_equal_text(&p_sess->ftp_cmd_str, "SMNT"))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_COMMANDNOTIMPL, "SMNT not implemented.");
|
||||
}
|
||||
else if (str_equal_text(&p_sess->ftp_cmd_str, "FEAT"))
|
||||
{
|
||||
vsf_cmdio_write_hyphen(p_sess, FTP_FEAT, "Features:");
|
||||
vsf_cmdio_write_raw(p_sess, " MDTM\r\n");
|
||||
vsf_cmdio_write_raw(p_sess, " REST STREAM\r\n");
|
||||
vsf_cmdio_write_raw(p_sess, " SIZE\r\n");
|
||||
vsf_cmdio_write(p_sess, FTP_FEAT, "End");
|
||||
}
|
||||
else if (str_equal_text(&p_sess->ftp_cmd_str, "OPTS"))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADOPTS, "Option not understood.");
|
||||
}
|
||||
else if (str_equal_text(&p_sess->ftp_cmd_str, "STAT") &&
|
||||
str_isempty(&p_sess->ftp_arg_str))
|
||||
{
|
||||
handle_stat(p_sess);
|
||||
}
|
||||
else if (tunable_dirlist_enable &&
|
||||
str_equal_text(&p_sess->ftp_cmd_str, "STAT"))
|
||||
{
|
||||
handle_stat_file(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") ||
|
||||
@ -319,7 +366,14 @@ process_post_login(struct vsf_session* p_sess)
|
||||
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"))
|
||||
str_equal_text(&p_sess->ftp_cmd_str, "STOU") ||
|
||||
str_equal_text(&p_sess->ftp_cmd_str, "ALLO") ||
|
||||
str_equal_text(&p_sess->ftp_cmd_str, "REIN") ||
|
||||
str_equal_text(&p_sess->ftp_cmd_str, "ACCT") ||
|
||||
str_equal_text(&p_sess->ftp_cmd_str, "SMNT") ||
|
||||
str_equal_text(&p_sess->ftp_cmd_str, "FEAT") ||
|
||||
str_equal_text(&p_sess->ftp_cmd_str, "OPTS") ||
|
||||
str_equal_text(&p_sess->ftp_cmd_str, "STAT"))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
|
||||
}
|
||||
@ -348,7 +402,13 @@ handle_pwd(struct vsf_session* p_sess)
|
||||
static void
|
||||
handle_cwd(struct vsf_session* p_sess)
|
||||
{
|
||||
int retval = str_chdir(&p_sess->ftp_arg_str);
|
||||
int retval;
|
||||
if (!vsf_access_check_file(&p_sess->ftp_arg_str))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
|
||||
return;
|
||||
}
|
||||
retval = str_chdir(&p_sess->ftp_arg_str);
|
||||
if (retval == 0)
|
||||
{
|
||||
/* Handle any messages */
|
||||
@ -456,6 +516,7 @@ handle_pasv(struct vsf_session* p_sess, int is_epsv)
|
||||
{
|
||||
p_sess->pasv_listen_fd = vsf_sysutil_get_ipv4_sock();
|
||||
}
|
||||
vsf_sysutil_activate_reuseaddr(p_sess->pasv_listen_fd);
|
||||
while (--bind_retries)
|
||||
{
|
||||
int retval;
|
||||
@ -463,11 +524,11 @@ handle_pasv(struct vsf_session* p_sess, int is_epsv)
|
||||
/* IPPORT_RESERVED */
|
||||
unsigned short min_port = 1024;
|
||||
unsigned short max_port = 65535;
|
||||
if (tunable_pasv_min_port > min_port && tunable_pasv_min_port < max_port)
|
||||
if (tunable_pasv_min_port > min_port && tunable_pasv_min_port <= max_port)
|
||||
{
|
||||
min_port = tunable_pasv_min_port;
|
||||
}
|
||||
if (tunable_pasv_max_port > min_port && tunable_pasv_max_port < max_port)
|
||||
if (tunable_pasv_max_port >= min_port && tunable_pasv_max_port < max_port)
|
||||
{
|
||||
max_port = tunable_pasv_max_port;
|
||||
}
|
||||
@ -475,8 +536,8 @@ handle_pasv(struct vsf_session* p_sess, int is_epsv)
|
||||
the_port <<= 8;
|
||||
the_port |= vsf_sysutil_get_random_byte();
|
||||
scaled_port = (double) min_port;
|
||||
scaled_port += ((double) the_port / (double) 65535) *
|
||||
((double) max_port - min_port);
|
||||
scaled_port += ((double) the_port / (double) 65536) *
|
||||
((double) max_port - min_port + 1);
|
||||
the_port = (unsigned short) scaled_port;
|
||||
vsf_sysutil_sockaddr_clone(&s_p_sockaddr, p_sess->p_local_addr);
|
||||
vsf_sysutil_sockaddr_set_port(s_p_sockaddr, the_port);
|
||||
@ -557,6 +618,11 @@ handle_retr(struct vsf_session* p_sess)
|
||||
"No support for resume of ASCII transfer.");
|
||||
return;
|
||||
}
|
||||
if (!vsf_access_check_file(&p_sess->ftp_arg_str))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
|
||||
return;
|
||||
}
|
||||
opened_file = str_open(&p_sess->ftp_arg_str, kVSFSysStrOpenReadOnly);
|
||||
if (vsf_sysutil_retval_is_error(opened_file))
|
||||
{
|
||||
@ -631,11 +697,11 @@ file_close_out:
|
||||
static void
|
||||
handle_list(struct vsf_session* p_sess)
|
||||
{
|
||||
handle_dir_common(p_sess, 1);
|
||||
handle_dir_common(p_sess, 1, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_dir_common(struct vsf_session* p_sess, int full_details)
|
||||
handle_dir_common(struct vsf_session* p_sess, int full_details, int stat_cmd)
|
||||
{
|
||||
static struct mystr s_option_str;
|
||||
static struct mystr s_filter_str;
|
||||
@ -644,11 +710,12 @@ handle_dir_common(struct vsf_session* p_sess, int full_details)
|
||||
int remote_fd;
|
||||
int dir_allow_read = 1;
|
||||
struct vsf_sysutil_dir* p_dir = 0;
|
||||
int retval = 0;
|
||||
str_empty(&s_option_str);
|
||||
str_empty(&s_filter_str);
|
||||
/* By default open the current directory */
|
||||
str_alloc_text(&s_dir_name_str, ".");
|
||||
if (!pasv_active(p_sess) && !port_active(p_sess))
|
||||
if (!stat_cmd && !pasv_active(p_sess) && !port_active(p_sess))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Use PORT or PASV first.");
|
||||
return;
|
||||
@ -671,6 +738,11 @@ handle_dir_common(struct vsf_session* p_sess, int full_details)
|
||||
}
|
||||
if (!str_isempty(&s_filter_str))
|
||||
{
|
||||
if (!vsf_access_check_file(&s_filter_str))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
|
||||
return;
|
||||
}
|
||||
/* First check - is it an outright directory, as in "ls /pub" */
|
||||
p_dir = str_opendir(&s_filter_str);
|
||||
if (p_dir != 0)
|
||||
@ -706,12 +778,27 @@ handle_dir_common(struct vsf_session* p_sess, int full_details)
|
||||
p_dir = str_opendir(&s_dir_name_str);
|
||||
}
|
||||
/* Fine, do it */
|
||||
remote_fd = get_remote_transfer_fd(p_sess);
|
||||
if (stat_cmd)
|
||||
{
|
||||
remote_fd = VSFTP_COMMAND_FD;
|
||||
str_append_char(&s_option_str, 'a');
|
||||
}
|
||||
else
|
||||
{
|
||||
remote_fd = get_remote_transfer_fd(p_sess);
|
||||
}
|
||||
if (vsf_sysutil_retval_is_error(remote_fd))
|
||||
{
|
||||
goto dir_close_out;
|
||||
}
|
||||
vsf_cmdio_write(p_sess, FTP_DATACONN, "Here comes the directory listing.");
|
||||
if (stat_cmd)
|
||||
{
|
||||
vsf_cmdio_write_hyphen(p_sess, FTP_STATFILE_OK, "Status follows:");
|
||||
}
|
||||
else
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_DATACONN, "Here comes the directory listing.");
|
||||
}
|
||||
if (p_sess->is_anonymous && p_dir && tunable_anon_world_readable_only)
|
||||
{
|
||||
vsf_sysutil_dir_stat(p_dir, &s_p_dirstat);
|
||||
@ -720,25 +807,43 @@ handle_dir_common(struct vsf_session* p_sess, int full_details)
|
||||
dir_allow_read = 0;
|
||||
}
|
||||
}
|
||||
if (p_dir == 0 || !dir_allow_read)
|
||||
if (p_dir != 0 && dir_allow_read)
|
||||
{
|
||||
retval = vsf_ftpdataio_transfer_dir(p_sess, remote_fd, p_dir,
|
||||
&s_dir_name_str, &s_option_str,
|
||||
&s_filter_str, full_details);
|
||||
}
|
||||
if (stat_cmd)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_STATFILE_OK, "End of status");
|
||||
}
|
||||
else if (p_dir == 0 || !dir_allow_read)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_TRANSFEROK,
|
||||
"Transfer done (but failed to open directory).");
|
||||
}
|
||||
else if (retval == 0)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "Directory send OK.");
|
||||
}
|
||||
else
|
||||
{
|
||||
(void) vsf_ftpdataio_transfer_dir(p_sess, remote_fd, p_dir,
|
||||
&s_dir_name_str, &s_option_str,
|
||||
&s_filter_str, full_details);
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDNET, "Failure writing network stream.");
|
||||
}
|
||||
if (!stat_cmd)
|
||||
{
|
||||
(void) dispose_remote_transfer_fd(p_sess);
|
||||
}
|
||||
(void) dispose_remote_transfer_fd(p_sess);
|
||||
dir_close_out:
|
||||
if (p_dir)
|
||||
{
|
||||
vsf_sysutil_closedir(p_dir);
|
||||
}
|
||||
port_cleanup(p_sess);
|
||||
pasv_cleanup(p_sess);
|
||||
if (!stat_cmd)
|
||||
{
|
||||
port_cleanup(p_sess);
|
||||
pasv_cleanup(p_sess);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -852,6 +957,11 @@ handle_upload_common(struct vsf_session* p_sess, int is_append, int is_unique)
|
||||
get_unique_filename(&s_filename, p_filename);
|
||||
p_filename = &s_filename;
|
||||
}
|
||||
if (!vsf_access_check_file(p_filename))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
|
||||
return;
|
||||
}
|
||||
/* NOTE - actual file permissions will be governed by the tunable umask */
|
||||
/* XXX - do we care about race between create and chown() of anonymous
|
||||
* upload?
|
||||
@ -947,6 +1057,11 @@ static void
|
||||
handle_mkd(struct vsf_session* p_sess)
|
||||
{
|
||||
int retval;
|
||||
if (!vsf_access_check_file(&p_sess->ftp_arg_str))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
|
||||
return;
|
||||
}
|
||||
vsf_log_start_entry(p_sess, kVSFLogEntryMkdir);
|
||||
str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
|
||||
prepend_path_to_filename(&p_sess->log_str);
|
||||
@ -978,7 +1093,13 @@ handle_mkd(struct vsf_session* p_sess)
|
||||
static void
|
||||
handle_rmd(struct vsf_session* p_sess)
|
||||
{
|
||||
int retval = str_rmdir(&p_sess->ftp_arg_str);
|
||||
int retval;
|
||||
if (!vsf_access_check_file(&p_sess->ftp_arg_str))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
|
||||
return;
|
||||
}
|
||||
retval = str_rmdir(&p_sess->ftp_arg_str);
|
||||
if (retval != 0)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_FILEFAIL,
|
||||
@ -994,7 +1115,13 @@ handle_rmd(struct vsf_session* p_sess)
|
||||
static void
|
||||
handle_dele(struct vsf_session* p_sess)
|
||||
{
|
||||
int retval = str_unlink(&p_sess->ftp_arg_str);
|
||||
int retval;
|
||||
if (!vsf_access_check_file(&p_sess->ftp_arg_str))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
|
||||
return;
|
||||
}
|
||||
retval = str_unlink(&p_sess->ftp_arg_str);
|
||||
if (retval != 0)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Delete operation failed.");
|
||||
@ -1028,6 +1155,11 @@ handle_rnfr(struct vsf_session* p_sess)
|
||||
int retval;
|
||||
/* Clear old value */
|
||||
str_free(&p_sess->rnfr_filename_str);
|
||||
if (!vsf_access_check_file(&p_sess->ftp_arg_str))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
|
||||
return;
|
||||
}
|
||||
/* Does it exist? */
|
||||
retval = str_stat(&p_sess->ftp_arg_str, &p_statbuf);
|
||||
if (retval == 0)
|
||||
@ -1053,6 +1185,11 @@ handle_rnto(struct vsf_session* p_sess)
|
||||
"RNFR required first.");
|
||||
return;
|
||||
}
|
||||
if (!vsf_access_check_file(&p_sess->ftp_arg_str))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
|
||||
return;
|
||||
}
|
||||
/* NOTE - might overwrite destination file. Not a concern because the same
|
||||
* could be accomplished with DELE.
|
||||
*/
|
||||
@ -1072,7 +1209,7 @@ handle_rnto(struct vsf_session* p_sess)
|
||||
static void
|
||||
handle_nlst(struct vsf_session* p_sess)
|
||||
{
|
||||
handle_dir_common(p_sess, 0);
|
||||
handle_dir_common(p_sess, 0, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1180,7 +1317,13 @@ handle_size(struct vsf_session* p_sess)
|
||||
* I will not do it because it is a potential I/O DoS.
|
||||
*/
|
||||
static struct vsf_sysutil_statbuf* s_p_statbuf;
|
||||
int retval = str_stat(&p_sess->ftp_arg_str, &s_p_statbuf);
|
||||
int retval;
|
||||
if (!vsf_access_check_file(&p_sess->ftp_arg_str))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
|
||||
return;
|
||||
}
|
||||
retval = str_stat(&p_sess->ftp_arg_str, &s_p_statbuf);
|
||||
if (retval != 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_FILEFAIL, "Could not get file size.");
|
||||
@ -1238,6 +1381,11 @@ handle_site_chmod(struct vsf_session* p_sess, struct mystr* p_arg_str)
|
||||
vsf_cmdio_write(p_sess, FTP_BADCMD, "SITE CHMOD needs 2 arguments.");
|
||||
return;
|
||||
}
|
||||
if (!vsf_access_check_file(&s_chmod_file_str))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
|
||||
return;
|
||||
}
|
||||
/* Don't worry - our chmod() implementation only allows 0 - 0777 */
|
||||
perms = str_octal_to_uint(p_arg_str);
|
||||
retval = str_chmod(&s_chmod_file_str, perms);
|
||||
@ -1284,7 +1432,13 @@ static void
|
||||
handle_mdtm(struct vsf_session* p_sess)
|
||||
{
|
||||
static struct vsf_sysutil_statbuf* s_p_statbuf;
|
||||
int retval = str_stat(&p_sess->ftp_arg_str, &s_p_statbuf);
|
||||
int retval;
|
||||
if (!vsf_access_check_file(&p_sess->ftp_arg_str))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
|
||||
return;
|
||||
}
|
||||
retval = str_stat(&p_sess->ftp_arg_str, &s_p_statbuf);
|
||||
if (retval != 0 || !vsf_sysutil_statbuf_is_regfile(s_p_statbuf))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_FILEFAIL,
|
||||
@ -1372,13 +1526,15 @@ static void
|
||||
handle_help(struct vsf_session* p_sess)
|
||||
{
|
||||
vsf_cmdio_write_hyphen(p_sess, FTP_HELP,
|
||||
"The following commands are implemented.");
|
||||
"The following commands are recognized.");
|
||||
vsf_cmdio_write_raw(p_sess,
|
||||
" ABOR APPE CDUP CWD DELE EPRT EPSV HELP LIST MDTM MKD MODE NLST NOOP\r\n");
|
||||
" ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD\r\n");
|
||||
vsf_cmdio_write_raw(p_sess,
|
||||
" PASS PASV PORT PWD QUIT REST RETR RMD RNFR RNTO SITE SIZE STOR STOU\r\n");
|
||||
" MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR\r\n");
|
||||
vsf_cmdio_write_raw(p_sess,
|
||||
" STRU SYST TYPE USER XCUP XCWD XMKD XPWD XRMD\r\n");
|
||||
" RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD\r\n");
|
||||
vsf_cmdio_write_raw(p_sess,
|
||||
" XPWD XRMD\r\n");
|
||||
vsf_cmdio_write(p_sess, FTP_HELP, "Help OK.");
|
||||
}
|
||||
|
||||
@ -1409,3 +1565,59 @@ get_unique_filename(struct mystr* p_outstr, const struct mystr* p_base_str)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_stat(struct vsf_session* p_sess)
|
||||
{
|
||||
vsf_cmdio_write_hyphen(p_sess, FTP_STATOK, "FTP server status:");
|
||||
vsf_cmdio_write_raw(p_sess, " Connected to ");
|
||||
vsf_cmdio_write_raw(p_sess, str_getbuf(&p_sess->remote_ip_str));
|
||||
vsf_cmdio_write_raw(p_sess, "\r\n");
|
||||
vsf_cmdio_write_raw(p_sess, " Logged in as ");
|
||||
vsf_cmdio_write_raw(p_sess, str_getbuf(&p_sess->user_str));
|
||||
vsf_cmdio_write_raw(p_sess, "\r\n");
|
||||
vsf_cmdio_write_raw(p_sess, " TYPE: ");
|
||||
if (p_sess->is_ascii)
|
||||
{
|
||||
vsf_cmdio_write_raw(p_sess, "ASCII\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
vsf_cmdio_write_raw(p_sess, "BINARY\r\n");
|
||||
}
|
||||
if (p_sess->bw_rate_max == 0)
|
||||
{
|
||||
vsf_cmdio_write_raw(p_sess, " No session bandwidth limit\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
vsf_cmdio_write_raw(p_sess, " Session bandwidth limit in byte/s is ");
|
||||
vsf_cmdio_write_raw(p_sess, vsf_sysutil_ulong_to_str(p_sess->bw_rate_max));
|
||||
vsf_cmdio_write_raw(p_sess, "\r\n");
|
||||
}
|
||||
if (tunable_idle_session_timeout == 0)
|
||||
{
|
||||
vsf_cmdio_write_raw(p_sess, " No session timeout\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
vsf_cmdio_write_raw(p_sess, " Session timeout in seconds is ");
|
||||
vsf_cmdio_write_raw(p_sess,
|
||||
vsf_sysutil_ulong_to_str(tunable_idle_session_timeout));
|
||||
vsf_cmdio_write_raw(p_sess, "\r\n");
|
||||
}
|
||||
if (p_sess->num_clients > 0)
|
||||
{
|
||||
vsf_cmdio_write_raw(p_sess, " At session startup, client count was ");
|
||||
vsf_cmdio_write_raw(p_sess, vsf_sysutil_ulong_to_str(p_sess->num_clients));
|
||||
vsf_cmdio_write_raw(p_sess, "\r\n");
|
||||
}
|
||||
vsf_cmdio_write_raw(p_sess, " vsFTPd - secure, fast, stable\r\n");
|
||||
vsf_cmdio_write(p_sess, FTP_STATOK, "End of status");
|
||||
}
|
||||
|
||||
static void
|
||||
handle_stat_file(struct vsf_session* p_sess)
|
||||
{
|
||||
handle_dir_common(p_sess, 1, 1);
|
||||
}
|
||||
|
||||
|
@ -160,6 +160,12 @@ handle_anonymous_login(struct vsf_session* p_sess,
|
||||
{
|
||||
return kVSFLoginFail;
|
||||
}
|
||||
if (!str_isempty(&p_sess->email_passwords_str) &&
|
||||
(!str_contains_line(&p_sess->email_passwords_str, p_pass_str) ||
|
||||
str_isempty(p_pass_str)))
|
||||
{
|
||||
return kVSFLoginFail;
|
||||
}
|
||||
/* Store the anonymous identity string */
|
||||
str_copy(&p_sess->anon_pass_str, p_pass_str);
|
||||
if (str_isempty(&p_sess->anon_pass_str))
|
||||
@ -176,6 +182,7 @@ handle_anonymous_login(struct vsf_session* p_sess,
|
||||
str_free(&ftp_username_str);
|
||||
}
|
||||
str_free(&p_sess->banned_email_str);
|
||||
str_free(&p_sess->email_passwords_str);
|
||||
return kVSFLoginAnon;
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ vsf_secutil_change_credentials(const struct mystr* p_user_str,
|
||||
{
|
||||
struct mystr dir_str = INIT_MYSTR;
|
||||
/* Work out where the chroot() jail is */
|
||||
if (p_dir_str == 0)
|
||||
if (p_dir_str == 0 || str_isempty(p_dir_str))
|
||||
{
|
||||
str_alloc_text(&dir_str, vsf_sysutil_user_get_homedir(p_user));
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ struct vsf_session
|
||||
|
||||
/* Things we need to cache before we chroot() */
|
||||
struct mystr banned_email_str;
|
||||
struct mystr email_passwords_str;
|
||||
struct mystr userlist_str;
|
||||
struct mystr banner_str;
|
||||
int tcp_wrapper_ok;
|
||||
|
29
str.c
29
str.c
@ -317,15 +317,17 @@ str_replace_text(struct mystr* p_str, const char* p_from, const char* p_to)
|
||||
{
|
||||
static struct mystr s_lhs_chunk_str;
|
||||
static struct mystr s_rhs_chunk_str;
|
||||
unsigned int lhs_len;
|
||||
str_copy(&s_lhs_chunk_str, p_str);
|
||||
str_free(p_str);
|
||||
do
|
||||
{
|
||||
lhs_len = str_getlen(&s_lhs_chunk_str);
|
||||
str_split_text(&s_lhs_chunk_str, &s_rhs_chunk_str, p_from);
|
||||
/* Copy lhs to destination */
|
||||
str_append_str(p_str, &s_lhs_chunk_str);
|
||||
/* If this was a 'hit', append the 'to' text */
|
||||
if (!str_isempty(&s_rhs_chunk_str))
|
||||
if (str_getlen(&s_lhs_chunk_str) < lhs_len)
|
||||
{
|
||||
str_append_text(p_str, p_to);
|
||||
}
|
||||
@ -422,6 +424,31 @@ str_locate_char(const struct mystr* p_str, char look_char)
|
||||
return str_locate_text(p_str, look_str);
|
||||
}
|
||||
|
||||
struct str_locate_result
|
||||
str_locate_chars(const struct mystr* p_str, const char* p_chars)
|
||||
{
|
||||
struct str_locate_result retval;
|
||||
unsigned int num_chars = vsf_sysutil_strlen(p_chars);
|
||||
unsigned int i = 0;
|
||||
retval.found = 0;
|
||||
for (; i < p_str->len; ++i)
|
||||
{
|
||||
unsigned int j = 0;
|
||||
char this_char = p_str->p_buf[i];
|
||||
for (; j < num_chars; ++j)
|
||||
{
|
||||
if (p_chars[j] == this_char)
|
||||
{
|
||||
retval.found = 1;
|
||||
retval.index = i;
|
||||
retval.char_found = p_chars[j];
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct str_locate_result
|
||||
str_locate_text(const struct mystr* p_str, const char* p_text)
|
||||
{
|
||||
|
3
str.h
3
str.h
@ -69,6 +69,7 @@ struct str_locate_result
|
||||
{
|
||||
int found;
|
||||
unsigned int index;
|
||||
char char_found;
|
||||
};
|
||||
|
||||
struct str_locate_result str_locate_char(
|
||||
@ -81,6 +82,8 @@ struct str_locate_result str_locate_text(
|
||||
const struct mystr* p_str, const char* p_text);
|
||||
struct str_locate_result str_locate_text_reverse(
|
||||
const struct mystr* p_str, const char* p_text);
|
||||
struct str_locate_result str_locate_chars(
|
||||
const struct mystr* p_str, const char* p_chars);
|
||||
|
||||
void str_left(const struct mystr* p_str, struct mystr* p_out,
|
||||
unsigned int chars);
|
||||
|
45
sysdeputil.c
45
sysdeputil.c
@ -35,6 +35,7 @@
|
||||
#undef VSF_SYSDEP_HAVE_LINUX_SENDFILE
|
||||
#undef VSF_SYSDEP_HAVE_FREEBSD_SENDFILE
|
||||
#undef VSF_SYSDEP_HAVE_HPUX_SENDFILE
|
||||
#undef VSF_SYSDEP_HAVE_AIX_SENDFILE
|
||||
#undef VSF_SYSDEP_HAVE_SETPROCTITLE
|
||||
#undef VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
|
||||
#undef VSF_SYSDEP_HAVE_HPUX_SETPROCTITLE
|
||||
@ -70,6 +71,17 @@
|
||||
#define VSF_SYSDEP_HAVE_SETPROCTITLE
|
||||
#endif
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
#include <stdlib.h>
|
||||
#define VSF_SYSDEP_HAVE_SETPROCTITLE
|
||||
#include <sys/param.h>
|
||||
#if __NetBSD_Version__ >= 106070000
|
||||
#define WTMPX_FILE _PATH_WTMPX
|
||||
#else
|
||||
#undef VSF_SYSDEP_HAVE_UTMPX
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __hpux
|
||||
#include <sys/socket.h>
|
||||
#ifdef SF_DISCONNECT
|
||||
@ -93,6 +105,18 @@
|
||||
#undef VSF_SYSDEP_HAVE_LIBCAP
|
||||
#endif
|
||||
|
||||
#ifdef _AIX
|
||||
#undef VSF_SYSDEP_HAVE_USERSHELL
|
||||
#undef VSF_SYSDEP_HAVE_LIBCAP
|
||||
#undef VSF_SYSDEP_HAVE_UTMPX
|
||||
#undef VSF_SYSDEP_HAVE_PAM
|
||||
#undef VSF_SYSDEP_HAVE_SHADOW
|
||||
#undef VSF_SYSDEP_HAVE_SETPROCTITLE
|
||||
#define VSF_SYSDEP_HAVE_AIX_SENDFILE
|
||||
#define VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
|
||||
#define VSF_SYSDEP_HAVE_MAP_ANON
|
||||
#endif
|
||||
|
||||
#ifdef __osf__
|
||||
#undef VSF_SYSDEP_HAVE_USERSHELL
|
||||
#endif
|
||||
@ -593,6 +617,7 @@ static int do_sendfile(const int out_fd, const int in_fd,
|
||||
#if defined(VSF_SYSDEP_HAVE_LINUX_SENDFILE) || \
|
||||
defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE) || \
|
||||
defined(VSF_SYSDEP_HAVE_HPUX_SENDFILE) || \
|
||||
defined(VSF_SYSDEP_HAVE_AIX_SENDFILE) || \
|
||||
defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE)
|
||||
if (tunable_use_sendfile)
|
||||
{
|
||||
@ -633,6 +658,24 @@ static int do_sendfile(const int out_fd, const int in_fd,
|
||||
retval = (int) written;
|
||||
}
|
||||
}
|
||||
#elif defined(VSF_SYSDEP_HAVE_AIX_SENDFILE)
|
||||
{
|
||||
struct sf_parms sf_iobuf;
|
||||
vsf_sysutil_memclr(&sf_iobuf, sizeof(sf_iobuf));
|
||||
sf_iobuf.header_data = NULL;
|
||||
sf_iobuf.header_length = 0;
|
||||
sf_iobuf.trailer_data = NULL;
|
||||
sf_iobuf.trailer_length = 0;
|
||||
sf_iobuf.file_descriptor = in_fd;
|
||||
sf_iobuf.file_offset = start_pos;
|
||||
sf_iobuf.file_bytes = num_send;
|
||||
|
||||
retval = send_file((int*)&out_fd, &sf_iobuf, 0);
|
||||
if (retval >= 0)
|
||||
{
|
||||
retval = sf_iobuf.bytes_sent;
|
||||
}
|
||||
}
|
||||
#else /* must be VSF_SYSDEP_HAVE_HPUX_SENDFILE */
|
||||
{
|
||||
retval = sendfile(out_fd, in_fd, start_pos, num_send, NULL, 0);
|
||||
@ -701,7 +744,7 @@ static int do_sendfile(const int out_fd, const int in_fd,
|
||||
total_written += num_written;
|
||||
if (num_written != num_read)
|
||||
{
|
||||
return -1;
|
||||
return num_written;
|
||||
}
|
||||
if (num_written > num_send)
|
||||
{
|
||||
|
@ -18,6 +18,8 @@
|
||||
|
||||
/* Activate 64-bit file support on Linux/32bit */
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
/* And Solaris.. */
|
||||
/*#define _LARGEFILE64_SOURCE 1*/
|
||||
|
||||
/* For Linux, this adds nothing :-) */
|
||||
#include "port/porting_junk.h"
|
||||
@ -2066,6 +2068,7 @@ vsf_sysutil_inet_ntop(const struct vsf_sysutil_sockaddr* p_sockptr)
|
||||
else
|
||||
{
|
||||
die("can only support ipv4 and ipv6 currently");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,6 +55,7 @@ int tunable_session_support = 0;
|
||||
int tunable_download_enable = 1;
|
||||
int tunable_dirlist_enable = 1;
|
||||
int tunable_chmod_enable = 1;
|
||||
int tunable_secure_email_list_enable = 0;
|
||||
|
||||
unsigned int tunable_accept_timeout = 60;
|
||||
unsigned int tunable_connect_timeout = 60;
|
||||
@ -97,3 +98,8 @@ 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;
|
||||
const char* tunable_hide_file = 0;
|
||||
const char* tunable_deny_file = 0;
|
||||
const char* tunable_user_sub_token = 0;
|
||||
const char* tunable_email_password_file = "/etc/vsftpd.email_passwords";
|
||||
|
||||
|
@ -51,6 +51,7 @@ 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) */
|
||||
extern int tunable_secure_email_list_enable; /* Require specific anon email */
|
||||
|
||||
/* Integer/numeric defines */
|
||||
extern unsigned int tunable_accept_timeout;
|
||||
@ -92,6 +93,10 @@ 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;
|
||||
extern const char* tunable_hide_file;
|
||||
extern const char* tunable_deny_file;
|
||||
extern const char* tunable_user_sub_token;
|
||||
extern const char* tunable_email_password_file;
|
||||
|
||||
#endif /* VSF_TUNABLES_H */
|
||||
|
||||
|
118
twoprocess.c
118
twoprocess.c
@ -31,9 +31,11 @@ static void common_do_login(struct vsf_session* p_sess,
|
||||
const struct mystr* p_user_str, int do_chroot,
|
||||
int anon);
|
||||
static void handle_per_user_config(const struct mystr* p_user_str);
|
||||
static void calculate_chdir_dir(int anon, struct mystr* p_chroot_str,
|
||||
static void calculate_chdir_dir(int anon, struct mystr* p_userdir_str,
|
||||
struct mystr* p_chroot_str,
|
||||
struct mystr* p_chdir_str,
|
||||
const struct mystr* p_user_str);
|
||||
const struct mystr* p_user_str,
|
||||
const struct mystr* p_orig_user_str);
|
||||
|
||||
static void
|
||||
handle_sigchld(int duff)
|
||||
@ -243,6 +245,7 @@ common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
|
||||
int do_chroot, int anon)
|
||||
{
|
||||
int was_anon = anon;
|
||||
const struct mystr* p_orig_user_str = p_user_str;
|
||||
int newpid;
|
||||
vsf_sysutil_install_null_sighandler(kVSFSysUtilSigCHLD);
|
||||
/* Asks the pre-login child to go away (by exiting) */
|
||||
@ -261,12 +264,8 @@ common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
|
||||
struct mystr guest_user_str = INIT_MYSTR;
|
||||
struct mystr chroot_str = INIT_MYSTR;
|
||||
struct mystr chdir_str = INIT_MYSTR;
|
||||
struct mystr userdir_str = INIT_MYSTR;
|
||||
unsigned int secutil_option = VSF_SECUTIL_OPTION_USE_GROUPS;
|
||||
calculate_chdir_dir(anon, &chroot_str, &chdir_str, p_user_str);
|
||||
if (do_chroot)
|
||||
{
|
||||
secutil_option |= VSF_SECUTIL_OPTION_CHROOT;
|
||||
}
|
||||
/* Child - drop privs and start proper FTP! */
|
||||
if (tunable_guest_enable && !anon)
|
||||
{
|
||||
@ -276,13 +275,20 @@ common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
|
||||
if (!tunable_virtual_use_local_privs)
|
||||
{
|
||||
anon = 1;
|
||||
do_chroot = 1;
|
||||
}
|
||||
}
|
||||
if (do_chroot)
|
||||
{
|
||||
secutil_option |= VSF_SECUTIL_OPTION_CHROOT;
|
||||
}
|
||||
if (!anon)
|
||||
{
|
||||
secutil_option |= VSF_SECUTIL_OPTION_CHANGE_EUID;
|
||||
}
|
||||
vsf_secutil_change_credentials(p_user_str, 0, &chroot_str,
|
||||
calculate_chdir_dir(was_anon, &userdir_str, &chroot_str, &chdir_str,
|
||||
p_user_str, p_orig_user_str);
|
||||
vsf_secutil_change_credentials(p_user_str, &userdir_str, &chroot_str,
|
||||
0, secutil_option);
|
||||
if (!str_isempty(&chdir_str))
|
||||
{
|
||||
@ -291,6 +297,7 @@ common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
|
||||
str_free(&guest_user_str);
|
||||
str_free(&chroot_str);
|
||||
str_free(&chdir_str);
|
||||
str_free(&userdir_str);
|
||||
/* Guard against the config error of having the anonymous ftp tree owned
|
||||
* by the user we are running as
|
||||
*/
|
||||
@ -310,59 +317,80 @@ common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
|
||||
static void
|
||||
handle_per_user_config(const struct mystr* p_user_str)
|
||||
{
|
||||
if (tunable_user_config_dir)
|
||||
struct mystr filename_str = INIT_MYSTR;
|
||||
struct vsf_sysutil_statbuf* p_statbuf = 0;
|
||||
struct str_locate_result loc_result;
|
||||
int retval;
|
||||
if (!tunable_user_config_dir)
|
||||
{
|
||||
struct mystr filename_str = INIT_MYSTR;
|
||||
struct vsf_sysutil_statbuf* p_statbuf = 0;
|
||||
int retval;
|
||||
str_alloc_text(&filename_str, tunable_user_config_dir);
|
||||
str_append_char(&filename_str, '/');
|
||||
str_append_str(&filename_str, p_user_str);
|
||||
retval = str_stat(&filename_str, &p_statbuf);
|
||||
/* Security - ignore unless owned by root */
|
||||
if (!vsf_sysutil_retval_is_error(retval) &&
|
||||
vsf_sysutil_statbuf_get_uid(p_statbuf) == VSFTP_ROOT_UID)
|
||||
{
|
||||
vsf_parseconf_load_file(str_getbuf(&filename_str), 1);
|
||||
}
|
||||
str_free(&filename_str);
|
||||
vsf_sysutil_free(p_statbuf);
|
||||
return;
|
||||
}
|
||||
/* Security paranoia - ignore if user has a / in it. */
|
||||
loc_result = str_locate_char(p_user_str, '/');
|
||||
if (loc_result.found)
|
||||
{
|
||||
return;
|
||||
}
|
||||
str_alloc_text(&filename_str, tunable_user_config_dir);
|
||||
str_append_char(&filename_str, '/');
|
||||
str_append_str(&filename_str, p_user_str);
|
||||
retval = str_stat(&filename_str, &p_statbuf);
|
||||
/* Security - ignore unless owned by root */
|
||||
if (!vsf_sysutil_retval_is_error(retval) &&
|
||||
vsf_sysutil_statbuf_get_uid(p_statbuf) == VSFTP_ROOT_UID)
|
||||
{
|
||||
vsf_parseconf_load_file(str_getbuf(&filename_str), 1);
|
||||
}
|
||||
str_free(&filename_str);
|
||||
vsf_sysutil_free(p_statbuf);
|
||||
}
|
||||
|
||||
static void
|
||||
calculate_chdir_dir(int anon, struct mystr* p_chroot_str,
|
||||
calculate_chdir_dir(int anon_login, struct mystr* p_userdir_str,
|
||||
struct mystr* p_chroot_str,
|
||||
struct mystr* p_chdir_str,
|
||||
const struct mystr* p_user_str)
|
||||
const struct mystr* p_user_str,
|
||||
const struct mystr* p_orig_user_str)
|
||||
{
|
||||
if (anon && tunable_anon_root)
|
||||
if (!anon_login)
|
||||
{
|
||||
str_alloc_text(p_chroot_str, tunable_anon_root);
|
||||
}
|
||||
else if (!anon && tunable_local_root)
|
||||
{
|
||||
str_alloc_text(p_chroot_str, tunable_local_root);
|
||||
}
|
||||
/* If enabled, the chroot() location embedded in the HOMEDIR takes
|
||||
* precedence.
|
||||
*/
|
||||
if (!anon && tunable_passwd_chroot_enable)
|
||||
{
|
||||
struct mystr homedir_str = INIT_MYSTR;
|
||||
const struct vsf_sysutil_user* p_user = str_getpwnam(p_user_str);
|
||||
struct str_locate_result loc_result;
|
||||
if (p_user == 0)
|
||||
{
|
||||
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, "/./");
|
||||
str_alloc_text(p_userdir_str, vsf_sysutil_user_get_homedir(p_user));
|
||||
if (tunable_user_sub_token)
|
||||
{
|
||||
str_replace_text(p_userdir_str, tunable_user_sub_token,
|
||||
str_getbuf(p_orig_user_str));
|
||||
}
|
||||
}
|
||||
if (anon_login && tunable_anon_root)
|
||||
{
|
||||
str_alloc_text(p_chroot_str, tunable_anon_root);
|
||||
}
|
||||
else if (!anon_login && tunable_local_root)
|
||||
{
|
||||
str_alloc_text(p_chroot_str, tunable_local_root);
|
||||
if (tunable_user_sub_token)
|
||||
{
|
||||
str_replace_text(p_chroot_str, tunable_user_sub_token,
|
||||
str_getbuf(p_orig_user_str));
|
||||
}
|
||||
}
|
||||
/* If enabled, the chroot() location embedded in the HOMEDIR takes
|
||||
* precedence.
|
||||
*/
|
||||
if (!anon_login && tunable_passwd_chroot_enable)
|
||||
{
|
||||
struct str_locate_result loc_result;
|
||||
loc_result = str_locate_text(p_userdir_str, "/./");
|
||||
if (loc_result.found)
|
||||
{
|
||||
str_split_text(&homedir_str, p_chdir_str, "/./");
|
||||
str_copy(p_chroot_str, &homedir_str);
|
||||
str_split_text(p_userdir_str, p_chdir_str, "/./");
|
||||
str_copy(p_chroot_str, p_userdir_str);
|
||||
}
|
||||
str_free(&homedir_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
13
vsftpd.8
13
vsftpd.8
@ -9,13 +9,22 @@
|
||||
.Op Ar configuration file
|
||||
.Sh DESCRIPTION
|
||||
.Nm vsftpd
|
||||
is the Very Secure File Transfer Protocol Daemon. The server should be
|
||||
invoked from a
|
||||
is the Very Secure File Transfer Protocol Daemon. The server can be launched
|
||||
via a
|
||||
.Dq super-server
|
||||
such as
|
||||
.Xr inetd 8
|
||||
or
|
||||
.Xr xinetd 8 .
|
||||
Alternatively, vsftpd can be launched in standalone mode, in which case vsftpd
|
||||
itself will listen on the network. This latter mode is easier to use, and
|
||||
recommended. It is activated by setting
|
||||
.Pa listen=YES
|
||||
in
|
||||
.Pa /etc/vsftpd.conf .
|
||||
Direct execution of the
|
||||
.Nm vsftpd
|
||||
binary will then launch the FTP service ready for immediate client connections.
|
||||
.Sh OPTIONS
|
||||
An optional
|
||||
.Op configuration file
|
||||
|
113
vsftpd.conf.5
113
vsftpd.conf.5
@ -88,7 +88,7 @@ Default: NO
|
||||
.TP
|
||||
.B async_abor_enable
|
||||
When enabled, a special FTP command known as "async ABOR" will be enabled.
|
||||
Only ill advised FTP clients will use this feature. Addtionally, this feature
|
||||
Only ill advised FTP clients will use this feature. Additionally, this feature
|
||||
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.
|
||||
@ -135,8 +135,8 @@ setting.
|
||||
Default: NO
|
||||
.TP
|
||||
.B chroot_local_user
|
||||
If set to YES, local users will be placed in a chroot() jail in their home
|
||||
directory after login.
|
||||
If set to YES, local users will be (by default) placed in a chroot() jail in
|
||||
their home directory after login.
|
||||
.BR Warning:
|
||||
This option has security implications, especially if the users have upload
|
||||
permission, or shell access. Only enable if you know what you are doing.
|
||||
@ -193,7 +193,7 @@ Default: NO
|
||||
.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.
|
||||
excludes the "." and ".." entries.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
@ -222,7 +222,7 @@ Default: NO
|
||||
.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.
|
||||
exclusive.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
@ -265,7 +265,7 @@ If enabled, along with
|
||||
.BR chroot_local_user
|
||||
, then a chroot() jail location may be specified on a per-user basis. Each
|
||||
user's jail is derived from their home directory string in /etc/passwd. The
|
||||
occurence of /./ in the home directory string denotes that the jail is at that
|
||||
occurrence of /./ in the home directory string denotes that the jail is at that
|
||||
particular location in the path.
|
||||
|
||||
Default: NO
|
||||
@ -280,7 +280,8 @@ Default: YES
|
||||
Set to YES if you want to disable the PASV security check that ensures the
|
||||
data connection originates from the same IP address as the control connection.
|
||||
Only enable if you know what you are doing! The only legitimate use for this
|
||||
is in some form of secure tunnelling scheme.
|
||||
is in some form of secure tunnelling scheme, or perhaps to facilitate FXP
|
||||
support.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
@ -295,6 +296,18 @@ Set to YES if you want to disable the PORT security check that ensures that
|
||||
outgoing data connections can only connect to the client. Only enable if
|
||||
you know what you are doing!
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
.B secure_email_list_enable
|
||||
Set to YES if you want only a specified list of e-mail passwords for anonymous
|
||||
logins to be accepted. This is useful as a low-hassle way of restricting
|
||||
access to low-security content without needing virtual users. When enabled,
|
||||
anonymous logins are prevented unless the password provided is listed in the
|
||||
file specified by the
|
||||
.BR email_password_file
|
||||
setting. The file format is one password per line, no extra whitespace. The
|
||||
default filename is /etc/vsftpd.email_passwords.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
.B session_support
|
||||
@ -552,9 +565,10 @@ The option is the name of a file containing a list of local users which
|
||||
will be placed in a chroot() jail in their home directory. This option is
|
||||
only relevant if the option
|
||||
.BR chroot_list_enable
|
||||
is enabled, and the option
|
||||
is enabled. If the option
|
||||
.BR chroot_local_user
|
||||
is disabled.
|
||||
is enabled, then the list file becomes a list of users to NOT place in a
|
||||
chroot() jail.
|
||||
|
||||
Default: /etc/vsftpd.chroot_list
|
||||
.TP
|
||||
@ -566,13 +580,33 @@ FTP server. Example: cmds_allowed=PASV,RETR,QUIT
|
||||
|
||||
Default: (none)
|
||||
.TP
|
||||
.B guest_username
|
||||
See the boolean setting
|
||||
.BR guest_enable
|
||||
for a description of what constitutes a guest login. This setting is the
|
||||
real username which guest users are mapped to.
|
||||
.B deny_file
|
||||
This option can be used to set a pattern for filenames (and directory names
|
||||
etc.) which should not be accessible in any way. The affected items are not
|
||||
hidden, but any attempt to do anything to them (download, change into
|
||||
directory, affect something within directory etc.) will be denied. This option
|
||||
is very simple, and should not be used for serious access control - the
|
||||
filesystem's permissions should be used in preference. However, this option
|
||||
may be useful in certain virtual user setups. In particular aware that if
|
||||
a filename is accessible by a variety of names (perhaps due to symbolic
|
||||
links or hard links), then care must be taken to deny access to all the names.
|
||||
Access will be denied to items if their name contains the string given by
|
||||
hide_file, or if they match the regular expression specified by hide_file.
|
||||
Note that vsftpd's regular expression matching code is a simple implementation
|
||||
which is a subset of full regular expression functionality. Because of this,
|
||||
you will need to carefully and exhaustively test any application of this
|
||||
option. And you are recommended to use filesystem permissions for any
|
||||
important security policies due to their greater reliability. Example:
|
||||
deny_file={*.mp3,*.mov,.private}
|
||||
|
||||
Default: ftp
|
||||
Default: (none)
|
||||
.TP
|
||||
.B email_password_file
|
||||
This option can be used to provide an alternate file for usage by the
|
||||
.BR secure_email_list_enable
|
||||
setting.
|
||||
|
||||
Default: /etc/vsftpd.email_passwords
|
||||
.TP
|
||||
.B ftp_username
|
||||
This is the name of the user we use for handling anonymous FTP. The home
|
||||
@ -586,6 +620,26 @@ by vsftpd when a connection first comes in.
|
||||
|
||||
Default: (none - default vsftpd banner is displayed)
|
||||
.TP
|
||||
.B guest_username
|
||||
See the boolean setting
|
||||
.BR guest_enable
|
||||
for a description of what constitutes a guest login. This setting is the
|
||||
real username which guest users are mapped to.
|
||||
|
||||
Default: ftp
|
||||
.TP
|
||||
.B hide_file
|
||||
This option can be used to set a pattern for filenames (and directory names
|
||||
etc.) which should be hidden from directory listings. Despite being hidden,
|
||||
the files / directories etc. are fully accessible to clients who know what
|
||||
names to actually use. Items will be hidden if their names contain the string
|
||||
given by hide_file, or if they match the regular expression specified by
|
||||
hide_file. Note that vsftpd's regular expression matching code is a simple
|
||||
implementation which is a subset of full regular expression functionality.
|
||||
Example: hide_file={*.mp3,.hidden,hide*,h?}
|
||||
|
||||
Default: (none)
|
||||
.TP
|
||||
.B listen_address
|
||||
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.
|
||||
@ -651,7 +705,32 @@ and then log on as the user "chris", then vsftpd will apply the settings in
|
||||
the file
|
||||
.BR /etc/vsftpd_user_conf/chris
|
||||
for the duration of the session. The format of this file is as detailed in
|
||||
this manual page!
|
||||
this manual page! PLEASE NOTE that not all settings are effective on a
|
||||
per-user basis. For example, many settings only prior to the user's session
|
||||
being started. Examples of settings which will not affect any behviour on
|
||||
a per-user basis include listen_address, banner_file, max_per_ip, max_clients,
|
||||
xferlog_file, etc.
|
||||
|
||||
Default: (none)
|
||||
.TP
|
||||
.B user_sub_token
|
||||
This option is useful is conjunction with virtual users. It is used to
|
||||
automatically generate a home directory for each virtual user, based on a
|
||||
template. For example, if the home directory of the real user specified via
|
||||
.BR guest_username
|
||||
is
|
||||
.BR /home/virtual/$USER ,
|
||||
and
|
||||
.BR user_sub_token
|
||||
is set to
|
||||
.BR $USER ,
|
||||
then when virtual user fred logs in, he will end up (usually chroot()'ed) in
|
||||
the directory
|
||||
.BR /home/virtual/fred .
|
||||
This option also takes affect if
|
||||
.BR local_root
|
||||
contains
|
||||
.BR user_sub_token .
|
||||
|
||||
Default: (none)
|
||||
.TP
|
||||
@ -661,7 +740,7 @@ This option is the name of the file loaded when the
|
||||
option is active.
|
||||
|
||||
Default: /etc/vsftpd.user_list
|
||||
.BR
|
||||
.TP
|
||||
.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
|
||||
|
@ -1,7 +1,7 @@
|
||||
#ifndef VSF_VERSION_H
|
||||
#define VSF_VERSION_H
|
||||
|
||||
#define VSF_VERSION "1.2.0"
|
||||
#define VSF_VERSION "1.2.1"
|
||||
|
||||
#endif /* VSF_VERSION_H */
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user