mirror of
https://github.com/InfrastructureServices/vsftpd.git
synced 2025-04-19 01:24:02 +03:00
Updated to v0.9.2
This commit is contained in:
commit
20e89955e0
35
AUDIT
Normal file
35
AUDIT
Normal file
@ -0,0 +1,35 @@
|
||||
This file contains information on the audit status of the code in this program.
|
||||
Each file has an audit code of between 1 and 5, ranging from 1 being unaudited
|
||||
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.
|
||||
|
||||
ascii.c 3
|
||||
dirchange.c 3
|
||||
filestr.c 3
|
||||
ftpcmdio.c 3
|
||||
ftpdataio.c 2
|
||||
logging.c 3
|
||||
ls.c 2
|
||||
main.c 3
|
||||
netstr.c 3
|
||||
oneprocess.c 3
|
||||
parseconf.c 2
|
||||
postlogin.c 2
|
||||
postprivparent.c 3
|
||||
prelogin.c 3
|
||||
privops.c 2
|
||||
privparent.c 3
|
||||
privsock.c 3
|
||||
secbuf.c 3
|
||||
secutil.c 3
|
||||
str.c 2
|
||||
strlist.c 2
|
||||
sysdeputil.c 2
|
||||
sysstr.c 3
|
||||
sysutil.c 2
|
||||
tunables.c 3
|
||||
twoprocess.c 2
|
||||
utility.c 3
|
||||
|
62
BENCHMARKS
Normal file
62
BENCHMARKS
Normal file
@ -0,0 +1,62 @@
|
||||
--
|
||||
Below are some quick benchmark figures vs. wu-ftpd. This is an untuned BETA
|
||||
version of vsftpd (0.0.10)
|
||||
|
||||
The executive summary is that wu-ftpd got a thorough thrashing. The most
|
||||
telling statistic is wu-ftpd typically failing to sustain 400 users, whereas
|
||||
vsftpd copes with 1000 with room to spare.
|
||||
|
||||
A 2.2.x kernel was used. A 2.4.x kernel should make vsftpd look even better
|
||||
relative to wu-ftpd thanks to the sendfile() boosts in 2.4.x. A 2.4.x kernel
|
||||
with zerocopy should be amazing.
|
||||
|
||||
Many thanks to Andrew Anderson <andrew@redhat.com>
|
||||
--
|
||||
|
||||
Here's some benchmarks that I did on vsftpd vs. wu-ftpd. The tests were
|
||||
run with "dkftpbench -hftpserver -n500 -t600 -f/pub/dkftp/<file>". The
|
||||
attached file are the summary output with time to reach the steady-state
|
||||
condition.
|
||||
|
||||
The interesting things I noticed are:
|
||||
|
||||
- In the raw test results, vsftpd had a much higher peak on the x10k.dat
|
||||
transfer run than wu-ftpd did. Wu-ftpd peaked at ~150 connections and
|
||||
bled down to ~130 connections, while vsftpd peaked at ~400 connections and
|
||||
bled down to ~160 connections. I tend to believe the peaks more than the
|
||||
final steady-state that dkftpbench reports, though.
|
||||
|
||||
- For the other tests, our wu-ftpd setup was limited to 400 connections,
|
||||
but in about half of the x100k/x1000k runs could not even sustain 400
|
||||
connections, while vsftpd handled 500 easily on those runs.
|
||||
|
||||
- During the peak runs at x10k, the machine load with vsftpd looked like
|
||||
this (I don't have this data still for the wu-ftpd runs):
|
||||
|
||||
01:01:00 AM all 4.92 0.00 21.23 73.85
|
||||
03:31:00 AM all 4.89 0.00 19.53 75.58
|
||||
05:11:00 AM all 4.19 0.00 16.89 78.92
|
||||
07:01:00 AM all 5.61 0.00 22.47 71.92
|
||||
|
||||
The steady-state loads were more in the 3-5% user, 10-15% system. For the
|
||||
x100/x1000 loads with vsftpd, the system load looked like this:
|
||||
|
||||
x100k.dat:
|
||||
09:01:00 AM all 2.27 0.00 9.79 87.94
|
||||
|
||||
x1000k.dat:
|
||||
11:01:00 AM all 0.42 0.00 5.75 93.83
|
||||
|
||||
Not bad -- 500 concurrent users for ~7% system load.
|
||||
|
||||
- Just for kicks I ran the x1000k test with 1000 users. At peak load:
|
||||
|
||||
X1000k.dat with 1000 users:
|
||||
04:41:00 PM all 1.23 0.00 46.59 52.18
|
||||
|
||||
Based on what I'm seeing, it looks like if a server had enough bandwidth,
|
||||
it could indeed sustain ~2000 users with the current 2 process model
|
||||
that's implemented in vsftpd. I did notice that dkftpbench slowed down
|
||||
the connection rate after 800 connections. I'm not sure if that was a
|
||||
dkftpbench issue, or if I ran into something other limit.
|
||||
|
25
BUGS
Normal file
25
BUGS
Normal file
@ -0,0 +1,25 @@
|
||||
BUGS
|
||||
====
|
||||
|
||||
This file, surprisingly enough, contains a list of known outstanding bugs
|
||||
in the program. Bugs that get documented here are typically not particularly
|
||||
serious, and may never get fixed. Serious bugs will get fixed immediately.
|
||||
|
||||
- RFC compliance: if we get no PORT or PASV, looks like we're supposed to
|
||||
assume a PORT to the same IP as control connection, and port 20.
|
||||
- RFC compliance: shouldn't include directories in NLST. Note that wu-ftpd
|
||||
complies here, almost all other FTPd's don't
|
||||
- ls <existing but unreadable dir> should list nothing, but it lists the
|
||||
directory name itself
|
||||
- In ASCII mode, the SIZE command needs to take into account the size of
|
||||
the file _after_ ASCII linefeed mangling?
|
||||
- ASCII mode uploads: we're only supposed to remove \r if they preceed \n,
|
||||
but I rip them out unconditionally.
|
||||
- "standard" xferlog format: we log dates like Feb 09, wu-ftpd logs like
|
||||
Feb 9.
|
||||
- Security model: vsf_privop_get_ftp_port_sock() should probably do the
|
||||
connect() to the remote location itself.
|
||||
- If a user's homedir doesn't exist, we ungracefully exit with "OOPS: chdir"
|
||||
- If someone has one of the timeouts (command, data) setup, but not the other,
|
||||
then timeout will behave whackily.
|
||||
|
500
Changelog
Normal file
500
Changelog
Normal file
@ -0,0 +1,500 @@
|
||||
0.0.1 initial versioned tarball released
|
||||
----------------------------------------
|
||||
|
||||
- Added "-ldl" to LIBS to get linking to work on RedHat6.1
|
||||
- Add RedHat6.1 on list of tested platforms :)
|
||||
|
||||
0.0.2 packaged
|
||||
--------------
|
||||
|
||||
- Emit version in greeting string
|
||||
- In PORT command, reject numbers <0 or >255. Problem noted by Solar Designer,
|
||||
<solar@openwall.com>
|
||||
- Allow an option AND a path for LIST/NLST, e.g. "LIST -al /pub". Reported by
|
||||
Bill Nottingham <notting@redhat.com>, using ncftp. Further noted by Colin
|
||||
Hogben <chah@jet.uk> using emacs and James Antill <james@and.org>.
|
||||
- Don't prepend directory path for LIST (but still so for NLST). Noted by
|
||||
Colin Hogben <chah@jet.uk> and Ingo Luetkebohle <ingo@blank.pages.de>
|
||||
- Fix problem listing non-existant or unreadable directories - just return
|
||||
a blank listing rather than an error. Problem noted by Martin Sillence
|
||||
<martin.sillence@prnewswire.co.uk>, using squid.
|
||||
- Fix KDE's downloads (via KFM), it was using the "SIZE" command which I had
|
||||
not implemented. Reported by Simon Dales <simonD@nuffield.co.uk> and Jo Dillon
|
||||
<jo@trolltech.com>. Apparently implementing SIZE also fixed lftp's download
|
||||
time estimator, reported by Ingo Luetkebohle <ingo@blank.pages.de>
|
||||
- Remove abornal_exit() from utility.c
|
||||
- Fix so we don't write "500 OOPS: child died" upon QUIT. Reported by Solar
|
||||
Designer, <solar@openwall.com> and Tim Bagot <tsb@earth.li>
|
||||
|
||||
0.0.3 packaged
|
||||
--------------
|
||||
|
||||
- Oops: fix so we don't emit a status 150 mark unless we actually got a
|
||||
connection from the client (stops some clients hanging trying to list an
|
||||
inaccessible directory)
|
||||
|
||||
0.0.4 packaged
|
||||
--------------
|
||||
|
||||
- In verbose directory listing, report symlink targets. Use the traditional
|
||||
syntax of: "link_name -> target_path"
|
||||
- Damn netscape! The comma in the response text to PASV confused it, so it
|
||||
had to be removed. Discovered with tcpdump!
|
||||
- Don't require clients to redo PORT or PASV if a RETR or STOR fails due
|
||||
to inability to open/create file. Fixes Netscape symlink navigation problem.
|
||||
- Fix for listing absolute paths with only one /, e.g. "ls /.message" was
|
||||
failing
|
||||
|
||||
0.0.5 packaged
|
||||
--------------
|
||||
|
||||
- Remove README.ftpproto
|
||||
- Add SECURITY/OVERVIEW
|
||||
- Add SECURITY/DESIGN
|
||||
- Note that as a security tweak, we should lose more privs if we're configured
|
||||
for anonymous only logins (TODO)
|
||||
- Add SECURITY/IMPLEMENTATION, SECURITY/TRUST, but nothing in them yet.
|
||||
- Convert str.c to vsf_sysutil_*. This leaves the following to do:
|
||||
checkauth.c, main.c, postprivparent.c, privparent.c, privsock.c, utility.c
|
||||
- Convert privparent.c to vsf_sysutil_*.
|
||||
- Create BUGS and move existing listed bugs from TODO into this new file
|
||||
- Add parseconf.h, parseconf.c to handle parsing of a config file (work in
|
||||
progress)
|
||||
- Fix change_full_credentials() in utility.c, to always chdir() even if we
|
||||
are not going to do a chroot()
|
||||
- Rename get_random_byte() to vsf_sysutil_get_random_byte(), and move from
|
||||
utility.c to sysutil.c
|
||||
- Create new file secutil.c, move change_full_credentials() to it and rename
|
||||
- Convert utility.c to vsf_sysutil_*.
|
||||
- handle_local_login(): don't look up username; common_do_login() does it
|
||||
- implement different tunable umask() values for local/anonymous users
|
||||
- implement SITE UMASK
|
||||
- implement SITE CHMOD
|
||||
- whoops! allow non-anonymous users to overwrite files with STOR
|
||||
|
||||
0.0.6 packaged
|
||||
--------------
|
||||
|
||||
- SECURITY: when in anonymous-only mode, reject usernames that aren't the
|
||||
anonymous usernames. This is hoping some FTP clients will be stopped from
|
||||
sending a cleartext password. Idea from Gerald Teschl <gt@esi.ac.at>.
|
||||
- Decided to put "telnet strings" on the back burner :)
|
||||
- Sprinkling of static in main.c
|
||||
- Complete parseconf.c config file parsing and plug it into main.c
|
||||
- Convert main.c to vsf_sysutil_*. This leaves
|
||||
checkauth.c, postprivparent.c and privsock.c
|
||||
- Now we have runtime config, make compiled in defaults extra paranoid
|
||||
- Implement "tunable_anon_world_readable_only" to only serve publicly
|
||||
readable files anonymously
|
||||
- Add sample "vsftpd.conf"
|
||||
- Eww - missing "return" in parseconf.c
|
||||
- Move ASCII mode transfers out of critical section in TODO
|
||||
- parseconf.c: if an integer starts with "0", treat it as octal
|
||||
- Ban "SITE CHMOD" if !tunable_write_enable
|
||||
- Wrote SECURITY/TRUST
|
||||
- Wrote SECURITY/IMPLEMENTATION, probably more to come
|
||||
- Update INSTALL
|
||||
- Add "tunable_nopriv_user"
|
||||
- Update parseconf.c with the two latest new config variables
|
||||
- Add sysdeputil.h, sysdeputil.c for system specific facilities, i.e.
|
||||
capabilites, authentication.
|
||||
- Lose checkauth.c,h - they moved into sysdeputil.c,h
|
||||
- Lose config.h - it moved into sysdeputil.c
|
||||
- Convert postprivparent.c to vsf_sysutil_* (leaves privsock.c)
|
||||
- Convert privsock.c to vsf_sysutil_*. All done, yay!! :)
|
||||
- D'oh! Missing "!" in postlogin.c refused to server publicly readable files:)
|
||||
- Fix chown() of uploaded files (broken initialization order in main())
|
||||
- Add SPEED, and fill it with wild speculation
|
||||
- Rename distribution directory "vsftpd-x.x.x" (note the added "d")
|
||||
|
||||
0.0.7 packaged
|
||||
--------------
|
||||
|
||||
- Build with -O2
|
||||
- Fix "uninitialized" warnings -O2 exposed - the one in capabilities setup
|
||||
could be nasty!
|
||||
- Nail warning in vsf_sysutil_sendfile(). We're now "-Wall warning free"
|
||||
- Build with -Werror to signal intent to _stay_ warning free
|
||||
- A few int -> long in the area of file sizes and offsets
|
||||
- Remove comma's at end of enum lists (-pedantic caught it)
|
||||
- Impact from fixing warnings caused by -pedantic
|
||||
- Date format %e -> %d in date display, %e isn't everywhere
|
||||
- Paranoia in vsf_sysutil_malloc()
|
||||
- Clean up interface to substring searching in str.c
|
||||
- Cleanups in str.c
|
||||
- Squash most "unsigned<->signed" conversions exposed by -Wconversion
|
||||
- Lose "-g" to CFLAGS; after all we're bug-free now ;-)
|
||||
- Add "AUDIT"
|
||||
- Fix up a bunch of potential 64-bit issues (maybe >2Gb files will work on
|
||||
64-bit platforms now, no way to test)
|
||||
- Implement PR_SET_KEEPCAPS support for 2.2.18+ and 2.4.0+ kernels
|
||||
- In sysdeputil.c, change NULL -> 0 to help Solaris build problem
|
||||
- Repair vsf_sysutil_sendfile() and the caller
|
||||
- Logging: log the username
|
||||
- Logging: don't log "//" as start of filenames under certain conditions
|
||||
- Logging: log the date. Logging is almost useful now!
|
||||
- Logging: log MKD commands too; they are used in anon ftp a fair bit
|
||||
- Take the trouble to look into partial reads/writes. Looks like we are safe.
|
||||
- vsf_sysutil_read and vsf_sysutil_write now hide EINTR and retry
|
||||
- Replace some vsf_sysutil_{read,write} usage with
|
||||
vsf_sysutil_{read,write)_loop which handles partial reads and writes
|
||||
- Implement a sendfile() replacement for systems which lack it
|
||||
- Implement runtime checking for system specific Linux stuff, i.e.
|
||||
prctl(PR_SET_KEEPCAPS). This is inspired by RedHat7.0 headers claiming to
|
||||
be a 2.4.0 kernel, but actually you are running on 2.2.x! :-(
|
||||
- Strip the build executable at link time
|
||||
|
||||
0.0.8 packaged
|
||||
--------------
|
||||
|
||||
- A few incorrect sizeof()'s in postlogin.c, thanks to Antonomasia
|
||||
<ant@notatla.demon.co.uk> for noting these.
|
||||
- Decide that ASCII support isn't too important for now (waiting for users to
|
||||
demand it). Also decide that ABOR is a must :( Thanks to Zach Brown
|
||||
<zab@zabbo.net> for the discussion.
|
||||
- More TODO items thanks to Stephen White <swhite@ox.compsoc.net> - 2.0.x
|
||||
issues.
|
||||
- Provide a definition for SHUT_RDWR in sysutil.c, not all systems have that
|
||||
definition yet. Thanks Stephen White <swhite@ox.compsoc.net>.
|
||||
- Tidy privparent.c
|
||||
- Decide ASCII _is_ quite important, thanks Solar ;-)
|
||||
- Bit of extra paranoia in sysutil.c: don't call mem*() if size == 0
|
||||
- Tidy str.c
|
||||
- Command line: if vsftpd has an argument, it is a path to a config file.
|
||||
- Set TCP_NODELAY on command stream
|
||||
- Don't lseek() for RETR in common case with REST set to 0
|
||||
- Correct error code for transfer after succesful connection (425 -> 426)
|
||||
- ABOR support. Bah.
|
||||
- APPE support (why not, it was trivial). Putting off ASCII support ;-)
|
||||
- Add ASCII transfer support. Bah.
|
||||
- Tidy up sysutil.c, fix breakage in read_loop and write_loop.
|
||||
|
||||
0.0.9 packaged
|
||||
--------------
|
||||
|
||||
- Remove ".message" from distribution. Thanks Mitchell Blank Jr
|
||||
<mitch@sfgoth.com>
|
||||
- Note where I can get some load testing software, thanks to Dan Kegel
|
||||
<dank@alumni.caltech.edu>. I'll do that soon because I hope to waste wu-ftpd.
|
||||
- Fix an Alpha build warning and check return value from final pam_end().
|
||||
Reported by Solar Designer <solar@openwall.com>.
|
||||
- Add xinetd.d/vsftpd, from Kurt Seifried <listuser@seifried.org>.
|
||||
- Integrate comments/fixes into SECURITY documentation, thanks to Antonomasia
|
||||
<ant@notatla.demon.co.uk>
|
||||
- SECURITY: default tunable_chroot_local_user to 0, because it is dangerous to
|
||||
give users write access to the filesystem root (think of opening trusted
|
||||
files relative to the root). Thanks again Solar Designer
|
||||
<solar@openwall.com>.
|
||||
- Add "make install" target. Currently it is minimal!
|
||||
- Clearer error message if vsftpd is started manually. Suggestion from
|
||||
Tom <tom@lemuria.org>.
|
||||
- Report futuristic or old (>6 months) dates in a different format, showing
|
||||
the year like /bin/ls does.
|
||||
- Add KERNEL-2.4.0-WARNING. Whoo-hoo. Why do all my non-trivial programs seem
|
||||
to trigger kernel bugs?
|
||||
- SECURITY: refuse to allow anonymous logins if some bonehead has configured
|
||||
the anonymous ftp user with write access to the ftp root.
|
||||
- Fix ASCII downloads so that \n UNCONDITIONALLY maps to \r\n. This behaviour
|
||||
is now consistent with wu-ftpd and results in simpler code.
|
||||
- Fix ASCII uploads to not to fail to strip some \r characters. Noted by
|
||||
Mitchell Blank Jr <mitch@sfgoth.com>.
|
||||
- Add TODO items: log transfer rate and anonymous password. Andrew Anderson
|
||||
<andrew@redhat.com>.
|
||||
|
||||
0.0.10 packaged
|
||||
---------------
|
||||
|
||||
- Remove errant #include <sys/sendfile.h> from sysutil.c. Noted by Jan-Frode
|
||||
Myklebust <janfrode@parallab.uib.no>
|
||||
- Use gettimeofday(2) not time(2), for better resolution.
|
||||
- Add transfer rate to the log
|
||||
- Add <limits.h> to sysutil.c, spotted by Kevin Vajk <kvajk@cup.hp.com>.
|
||||
- Spell "LICENSE" correctly: Kevin Vajk <kvajk@cup.hp.com>.
|
||||
- Use fcntl() for locking instead of flock() because it is much more standard.
|
||||
flock() usage noted by Kevin Vajk <kvajk@cup.hp.com>.
|
||||
- Use more portable IPPROTO_* instead of SOL_* (IPPROTO_IP, IPPROTO_TCP).
|
||||
Thanks to Neil Blakey-Milner <nbm@mithrandr.moria.org> porting to FreeBSD.
|
||||
- Start of Solaris port, thanks to Kurt Seifried <seifried@securityportal.com>
|
||||
for access to a Solaris 8 box.
|
||||
- Portability fix: include <netinet/in_systm.h> before <netinet/ip.h>.
|
||||
- Port to Solaris 8: new directory port. New file porting_junk.h. New file
|
||||
solaris_bogons.h
|
||||
- Add vsf_findlibs.sh to cater for different platform link requirements. Now
|
||||
builds on Solaris and Linux with "make".
|
||||
- struct sockaddr casts to kill Solaris warnings.
|
||||
- sysdeputil.c: remove unused variable warnings.
|
||||
- sysutil.c: use _exit() instead of exit() to avoid libc doing stuff on exit.
|
||||
Fixes segfault reported by Joshua Hill <josh@untruth.org>.
|
||||
- Add BENCHMARKS. Many thanks to Andrew Anderson <andrew@redhat.com>.
|
||||
- Fix disconnect/crash if SIGURG received whilst blocking on command stream.
|
||||
- Update INSTALL with more platforms.
|
||||
|
||||
0.0.11 packaged
|
||||
---------------
|
||||
|
||||
- Brag about performance in README. And why not.
|
||||
- Better bail-out message if the "ftp" anonymous user isn't found
|
||||
- Better bail-out message if the secure chroot directory isn't found
|
||||
- Introduce tunable_one_process_model and start work on it
|
||||
- Fix rare segfault on exit - race leading to infinite stack recursion
|
||||
- Don't bail out if we didn't get an argv[0]. Who cares? Noted by Kurt Seifried
|
||||
<seifried@securityportal.com>.
|
||||
- Change logged date format to include the year.
|
||||
- Add option to log in standard (wu-ftpd like) "xferlog" format.
|
||||
- Cater for sendfile() returning EINTR in sysdeputil.c
|
||||
- Use SO_LINGER on data sockets, to get accurate transfer rates!
|
||||
- Cater for an interrupted blocking close()
|
||||
- Tuning: eliminate 3 mprotect(), 1 munmap() and 1 mmap() system call per
|
||||
command read.
|
||||
- Prevent infinite loops calling sendfile(). Two bugs - we needed to check
|
||||
the sendfile() return for 0 (doh!!) and also, we sometimes did lseek() on
|
||||
a file, to beyond its end. Thanks to Daniel Veillard <Daniel.Veillard@imag.fr>
|
||||
for reporting.
|
||||
- Tuning: cache fd's for /etc/passwd and /etc/group to avoid syscalls.
|
||||
- Tuning: "assist" the get*uid(), get*nam() calls to not make lots of useless
|
||||
syscalls, if /etc/group and /etc/passwd are missing. Thanks to Daniel Veillard
|
||||
<Daniel.Veillard@imag.fr> for reporting.
|
||||
- Use SO_LINGER timeout of 5 mins; INT_MAX seemed to do nothing!
|
||||
- Finally(!) fix transfer rate timing.
|
||||
|
||||
0.0.12 packaged
|
||||
---------------
|
||||
|
||||
- Update INSTALL. Mention the config file can be given on the command line.
|
||||
- Lower VSFTP_MAX_COMMAND_LINE to 4096 (wu-ftpd uses 512 I think).
|
||||
- Add RedHat/vsftpd-rh7.spec, kindly provided by Emmanuel Galanos
|
||||
<egalanos@anchor.net.au>.
|
||||
- Add more RedHat/* spec files etc, kindly provided by Andrew Anderson
|
||||
<andrew@redhat.com>.
|
||||
- Cleanup: move two process model code to "twoprocess.c".
|
||||
- Damn! Make the file lock _block_ if it's busy, in sysutil.c.
|
||||
- Finish implementing one process model - benchmarks to follow
|
||||
- Don't log success if the download is ABOR'ed during the blocking close().
|
||||
- Build on systems without PAM (obviously local logins won't work..)
|
||||
- Beware of FreeBSD accept() bug: ai32@drexel.edu
|
||||
- Implemented a customizable ftp banner with "ftpd_banner" config file setting
|
||||
- Builds on OpenBSD 2.8 - woohoo
|
||||
- FreeBSD: look for libpam.so* in /usr/lib
|
||||
- FreeBSD: add #include <sys/param.h> otherwise CMSG_* break.
|
||||
- Kill privparent.[ch] - merged them into twoprocess.c
|
||||
- Enable SIGCHLD handler _before_ forking - should nail a race which could lead
|
||||
to zombies. Inspired by zombie report from Joe Klemmer <klemmerj@webtrek.com>.
|
||||
- Data connection timeout code.
|
||||
- ftpcmdio.c: Don't cancel the alarm when we get a command. For safety, we
|
||||
insist that that the only way to "cancel" the alarm is to reset it. This
|
||||
prevents hangs blocking on write() to the command stream. Of course, data
|
||||
transfers are long running operations and have their own timeouts.
|
||||
- Data transfer timeout now kills session.
|
||||
- Take care that no writes block once we've decided to abandon ship.
|
||||
- FreeBSD sendfile() support. I wonder if it works!
|
||||
|
||||
0.0.13 packaged
|
||||
---------------
|
||||
|
||||
- Split out directory listing code into ls.c
|
||||
- Change blocking accept() and connect() code to use select() not SIGALRM!
|
||||
- Remove alarm() timeout junk from file locking in logging.c
|
||||
- Cater for signals interrupting the blocking file lock
|
||||
- Whoops: fix data timeout incorrectly going off. Noted and fixed by Joshua
|
||||
Hill <josh@untruth.org>.
|
||||
- Implement tunable_pasv_promiscuous to relax PASV IP checks. Useful if you
|
||||
are playing with secure tunneling of command connection. Idea, patch from
|
||||
Seth Vidal <skvidal@phy.duke.edu>.
|
||||
- Much better line-by-line file reading string buffer functions.
|
||||
- Use the above better functions for directory messages and config file
|
||||
reading. This eliminates a probable quadratic algorithm, i.e. it's a speedup.
|
||||
- Explictly free certain buffers rather than using the static trick. For
|
||||
example, the config file buffer which is only used once.
|
||||
- Massive cleanup and refactoring of login code.
|
||||
- Add ability to specify file containing list of banned e-mail addresses for
|
||||
anonymous users. Apparently a required feature for big sites trying to avoid
|
||||
DDoS attacks.
|
||||
- Add ability to specify file containing list of users to chroot(), request
|
||||
from helo <helo@neounix.com>, who also persuaded me not to use the homedir
|
||||
hack in /etc/passwd.
|
||||
- Add TODO: PASV port range config setting, for firewalled setups. From Rafal
|
||||
Wojtczuk <nergal@idea.avet.com.pl>.
|
||||
- Rudimentary support for non-PAM local user authentication, with
|
||||
encouragement and helpful discussion from D Richard Felker III
|
||||
<dalias@aerifal.cx>.
|
||||
- Use MAP_ANON instead of mmap() /dev/zero for anonymous pages. It saves
|
||||
using a file descriptor. Neither are standard(?) but MAP_ANON seems to work
|
||||
on a superset of systems compared with mmap() /dev/zero.
|
||||
- Ability to specify a PASV local port range with pasv_min_port and
|
||||
pasv_max_port. Request from Rafal Wojtczuk <nergal@idea.avet.com.pl>.
|
||||
- Non-PAM authentication: check /etc/shells, and support shadow password and
|
||||
account expiry.
|
||||
- First cut at a vsftpd.conf man page! (vsftpd.conf.5)
|
||||
|
||||
0.0.14 packaged
|
||||
---------------
|
||||
|
||||
- Default to ASCII mode transfers, as per RFC. Bug noted with Macintosh client
|
||||
by William Day <day@chem.duke.edu>.
|
||||
- Implement "ls -a".
|
||||
- Implement "ls -r".
|
||||
- Implement "ls -l", i.e. "NLST -L" now works
|
||||
- Implement "ls -t". Superb - now the oft-used "ls -ltr" works!
|
||||
- setproctitle() support - FreeBSD only in the first cut.
|
||||
- setproctitle() on Linux support - what a hack! This crap really needs kernel
|
||||
support. I'm ashamed I bothered.
|
||||
- Repair the contributed spec files a bit, based on reports from Oleg Drokin
|
||||
<green@iXcelerator.com> and Jakob Lichtenberg <jl@it-c.dk>.
|
||||
- Show remote IP and local username in setproctitle() support.
|
||||
- Add vsftpd.8 man page, thanks to Daniel Jacobowitz <dan@debian.org>.
|
||||
- In sysdeputil.c, check macros LINUX_VERSION_CODE and KERNEL_VERSION are
|
||||
defined. From James Antill <james@and.org>.
|
||||
- Workaround a broken firewall that expects a very precise PASV response. We
|
||||
now match wu-ftpd. Many many thanks to Jakob Lichtenberg <jl@it-c.dk> for
|
||||
his help.
|
||||
- If tunable_anon_world_readable_only (default), don't list directories unless
|
||||
they are world readable.
|
||||
- Use qsort() for directory sorting - eliminates gross quadratic sorting.
|
||||
Turbo charges directory listings with 1000's of entries.
|
||||
- Fix big memory leak in str_list_free().
|
||||
- Simplify + reduce heap usage in strlist.c
|
||||
- Optimize away lots of excessive heap usage and redundant copying in str.c
|
||||
- By default, show numeric user/group id's in directory listings. Makes
|
||||
generating directory listings perhaps 4 times(!) faster, and is noticeable
|
||||
with e.g. 5000 entries in a directory. n.b. this performance figure is as
|
||||
measured on a glibc-2.2 system, so glibc would seem to be inefficient.
|
||||
- Don't use MSG_DONTWAIT - prefer the more portable fcntl()/O_NONBLOCK. Fixes
|
||||
glibc-2.0 build issues.
|
||||
- Work around broken Linux-2.0 unix fd passing. Now builds/runs on RH5.2.
|
||||
- Build fixes for FreeBSD 3.5, with help from Jerry Walsh <jerry@aardvark.ie>.
|
||||
- Only restrict directory listings to world-readable for _anonymous_ users!
|
||||
Thanks again Jerry Walsh <jerry@aardvark.ie> for the report.
|
||||
- Add TUNING
|
||||
- Special case for security/performance: if we need _no_ privilege, then
|
||||
force one process model. Security: root dropped totally straight away.
|
||||
Performance: no messing around forking etc.
|
||||
- Minor performance tweaks, don't leave big mappings lying around from
|
||||
config file parsing.
|
||||
|
||||
0.0.15 packaged
|
||||
---------------
|
||||
|
||||
- Argh. Fix SuSE 6.0 build issue (time_t used but not defined). Reported by
|
||||
Peter Stern <peter@frontierflying.com>.
|
||||
- Another SuSE 6.0 issue - another damn system lacking CMSG_SPACE etc.
|
||||
- Cope with any return value from blocking close(2). Previously, we missed
|
||||
EAGAIN, which some systems might return (not Linux).
|
||||
- New wizzy synchronous signal framework, to prevent re-entrancy issues. It
|
||||
presents an interface very similar to the traditional UNIX async interface.
|
||||
Technically this is a security fix; imagine a SIGURG (user controllable!)
|
||||
coming in whilst we are deep inside glibc. The SIGURG handler is non-trivial
|
||||
and may well re-enter and upset glibc. Specific example: the malloc subsystem.
|
||||
- When handing SIGURG, account the time taken under the data tranfer timeout.
|
||||
- Install the command timeout handler before we write anything to the remote.
|
||||
- Cleanup capabilities handling to be taken care of in secutil.c.
|
||||
- Fix bug: one_process_model mode could lose supplementary groups.
|
||||
- Add "SIZE" file.
|
||||
- Make one_process_model work with the anon deny e-mail list.
|
||||
- Massive cleanups. Start moving static state into a session structure.
|
||||
- Oops - fix Solaris 8 build by fixing include order in porting_junk.h, and
|
||||
include a dirfd() replacement. Noted by William Yodlowsky
|
||||
<wyodlows@andromeda.rutgers.edu> and Mike Batchelor <mikebat@tmcs.net>.
|
||||
- Fix return of a void function call in a void function. It upsets Sun's
|
||||
compiler. (gcc is fine with it, I'm not sure if it's against the rules).
|
||||
Noted by Mike Batchelor <mikebat@tmcs.net>.
|
||||
- Make it possible to use port ranges starting lower than 5001, from
|
||||
Matthew Kirkwood <weejock@ferret.lmh.ox.ac.uk>.
|
||||
- Use a /dev/zero mmap() fallback if we do not find MAP_ANON. This should
|
||||
fix the build on Solaris 2.6, 2.7 machines. Reported by Mike Batchelor
|
||||
<mikebat@tmcs.net>. Also noted as one of the problems facing an IRIX build.
|
||||
- Add MDTM support, so clients like ncftp can set the date on downloaded files.
|
||||
- Add irix_bogons.h, trying to port to IRIX 6.5, with help from Jan-Frode
|
||||
Myklebust <janfrode@parallab.uib.no>.
|
||||
- Don't reference "struct msghdr.msg_flags", not all systems have it. Clear it
|
||||
with vsf_sysutil_memclr() instead. Found on IRIX 6.5.11
|
||||
- Cater for systems lacking getusershell(), e.g. IRIX 6.5.11, by not using it.
|
||||
- Fix compiler error with header files claiming 2.4 headers but only having
|
||||
2.2 headers. Reported by Ben Ricker <bricker@wellinx.com>.
|
||||
- Kill warning on system without capabilities.
|
||||
- Add -R option to ls (disabled by default), to cater for broken clients which
|
||||
assume it is present (e.g. mirror).
|
||||
- Add "Makefile.sun", from Mike Batchelor <mikebat@tmcs.net>.
|
||||
- Fix PORT transfer crashes with "one_process_model". Reported by
|
||||
Andrew Anderson <andrew@redhat.com>.
|
||||
- Cater for HP-UX shared libraries which end in ".sl", from Kevin Vajk
|
||||
<kvajk@cup.hp.com>.
|
||||
- Add hpux_bogons.h, and make MAP_ANON a synonym for MAP_ANONYMOUS.
|
||||
- Move send_fd and recv_fd to sysdeputil.c and provide old-style fd passing
|
||||
code for IRIX and HP-UX.
|
||||
- Get it going on HP-UX 11.11 and HP-UX 10.20, thanks to Kevin Vajk
|
||||
<kvajk@cup.hp.com>. Minor changes to hpux_bogons.h
|
||||
- Update vsftpd.conf with "ls_recurse_enable".
|
||||
- Get it going on IRIX 6.5.11, thanks to Jan-Frode Myklebust
|
||||
<janfrode@parallab.uib.no>.
|
||||
- Fix reporting of filenames in MKD operations (regression since 0.0.15).
|
||||
- Wow - lots of contributed .spec files. Adopt those from Seth Vidal
|
||||
<skvidal@phy.duke.edu>.
|
||||
- Fix FreeBSD build.
|
||||
|
||||
0.9.0 packaged
|
||||
--------------
|
||||
|
||||
- Fix .spec files to include URL, from Seth Vidal <skvidal@phy.duke.edu>.
|
||||
- Don't let unprintable characters escape into setproctitle(). Thanks to
|
||||
Solar Designer for the suggestion.
|
||||
- Make the PAM service name a tunable, suggestion from Solar Designer.
|
||||
- Add option to log all FTP protocol (log_ftp_protocol).
|
||||
- Log logins, successful or failed.
|
||||
- Refuse to download a file in ASCII mode if REST position != 0. Solar
|
||||
reminded me by looking in the BUGS file.
|
||||
- Clearly mark an ASCII download in the FTP response string.
|
||||
- Argh. Fix broken upload timeout again (goes off erroneously).
|
||||
- Fix logging of FTP protocol, add logging of pid. Reported by Frank Fiamingo
|
||||
<FiamingF@strsoh.org>.
|
||||
- Fix bug where logging code bug()'s on the second logged operation, iff
|
||||
logging is in fact disabled! Reported by Alexander Schreiber
|
||||
<alexander.schreiber@informatik.tu-chemnitz.de>.
|
||||
- From Solar: be paranoid about libc implementations of isprint() in sysutil.c
|
||||
- Careful not to write any unprintable characters into the log.
|
||||
- fchmod() files that we fchown(), to prevent suid games, etc.
|
||||
- Cleanups, added comments to some headers.
|
||||
- Minor speedups to some str.c string handling functions.
|
||||
- Joe Klemmer <klemmerj@webtrek.com> reports zombies again! Nail a couple of
|
||||
races: make the SIGCHLD handler async, and cater for an interrupted wait(2)
|
||||
syscall.
|
||||
- If chroot_local_user=YES then chroot_list_enable becomes a list of users to
|
||||
NOT chroot(). With input from Lars Hecking <lhecking@nmrc.ie>.
|
||||
|
||||
0.9.1 packaged
|
||||
--------------
|
||||
|
||||
- DAMN! Fix silly "missing newline" logging bug.
|
||||
|
||||
0.9.1 repackaged
|
||||
----------------
|
||||
|
||||
- Refuse to start if local_enable and anonymous_enable are NO, hit by
|
||||
Lars Hecking <lhecking@nmrc.ie>.
|
||||
- Report anonymous e-mail in the LOGIN log event, idea from Joachim Blaabjerg
|
||||
<styx@mailbox.as>.
|
||||
- Fix man page install in vsftpd-rh7.spec, from Matthew Galgoci
|
||||
<mgalgoci@redhat.com>.
|
||||
- Fix chown_upload bug noted by brett <beldridg@best.com>.
|
||||
- Add concept of guest user, idea from Andrew Anderson <andrew@redhat.com>.
|
||||
- Simple bandwidth limitation, inspired by Mads Martin Jørgensen
|
||||
<mmj@suse.com>.
|
||||
- Fix chown_upload bug in a different way.
|
||||
- Correct *_umask details in vsftpd.conf.5, from brett <beldridg@best.com>.
|
||||
- Don't show .files unless "ls -a" was specified, n.b. this differs in
|
||||
behaviour from wu-ftpd, but not proftpd.
|
||||
- Implement directory write(2) buffering, for a 33% reduction in CPU used to
|
||||
send big dirs. Activate the bandwidth limit on directory listings.
|
||||
- HPUX enhancements: setproctitle and sendfile. Thanks to Kevin Vajk
|
||||
<kvajk@cup.hp.com>.
|
||||
- We DON'T need to follow symlinks on "ls -R" - phew.
|
||||
- Add README.solaris. Thanks to Mike Batchelor <mikebat@tmcs.net>.
|
||||
- Implement passing remote host to PAM (for pam_access etc.), thanks to
|
||||
Emmanuel Galanos <egalanos@cerberus.anchor.net.au>.
|
||||
- Fix guest_enable so that this means all non-anonymous users are guest users.
|
||||
- Add ability to deny selected users before they get the chance to send their
|
||||
cleartext password!!
|
||||
- Fix FreeBSD build - use a cast instead of floor() which needs libm.
|
||||
|
||||
0.9.2 packaged
|
||||
--------------
|
||||
|
46
INSTALL
Normal file
46
INSTALL
Normal file
@ -0,0 +1,46 @@
|
||||
For now, just type "make" and mail me if it doesn't build.
|
||||
|
||||
Once built, you will need to run the binary from an inetd of some kind.
|
||||
|
||||
The FTP server will refuse to start up unless you satisfy a few prerequisites:
|
||||
1) You will need the user "ftp" to exist and have a valid home directory.
|
||||
2) You will need the user "nobody" to exist.
|
||||
3) You will need an empty directory /usr/share/empty to exist.
|
||||
|
||||
Note that "ftp" "nobody" and "/usr/share/empty" are not hard-coded; you may
|
||||
specify values for these in the config file.
|
||||
|
||||
If you are running vsftpd on a PAM enabled machine, you will need to have a
|
||||
/etc/pam.d/ftp file present, otherwise non-anonymous logins will fail. [NOTE -
|
||||
if you have an older version of PAM, that file might be /etc/pam.conf]
|
||||
|
||||
As well as the above three pre-requisites, you are recommended to install a
|
||||
config file. The default location for the config file is /etc/vsftpd.conf.
|
||||
There is a sample vsftpd.conf in the distribution tarball. You probably want
|
||||
to copy that to /etc/vsftpd.conf as a basis for modification. For example,
|
||||
the default configuration allows neither local user logins nor anonymous
|
||||
uploads. You may wish to change these defaults.
|
||||
|
||||
If you have virtual hosting running, you may find it useful to specify the
|
||||
config file on the command line. This is accomplished by specifying a single
|
||||
command line argument which is a pathname to the config file. If using inetd
|
||||
as opposed to xinetd, be careful to specify the program name argv[0] before
|
||||
the config file location argv[1]!
|
||||
|
||||
Tested platforms (well, it builds)
|
||||
- RedHat Linux 7.0
|
||||
- RedHat Linux 6.1
|
||||
- RedHat Linux 6.2
|
||||
- RedHat Linux 5.2
|
||||
- Solaris 8 / GNU tools (light testing)
|
||||
- SuSE 6.4
|
||||
- SuSE 6.0
|
||||
- Debian 2.2
|
||||
- OpenBSD 2.8
|
||||
- FreeBSD 4.2
|
||||
- FreeBSD 3.5
|
||||
- HP-UX 11.11 / GNU tools
|
||||
- HP-UX 10.20 / GNU tools
|
||||
- Solaris 2.6
|
||||
- IRIX 6.5.11 / GNU tools
|
||||
|
9
KERNEL-2.4.0-WARNING
Normal file
9
KERNEL-2.4.0-WARNING
Normal file
@ -0,0 +1,9 @@
|
||||
WARNING
|
||||
-------
|
||||
|
||||
vsftpd seems to under some conditions kill kernels 2.4.0 and 2.4.1 dead. The
|
||||
problem has been observed by several people. The problem has been reported,
|
||||
and was fixed in kernel version 2.4.2-pre1.
|
||||
|
||||
The problem seems to trigger when vsftpd exits.
|
||||
|
30
Makefile
Normal file
30
Makefile
Normal file
@ -0,0 +1,30 @@
|
||||
# Makefile for systems with GNU tools
|
||||
CC = gcc
|
||||
INSTALL = install
|
||||
IFLAGS = -idirafter dummyinc
|
||||
CFLAGS = -O2 -Wall -W -Wshadow #-pedantic -Werror -Wconversion
|
||||
|
||||
LIBS = `./vsf_findlibs.sh`
|
||||
LINK = -s
|
||||
|
||||
OBJS = main.o utility.o prelogin.o ftpcmdio.o postlogin.o privsock.o \
|
||||
tunables.o ftpdataio.o secbuf.o ls.o \
|
||||
postprivparent.o logging.o str.o netstr.o sysstr.o strlist.o \
|
||||
dirchange.o filestr.o parseconf.o secutil.o \
|
||||
ascii.o oneprocess.o twoprocess.o privops.o \
|
||||
sysutil.o sysdeputil.o
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $*.c $(CFLAGS) $(IFLAGS)
|
||||
|
||||
vsftpd: $(OBJS)
|
||||
$(CC) -o vsftpd $(OBJS) $(LINK) $(LIBS)
|
||||
|
||||
install:
|
||||
$(INSTALL) -m 755 vsftpd /usr/sbin/vsftpd
|
||||
if [ -x /etc/xinetd.d ]; then \
|
||||
$(INSTALL) -m 644 xinetd.d/vsftpd /etc/xinetd.d/vsftpd; fi
|
||||
|
||||
clean:
|
||||
rm -f *.o *.swp vsftpd
|
||||
|
30
Makefile.sun
Normal file
30
Makefile.sun
Normal file
@ -0,0 +1,30 @@
|
||||
# Makefile for SUNWspro compiler and tools
|
||||
# Contributed by Mike Batchelor <mikebat@electabuzz.tech.tmcs>
|
||||
CC = cc
|
||||
INSTALL = /usr/ucb/install
|
||||
CFLAGS = -xO5
|
||||
|
||||
LIBS = -lsocket -lnsl -lpam
|
||||
LINK = -s
|
||||
|
||||
OBJS = main.o utility.o prelogin.o ftpcmdio.o postlogin.o privsock.o \
|
||||
tunables.o ftpdataio.o secbuf.o ls.o \
|
||||
postprivparent.o logging.o str.o netstr.o sysstr.o strlist.o \
|
||||
dirchange.o filestr.o parseconf.o secutil.o \
|
||||
ascii.o oneprocess.o twoprocess.o privops.o \
|
||||
sysutil.o sysdeputil.o
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $*.c $(CFLAGS)
|
||||
|
||||
vsftpd: $(OBJS)
|
||||
$(CC) -o vsftpd $(OBJS) $(LINK) $(LIBS)
|
||||
|
||||
install:
|
||||
$(INSTALL) -m 755 vsftpd /usr/sbin/vsftpd
|
||||
if [ -x /etc/xinetd.d ]; then \
|
||||
$(INSTALL) -m 644 xinetd.d/vsftpd /etc/xinetd.d/vsftpd; fi
|
||||
|
||||
clean:
|
||||
rm -f *.o vsftpd
|
||||
|
21
README
Normal file
21
README
Normal file
@ -0,0 +1,21 @@
|
||||
This is vsftpd, version 0.9.2
|
||||
Author: Chris Evans
|
||||
Contact: chris@scary.beasts.org
|
||||
|
||||
What is this?
|
||||
=============
|
||||
|
||||
vsftpd is an FTP server, or daemon. The "vs" stands for Very Secure. Obviously
|
||||
this is not a guarantee, but a reflection that I have written the entire
|
||||
codebase with security in mind, and carefully designed the program to be
|
||||
resilient to attack.
|
||||
|
||||
Recent evidence suggests that vsftpd is also extremely fast (and this is
|
||||
before any explicit performance tuning!) In tests against wu-ftpd, vsftpd
|
||||
was always faster, supporting over twice as many users in some tests.
|
||||
|
||||
Note - this is currently very much a work in progress, so some areas of the
|
||||
source are a bit of a mess. Please don't take this as indicative of the
|
||||
quality of my completed work ;-)
|
||||
|
||||
This document will be expanded in a future release!
|
2
README.security
Normal file
2
README.security
Normal file
@ -0,0 +1,2 @@
|
||||
For documentation about the security of vsftpd, please consult the files
|
||||
located within the SECURITY directory.
|
20
README.solaris
Normal file
20
README.solaris
Normal file
@ -0,0 +1,20 @@
|
||||
Solaris specific notes
|
||||
======================
|
||||
|
||||
Modern releases of Solaris (2.6+ ?) ship with PAM. PAM is an excellent
|
||||
generic authentication framework. Unfortunately, Solaris seems to be a
|
||||
little sparse in the number of PAM modules supported.
|
||||
|
||||
Specifically, many ftp daemon users will want to enable /etc/ftpusers control,
|
||||
as well login control based on the validity of a user's shell (/etc/shells).
|
||||
To perform these two tasks, pam_listfile and pam_shells are required.
|
||||
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
|
||||
|
||||
From Mike:
|
||||
"To install, just unpack it in /usr/lib/security, and edit /etc/pam.conf,
|
||||
using "ftp" as the service name."
|
||||
|
2
REWARD
Normal file
2
REWARD
Normal file
@ -0,0 +1,2 @@
|
||||
At some stage, a reward may be offered to anyone finding a serious security
|
||||
hole in vsftpd. Mail me if you have any ideas :)
|
76
RedHat/vsftpd-rh6.spec
Normal file
76
RedHat/vsftpd-rh6.spec
Normal file
@ -0,0 +1,76 @@
|
||||
Summary: vsftpd - Very Secure Ftp Daemon
|
||||
Name: vsftpd
|
||||
Version: 0.9.2
|
||||
Release: rh6_1
|
||||
Copyright: GPL
|
||||
Group: System Environment/Daemons
|
||||
URL: ftp://ferret.lmh.ox.ac.uk/pub/linux/
|
||||
Source: %{name}-%{version}.tar.gz
|
||||
Packager: Seth Vidal <skvidal@phy.duke.edu>
|
||||
BuildRoot: /var/tmp/%{name}-%{version}-root
|
||||
Requires: inetd, logrotate
|
||||
Provides: ftpserver
|
||||
|
||||
%description
|
||||
A Very Secure FTP Daemon - written from scratch - by Chris "One Man Security
|
||||
Audit Team" Evans
|
||||
|
||||
|
||||
%prep
|
||||
%setup -q -n %{name}-%{version}
|
||||
|
||||
%build
|
||||
make
|
||||
|
||||
%install
|
||||
[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/sbin
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/share/empty
|
||||
mkdir -p $RPM_BUILD_ROOT/etc
|
||||
mkdir -p $RPM_BUILD_ROOT/etc/pam.d
|
||||
mkdir -p $RPM_BUILD_ROOT/etc/logrotate.d
|
||||
mkdir -p $RPM_BUILD_ROOT/%{_mandir}/man5
|
||||
mkdir -p $RPM_BUILD_ROOT/%{_mandir}/man8
|
||||
install -m 755 vsftpd $RPM_BUILD_ROOT/usr/sbin/vsftpd
|
||||
install -m 600 vsftpd.conf $RPM_BUILD_ROOT/etc/vsftpd.conf
|
||||
install -m 644 RedHat/vsftpd.pam $RPM_BUILD_ROOT/etc/pam.d/ftp
|
||||
install -m 644 vsftpd.conf.5 $RPM_BUILD_ROOT/%{_mandir}/man5/
|
||||
install -m 644 vsftpd.8 $RPM_BUILD_ROOT/%{_mandir}/man8/
|
||||
install -m 644 RedHat/vsftpd.log $RPM_BUILD_ROOT/etc/logrotate.d/vsftpd.log
|
||||
|
||||
%clean
|
||||
[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
/usr/sbin/vsftpd
|
||||
%dir /usr/share/empty
|
||||
%config /etc/vsftpd.conf
|
||||
%config /etc/pam.d/ftp
|
||||
%config /etc/logrotate.d/vsftpd.log
|
||||
%doc INSTALL BUGS AUDIT Changelog LICENSE README README.security REWARD SPEED TODO SECURITY/ TUNING SIZE
|
||||
%{_mandir}/man5/vsftpd.conf.*
|
||||
%{_mandir}/man8/vsftpd.*
|
||||
|
||||
%changelog
|
||||
* Thu Mar 22 2001 Seth Vidal <skvidal@phy.duke.edu>
|
||||
- updated to 0.0.15
|
||||
- added entry for vsftpd.8 man page
|
||||
- added entry for vsftpd.log logrotate file
|
||||
- added TUNING file to docs list
|
||||
* Wed Mar 7 2001 Seth Vidal <skvidal@phy.duke.edu>
|
||||
- Updated to 0.0.14
|
||||
- made %files entry for man page
|
||||
* Wed Feb 21 2001 Seth Vidal <skvidal@phy.duke.edu>
|
||||
- Updated to 0.0.13
|
||||
* Mon Feb 12 2001 Seth Vidal <skvidal@phy.duke.edu>
|
||||
- Updated to 0.0.12
|
||||
* Wed Feb 7 2001 Seth Vidal <skvidal@phy.duke.edu>
|
||||
- updated to 0.0.11
|
||||
* Fri Feb 1 2001 Seth Vidal <skvidal@phy.duke.edu>
|
||||
- Update to 0.0.10
|
||||
* Fri Feb 1 2001 Seth Vidal <skvidal@phy.duke.edu>
|
||||
- First RPM packaging
|
||||
- Stolen items from wu-ftpd's pam setup
|
||||
- Separated rh 7 and rh 6.X's packages
|
||||
- Built for Rh6
|
80
RedHat/vsftpd-rh7.spec
Normal file
80
RedHat/vsftpd-rh7.spec
Normal file
@ -0,0 +1,80 @@
|
||||
Summary: vsftpd - Very Secure Ftp Daemon
|
||||
Name: vsftpd
|
||||
Version: 0.9.2
|
||||
Release: rh7_2
|
||||
Copyright: GPL
|
||||
Group: System Environment/Daemons
|
||||
URL: ftp://ferret.lmh.ox.ac.uk/pub/linux/
|
||||
Source: %{name}-%{version}.tar.gz
|
||||
Packager: Seth Vidal <skvidal@phy.duke.edu>
|
||||
BuildRoot: /var/tmp/%{name}-%{version}-root
|
||||
Requires: xinetd, /etc/pam.d/system-auth, logrotate
|
||||
Provides: ftpserver
|
||||
|
||||
%description
|
||||
A Very Secure FTP Daemon - written from scratch - by Chris "One Man Security
|
||||
Audit Team" Evans
|
||||
|
||||
|
||||
%prep
|
||||
%setup -q -n %{name}-%{version}
|
||||
|
||||
%build
|
||||
make
|
||||
|
||||
%install
|
||||
[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/sbin
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/share/empty
|
||||
mkdir -p $RPM_BUILD_ROOT/etc
|
||||
mkdir -p $RPM_BUILD_ROOT/etc/xinetd.d
|
||||
mkdir -p $RPM_BUILD_ROOT/etc/pam.d
|
||||
mkdir -p $RPM_BUILD_ROOT%{_mandir}/man5
|
||||
mkdir -p $RPM_BUILD_ROOT%{_mandir}/man8
|
||||
mkdir -p $RPM_BUILD_ROOT/etc/logrotate.d
|
||||
install -m 755 vsftpd $RPM_BUILD_ROOT/usr/sbin/vsftpd
|
||||
install -m 600 vsftpd.conf $RPM_BUILD_ROOT/etc/vsftpd.conf
|
||||
install -m 644 RedHat/vsftpd.pam $RPM_BUILD_ROOT/etc/pam.d/ftp
|
||||
install -m 644 xinetd.d/vsftpd $RPM_BUILD_ROOT/etc/xinetd.d/vsftpd
|
||||
install -m 644 vsftpd.conf.5 $RPM_BUILD_ROOT/%{_mandir}/man5/vsftpd.conf.5
|
||||
install -m 644 vsftpd.8 $RPM_BUILD_ROOT/%{_mandir}/man8/vsftpd.8
|
||||
install -m 644 RedHat/vsftpd.log $RPM_BUILD_ROOT/etc/logrotate.d/vsftpd.log
|
||||
|
||||
%clean
|
||||
[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
/usr/sbin/vsftpd
|
||||
%dir /usr/share/empty
|
||||
%config /etc/vsftpd.conf
|
||||
%config /etc/xinetd.d/vsftpd
|
||||
%config /etc/pam.d/ftp
|
||||
%config /etc/logrotate.d/vsftpd.log
|
||||
%{_mandir}/man5/vsftpd.conf.*
|
||||
%{_mandir}/man8/vsftpd.*
|
||||
%doc %attr(755,root,root)INSTALL BUGS AUDIT Changelog LICENSE README README.security REWARD SPEED TODO SECURITY/ TUNING SIZE
|
||||
|
||||
%changelog
|
||||
* Thu Mar 22 2001 Seth Vidal <skvidal@phy.duke.edu>
|
||||
- updated to 0.0.15
|
||||
- added entry for vsftpd.8 man page
|
||||
- added entry for vsftpd.log logrotate file
|
||||
- added TUNING file to docs list
|
||||
* Wed Mar 7 2001 Seth Vidal <skvidal@phy.duke.edu>
|
||||
- updated to 0.0.14
|
||||
- added entry for man page
|
||||
* Wed Feb 21 2001 Seth Vidal <skvidal@phy.duke.edu>
|
||||
- Update to 0.0.13
|
||||
* Mon Feb 12 2001 Seth Vidal <skvidal@phy.duke.edu>
|
||||
- Update to 0.0.12
|
||||
* Wed Feb 7 2001 Seth Vidal <skvidal@phy.duke.edu>
|
||||
- Update to 0.0.11
|
||||
- Use vsftpd provided xinetd.d file
|
||||
* Fri Feb 2 2001 Seth Vidal <skvidal@phy.duke.edu>
|
||||
- Update to 0.0.10
|
||||
* Thu Feb 1 2001 Seth Vidal <skvidal@phy.duke.edu>
|
||||
- First RPM packaging
|
||||
- Stolen items from wu-ftpd's pam setup
|
||||
- Separated rh 7 and rh 6.X's packages
|
||||
- fixed xinetd startup - duh!
|
4
RedHat/vsftpd.log
Normal file
4
RedHat/vsftpd.log
Normal file
@ -0,0 +1,4 @@
|
||||
/var/log/vsftpd.log {
|
||||
# ftpd doesn't handle SIGHUP properly
|
||||
nocompress
|
||||
}
|
6
RedHat/vsftpd.pam
Normal file
6
RedHat/vsftpd.pam
Normal file
@ -0,0 +1,6 @@
|
||||
#%PAM-1.0
|
||||
auth required /lib/security/pam_listfile.so item=user sense=deny file=/etc/ftpusers onerr=succeed
|
||||
auth required /lib/security/pam_pwdb.so shadow nullok
|
||||
auth required /lib/security/pam_shells.so
|
||||
account required /lib/security/pam_pwdb.so
|
||||
session required /lib/security/pam_pwdb.so
|
140
SECURITY/DESIGN
Normal file
140
SECURITY/DESIGN
Normal file
@ -0,0 +1,140 @@
|
||||
This document explains the design goals and decisions behind vsftpd.
|
||||
|
||||
The importance of a secure design
|
||||
=================================
|
||||
|
||||
In a world full of good, careful coders who do not make mistakes, a secure
|
||||
design would not be necessary. After all, in the absence of any programming
|
||||
errors, security would not differ no matter how the program is arranged.
|
||||
|
||||
Unfortunately, this is not an ideal world, and coders make plenty of mistakes.
|
||||
Even the careful coders make mistakes. Code auditing is important, and goes
|
||||
some way towards eliminating coding mistakes after the fact. However, we
|
||||
have no guarantee that an audit will catch all the flaws.
|
||||
|
||||
So, a secure design acknowledges the possibility of undiscovered flaws, and
|
||||
takes steps to minimise the security impact these flaws can have. An obvious
|
||||
example of something we want to do is to apply the principle of least
|
||||
privilege, which ensure that every part of the program runs with the privilege
|
||||
it needs and no more.
|
||||
|
||||
An example of insecure design
|
||||
=============================
|
||||
|
||||
Examples of insecure design may be found in most other ftpd's. That's one of
|
||||
the reasons vsftpd has been written. We'll pick on wu-ftpd as a specific
|
||||
example, since it is rumoured to run about half of all ftp services.
|
||||
|
||||
If I log on to wu-ftpd as an anonymous user, a process is run on my behalf to
|
||||
serve my ftp session. Unfortunately, this process typically runs with full
|
||||
root privileges on the remote machine. This means that any security flaw
|
||||
present in parsing the copious ftp protocol will lead to full compromise of
|
||||
that machine. Two concrete examples are the recent wu-ftpd format string bug
|
||||
(June 1999), and a buffer overflow dealing with large paths a few months
|
||||
beforehand.
|
||||
|
||||
Even OpenBSD's ftpd-BSD had a format string bug leading to remote root
|
||||
compromise of the affected machine, illustrating an earlier point about the
|
||||
requirement for secure design even in the presence of heavy auditing.
|
||||
|
||||
Secure design under UNIX
|
||||
========================
|
||||
|
||||
vsftpd is written to run under UNIX-like operating systems, and so its secure
|
||||
design is constrained by the facilities offered by UNIX. Ideally, UNIX would
|
||||
have a proper security model which would offer fine grained access control
|
||||
to all system interactions (files, network, etc). It doesn't, but it does
|
||||
offer some useful and often overlooked facilities which help us to implement
|
||||
the principle of least privilege:
|
||||
|
||||
- Strong inter-process communication facilities
|
||||
|
||||
In UNIX, the process is a strongly defined boundary. Different privilege
|
||||
credentials may be assigned to different processes, which are not able to
|
||||
interfere with each other. This is a very basic facility of UNIX.
|
||||
|
||||
It makes sense to use this facility to totally separate parts of a program
|
||||
which do not need to be privileged (most) from those parts that do (typically
|
||||
minimal).
|
||||
|
||||
The privileged and unprivileged parts of the program then communicate via
|
||||
one of many UNIX IPC mechanisms - perhaps a socketpair or IPC (the former
|
||||
is attractive because UNIX lets you pass file handles over a socket).
|
||||
|
||||
The minimal privileged process exercises the "principle of distrust" - it
|
||||
carefully filters what the unprivileged process asks it to do, so that even
|
||||
if the unprivileged process is compromised, it cannot ask the privileged
|
||||
process to do anything we don't want to allow.
|
||||
|
||||
- chroot()
|
||||
|
||||
chroot() is an often overlooked but useful tool. It can be used very
|
||||
effectively as a damage limitation tool.
|
||||
|
||||
Imagine a remotely compromised process which does not run as root, but also
|
||||
does not use chroot(). Now look at what the attacker can do. Amongst the worst
|
||||
items are pilfering of all publicly readable files, and also attempting to
|
||||
execute any publicly executable suid-root programs to try and elevate
|
||||
privilege.
|
||||
|
||||
Now imaging the same compromised process with a chroot() to an empty directory.
|
||||
The attackers options to do unpleasant things are substantially diminished.
|
||||
|
||||
No, chroot() is not the ideal way to do what we have just accomplished, but
|
||||
it is what we have got to work with. In an ideal environment with fine
|
||||
grained security, we would default to having access to _no_ files at all, and
|
||||
deliberately not ask for access to any.
|
||||
|
||||
- Capabilities (Linux 2.2+)
|
||||
|
||||
Like chroot(), capabilities are essentially a damage limitation excercise.
|
||||
They are also much less widespread than the other UNIX facilities detailled
|
||||
above. Nonetheless, they warrant mentioning because Linux has them, and they
|
||||
are used in vsftpd because that is the primary devlopment platform.
|
||||
|
||||
Capabilities split up the all powerful root privilege into lots of sometimes
|
||||
orthogonal privileges. Some of the capabilities represent privileges which
|
||||
are often the basis for requiring a program to run with full root privileges.
|
||||
Examples include CAP_NET_RAW (ping, traceroute) and CAP_NET_BIND_SERVICE
|
||||
(rlogin).
|
||||
|
||||
By using capabilities to ensure we only have the privilege we need (within
|
||||
the somewhat disappointing granularity they offer), we again limit the
|
||||
potential damage of security holes.
|
||||
|
||||
Presenting vsftpd's secure design
|
||||
=================================
|
||||
|
||||
vsftpd employs a secure design. The UNIX facilities outlined above are used
|
||||
to good effect. The design decisions taken are as follows:
|
||||
|
||||
1) All parsing and acting on potentially malicious remote network data is
|
||||
done in a process running as an unprivileged user. Furthermore, this process
|
||||
runs in a chroot() jail, ensuring only the ftp files area is accessible.
|
||||
|
||||
2) Any privileged operations are handled in a privileged parent process. The
|
||||
code for this privileged parent process is as small as possible for safety.
|
||||
|
||||
3) This same privileged parent process receives requests from the unprivileged
|
||||
child over a socket. All requests are distrusted. Here are example requests:
|
||||
- Login request. The child sends username and password. Only if the details
|
||||
are correct does the privileged parent launch a new child with the appropriate
|
||||
user credentials.
|
||||
- chown() request. The child may request a recently uploaded file gets
|
||||
chown'ed() to root for security purposes. The parent is careful to only allow
|
||||
chown() to root, and only from files owned by the ftp user.
|
||||
- Get privileged socket request. The ftp protocol says we are supposed to
|
||||
emit data connections from port 20. This requires privilege. The privileged
|
||||
parent process creates the privileged socket and passes it to child over
|
||||
the socket.
|
||||
|
||||
4) This same privileged parent process makes use of capabilities and chroot(),
|
||||
to run with the least privilege required. After login, depending on what
|
||||
options have been selected, the privileged parent dynamically calculates what
|
||||
privileges it requires. In some cases, this amounts to no privilege, and the
|
||||
privileged parent just exits, leaving no part of vsftpd running with
|
||||
privilege.
|
||||
|
||||
|
||||
Comments on this document are welcomed.
|
||||
|
44
SECURITY/IMPLEMENTATION
Normal file
44
SECURITY/IMPLEMENTATION
Normal file
@ -0,0 +1,44 @@
|
||||
This document details a few steps and decisions taken to ensure vsftpd is free
|
||||
of common implementation flaws.
|
||||
|
||||
Tackling the buffer overflow
|
||||
============================
|
||||
|
||||
Probably the most common implementation flaw causing security problems is the
|
||||
buffer overflow. Buffer overflows come in many shapes and sizes - overflows
|
||||
onto the stack, overflows off the end of dynamically malloc()'ed areas,
|
||||
overflows into static data areas. They range from easy to spot (where a user
|
||||
can put an arbitrary length string into a fixed size buffer), to very
|
||||
difficult to spot - buffer size miscalculations or single byte overflows. Or
|
||||
convoluted code where the buffer's definition and various usages are far
|
||||
apart.
|
||||
|
||||
The problem is that people insist on replicating buffer size handling code
|
||||
and buffer size security checks many times (or, of course, they omit size
|
||||
checks altogther). It is little surprise, then, that sometimes errors creep
|
||||
in to the checks.
|
||||
|
||||
The correct solution is to hide the buffer handling code behind an API. All
|
||||
buffer allocating, copying, size calculations, extending, etc. are done by
|
||||
a single piece of generic code. The size security checks need to be written
|
||||
once. You can concentrate on getting this one instance of code correct.
|
||||
|
||||
From the client's point of view, they are no longer dealing with a buffer. The
|
||||
buffer is encapsulated within the buffer API. All modifications to the buffer
|
||||
safely go through the API. If this sounds familiar, it is because what vsftpd
|
||||
implements is very similar to a C++ string class. You can do OO programming
|
||||
in C too, you know ;-)
|
||||
|
||||
A key point of having the buffer API is place is that is it MORE DIFFICULT to
|
||||
abuse the API than it is to use it properly. Try and create a buffer memory
|
||||
corruption or overflow scenario using just the buffer API.
|
||||
|
||||
|
||||
Unfortunately, secure string/buffer usage through a common API has not caught
|
||||
on much, despite the benefits it brings. Is it under publicised as a solution?
|
||||
Or do people have too much sentimental attachment to strcpy(), strlen(),
|
||||
malloc(), strcat() etc? Of notable exception, it is my understanding that at
|
||||
least the rather secure qmail program uses secure buffer handling, and I'd
|
||||
expect that to extend to all Dan Bernstein software. (Let me know of other good
|
||||
examples).
|
||||
|
12
SECURITY/OVERVIEW
Normal file
12
SECURITY/OVERVIEW
Normal file
@ -0,0 +1,12 @@
|
||||
The documents in this directory contain information about the security of
|
||||
vsftpd. They explain why various aspects of vsftpd were coded the way they
|
||||
are.
|
||||
|
||||
File Contents
|
||||
|
||||
DESIGN Comments on the overall architecture of vsftpd, from a
|
||||
security standpoint.
|
||||
IMPLEMENTATION Comments on steps taken to ensure a secure implementation.
|
||||
TRUST Comments on external components trusted or distrusted by
|
||||
vsftpd.
|
||||
|
111
SECURITY/TRUST
Normal file
111
SECURITY/TRUST
Normal file
@ -0,0 +1,111 @@
|
||||
This document describes what the vsftpd code trusts, what it doesn't trust, and
|
||||
the reasoning behind any trust decisions.
|
||||
|
||||
The importance of trust and trust relationships
|
||||
===============================================
|
||||
|
||||
Imagine a largely well written and secure piece of code. Now imagine that this
|
||||
piece of code delegates a task to an external program, perhaps in the name of
|
||||
code reuse. Now, if this external program is sloppily coded and insecure, we've
|
||||
wasted a lot of effort making our original program secure; our erroneous trust
|
||||
of the buggy external program means we have a security leak, even though we
|
||||
were careful in _our_ code.
|
||||
|
||||
There is a very similar situation with buggy library APIs. Imagine our secure
|
||||
program calling some complex library function which lets the side down by
|
||||
containing a security hole.
|
||||
|
||||
Lets put some concrete examples on the two similar above considerations. We can
|
||||
even give examples in the context of FTP daemons.
|
||||
|
||||
1) External /bin/ls helper
|
||||
|
||||
A very common operation asked of FTP servers is to provide a directory listing.
|
||||
Unfortunately, convention seems to be to emit the directory listing in UNIX
|
||||
"/bin/ls -l" format. Even the Microsoft FTP service can be observed to do this.
|
||||
When writing an FTP server for the UNIX platform, then, this leads to the
|
||||
temptation to reuse /bin/ls as a child process, to avoid having to rewrite a
|
||||
load of code to handle directory listings.
|
||||
|
||||
Even more unfortunately, FTP server writers seem to want to adopt the
|
||||
versatility of the average /bin/ls implementation. This means they allow
|
||||
clients to specify arbitrary parameters to /bin/ls.
|
||||
|
||||
By using an external /bin/ls command, we would tie the security of our FTP
|
||||
server to that of the /bin/ls code. Be careful not to underestimate the amount
|
||||
of code paths in /bin/ls which are explorable by a remote malicious user. GNU
|
||||
/bin/ls has a myriad of options. Some of these options are complex such as -I
|
||||
or the various formatting options. All it takes is a single coding flaw in the
|
||||
handling of one of these options, and your FTP security is in trouble.
|
||||
|
||||
By using an external /bin/ls, you also inherit the risk of any dangerous or
|
||||
complex APIs it uses. For example, calls to libc's complex fnmatch() or
|
||||
glob() functions, which will get given arbitrary malicious user controlled
|
||||
data as the search patterns. Also remember that users (and sometimes remote
|
||||
users) can upload/create files, and filenames are a very prominent input
|
||||
to /bin/ls.
|
||||
|
||||
To conclude: vsftpd has no intention of using an external /bin/ls program
|
||||
because of the risks outlined above. Even if I were to audit e.g. GNU
|
||||
fileutils /bin/ls, and also important parts of glibc, this would still leave
|
||||
security in an unknown state on other platforms. The solution I have employed
|
||||
is to write a minimal internal implementation of a /bin/ls listing generator;
|
||||
it's hardly difficult. As a happy side effect, this will boost performance by
|
||||
avoiding unneccesary fork()s and exec()s!
|
||||
|
||||
Here's some quick data about FTP servers which tend to use external ls
|
||||
programs:
|
||||
|
||||
ftp.wuftpd.org:
|
||||
ftp> ls --version
|
||||
227 Entering Passive Mode (x.x.x.x.x.x)
|
||||
150 Opening ASCII mode data connection for /bin/ls.
|
||||
ls (GNU fileutils) 3.16
|
||||
226 Transfer complete.
|
||||
|
||||
ftp.digital.com:
|
||||
ftp> ls -v
|
||||
227 Entering Passive Mode (x.x.x.x.x.x)
|
||||
150 Opening ASCII mode data connection for /bin/ls.
|
||||
/bin/ls: illegal option -- v
|
||||
usage: ls [ -1ACFLRabcdfgilmnopqrstux ] [files]
|
||||
226 Transfer complete.
|
||||
|
||||
Note that /bin/ls is not the only external program invoked by common FTP
|
||||
servers such as wu-ftpd. wu-ftpd also has the ability to invoke "tar" and
|
||||
"gzip" on the fly, so there are trust relationships there too.
|
||||
|
||||
|
||||
2) Complex library APIs
|
||||
|
||||
vsftpd is very careful to avoid using library calls which are potentially
|
||||
dangerous. I would typically classify calls as dangerous if they interact
|
||||
with the network non-trivially, or take malicious user supplied data and
|
||||
start parsing it in a major way.
|
||||
|
||||
Some examples are clearly required (vsftpd avoids using any of the following):
|
||||
|
||||
1) fnmatch(). This is the libc glob pattern matcher. The danger comes
|
||||
from the fact that the user supplies the glob pattern - "ls *.mp3" would
|
||||
be a simple example. Furthermore, glob pattern matching is complex and
|
||||
involves a lot of string handling.
|
||||
|
||||
2) gethostbyaddr(). This is a libc call to resolve an IP address to a hostname.
|
||||
Unfortunately, doing this is quite complicated. When you call gethostbyaddr(),
|
||||
a lot of work goes on under the covers. This usually involves making a network
|
||||
call out to the DNS server, and, dangerously, parsing the response.
|
||||
|
||||
For clarity (and clarity is a very important part of security), all external
|
||||
APIs used by vsftpd are encapsulated within two "system interaction" files,
|
||||
named "sysutil.c", and "sysdeputil.c" (for the more variable/system dependent
|
||||
calls). This provides a convenient audit point for ascertaining which calls
|
||||
vsftpd trusts.
|
||||
|
||||
|
||||
Summary
|
||||
=======
|
||||
|
||||
Be very aware of what APIs and/or programs you are trusting, or you might end
|
||||
up creating a trust relationship which makes your program exploitable --
|
||||
through no direct fault of your own.
|
||||
|
8
SIZE
Normal file
8
SIZE
Normal file
@ -0,0 +1,8 @@
|
||||
I'm not sure what you expected to find in this file :-)
|
||||
|
||||
Anyway, this is to explain that vsftpd is not as much code as you might
|
||||
expect from running a command like "wc -l *.c". Why? Simply because I use
|
||||
a very verbose style of coding in vsftpd, which consumes a lot of lines.
|
||||
Verbose code is very important in a secure program. How can you verify a
|
||||
program's security if it is not readable?
|
||||
|
35
SPEED
Normal file
35
SPEED
Normal file
@ -0,0 +1,35 @@
|
||||
This FTPd should be very performant. The reasons for this are below, followed
|
||||
by specific benchmarks as and when I get them.
|
||||
|
||||
1) Generally, it is a fairly minimal FTPd. There should not be much code and/or
|
||||
syscall bloat.
|
||||
|
||||
2) For binary downloads, Linux sendfile() is used. This is a lot lighter on
|
||||
CPU/syscall usage than your regular read()/write() loop.
|
||||
|
||||
3) The "ls" command is fully internal. That is to say, an external "ls" command
|
||||
does not need to be launch. Launching an external process is costly because
|
||||
of the fork(), exec(), ELF loader startup, etc.
|
||||
|
||||
|
||||
It is not all good news, of course. Potential sources of poor performance
|
||||
include
|
||||
|
||||
1) Overhead of two processes per session (in common configurations).
|
||||
|
||||
2) Excessive heap usage hidden behind the string API.
|
||||
|
||||
|
||||
BENCHMARKS
|
||||
==========
|
||||
|
||||
1) vsftpd downloads ASCII data at at least twice the rate of wu-ftpd.
|
||||
|
||||
2) vsftpd has achieved 86Mbyte/sec download over Gigabit ethernet between
|
||||
Linux-2.4.x boxes (thanks to sendfile())
|
||||
|
||||
3) vsftpd has smaller virtual memory usage (and RSS, it seems)
|
||||
|
||||
4) Various reports have trickled in and indicate that vsftpd thumps wu-ftpd
|
||||
in performance tests.
|
||||
|
27
TODO
Normal file
27
TODO
Normal file
@ -0,0 +1,27 @@
|
||||
CRITICAL
|
||||
========
|
||||
|
||||
NOT SO CRITICAL
|
||||
===============
|
||||
|
||||
- IPv6 support
|
||||
- "make install" should install man pages
|
||||
|
||||
ON THE BACK BURNER
|
||||
==================
|
||||
|
||||
- "Minimal" build support
|
||||
- Small race: signal might come in just before we start a blocking call
|
||||
- Support for "welcome.msg" on initial connection
|
||||
- wtmp support
|
||||
- OpenSSL support
|
||||
- transparent compression support
|
||||
- transparent tar support
|
||||
|
||||
NOT PLANNED
|
||||
===========
|
||||
|
||||
- syslog() support - I don't want to encourage the broken beast
|
||||
- telnet strings (no demand)
|
||||
- better pattern matching in "ls" (no demand)
|
||||
- standalone support (no demand, connections not a bottleneck)
|
26
TUNING
Normal file
26
TUNING
Normal file
@ -0,0 +1,26 @@
|
||||
So, you want vsftpd to go quickly?
|
||||
|
||||
Here are some random assorted performance tips.
|
||||
|
||||
1) vsftpd thrives because of its lightweight RSS and vm usage. If you run
|
||||
a glibc based system (e.g. RedHat 5+), look in /etc/nsswitch.conf, and
|
||||
if possible, disable the "nis" and "nisplus" options for "passwd", "shadow"
|
||||
and "group". This prevents unneeded runtime libraries being added into
|
||||
the vsftpd virtual memory space.
|
||||
|
||||
2) vsftpd will attempt to save CPU power by using sendfile() on capable
|
||||
operating systems. Currently, Linux 2.2+ and FreeBSD 3.0+ use sendfile().
|
||||
Consider running on these excellent operating systems.
|
||||
|
||||
3) Irritated by vsftpd using _two_ processes per connection? Don't be, it's
|
||||
a very secure architecture. However, if you run Linux 2.4+, or Linux 2.2.19, a
|
||||
"one process" security model is possible thanks to nifty security features.
|
||||
See the vsftpd.conf man page.
|
||||
|
||||
4) Avoid large directories (e.g. thousands of entries) if possible. Many
|
||||
filesystems do not handle such cases efficiently at all. Preparing large
|
||||
directory listings will require vsftpd to use moderate amounts of memory
|
||||
and CPU. If you _must_ have large directories, consider either making them
|
||||
unreadable, or use a filesystem which copes well with large directories such
|
||||
as reiserfs.
|
||||
|
54
ascii.c
Normal file
54
ascii.c
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* ascii.c
|
||||
*
|
||||
* Routines to handle ASCII mode tranfers. Yuk.
|
||||
*/
|
||||
|
||||
unsigned int
|
||||
vsf_ascii_ascii_to_bin(const char* p_in, char* p_out, unsigned int in_len)
|
||||
{
|
||||
/* Task: translate all \r\n into plain \n
|
||||
* For simplicity, I'm cheating and just ripping out all \r. If someone
|
||||
* complains about it breaking something, it'll get fixed.
|
||||
*/
|
||||
unsigned int index = 0;
|
||||
unsigned int written = 0;
|
||||
while (index < in_len)
|
||||
{
|
||||
char the_char = p_in[index];
|
||||
if (the_char != '\r')
|
||||
{
|
||||
*p_out++ = the_char;
|
||||
written++;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
vsf_ascii_bin_to_ascii(const char* p_in, char* p_out, unsigned int in_len)
|
||||
{
|
||||
/* Task: translate all \n into \r\n. Note that \r\n becomes \r\r\n. That's
|
||||
* what wu-ftpd does, and it's easier :-)
|
||||
*/
|
||||
unsigned int index = 0;
|
||||
unsigned int written = 0;
|
||||
while (index < in_len)
|
||||
{
|
||||
char the_char = p_in[index];
|
||||
if (the_char == '\n')
|
||||
{
|
||||
*p_out++ = '\r';
|
||||
written++;
|
||||
}
|
||||
*p_out++ = the_char;
|
||||
written++;
|
||||
index++;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
37
ascii.h
Normal file
37
ascii.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef VSFTP_ASCII_H
|
||||
#define VSFTP_ASCII_H
|
||||
|
||||
struct mystr;
|
||||
|
||||
/* vsf_ascii_ascii_to_bin()
|
||||
* PURPOSE
|
||||
* This function converts an input buffer from ascii format to binary format.
|
||||
* This entails ripping out all occurences of '\r'. The result is stored in
|
||||
* "p_out".
|
||||
* PARAMETERS
|
||||
* p_in - the input buffer, which is not modified
|
||||
* p_out - the output buffer, which MUST BE at least as big as "in_len"
|
||||
* in_len - the length in bytes of the input buffer
|
||||
* RETURNS
|
||||
* The number of characters stored in the output buffer.
|
||||
*/
|
||||
unsigned int vsf_ascii_ascii_to_bin(const char* p_in, char* p_out,
|
||||
unsigned int in_len);
|
||||
/* vsf_ascii_bin_to_ascii()
|
||||
* PURPOSE
|
||||
* This function converts an input buffer from binary format to ascii format.
|
||||
* This entails replacing all occurences of '\n' with '\r\n'. The result is
|
||||
* stored in "p_out".
|
||||
* PARAMETERS
|
||||
* p_in - the input buffer, which is not modified
|
||||
* p_out - the output buffer, which MUST BE at least TWICE as big as
|
||||
* "in_len"
|
||||
* in_len - the length in bytes of the input buffer
|
||||
* RETURNS
|
||||
* The number of characters stored in the output buffer
|
||||
*/
|
||||
unsigned int vsf_ascii_bin_to_ascii(const char* p_in, char* p_out,
|
||||
unsigned int in_len);
|
||||
|
||||
#endif /* VSFTP_ASCII_H */
|
||||
|
20
defs.h
Normal file
20
defs.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef VSF_DEFS_H
|
||||
#define VSF_DEFS_H
|
||||
|
||||
#define VSFTP_DEFAULT_CONFIG "/etc/vsftpd.conf"
|
||||
|
||||
#define VSFTP_COMMAND_FD 0
|
||||
|
||||
#define VSFTP_PASSWORD_MAX 128
|
||||
#define VSFTP_USERNAME_MAX 32
|
||||
#define VSFTP_MAX_COMMAND_LINE 4096
|
||||
#define VSFTP_PRIVSOCK_MAXSTR 1024
|
||||
#define VSFTP_DATA_BUFSIZE 65536
|
||||
#define VSFTP_DIR_BUFSIZE 16384
|
||||
#define VSFTP_PATH_MAX 4096
|
||||
#define VSFTP_CONF_FILE_MAX 100000
|
||||
|
||||
#define VSFTP_SECURE_UMASK 077
|
||||
|
||||
#endif /* VSF_DEFS_H */
|
||||
|
69
dirchange.c
Normal file
69
dirchange.c
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* dirchange.c
|
||||
*
|
||||
* Calls exposed to handle the junk a typical FTP server has to do upon
|
||||
* entering a new directory (messages, etc).
|
||||
*/
|
||||
|
||||
#include "dirchange.h"
|
||||
#include "strlist.h"
|
||||
#include "str.h"
|
||||
#include "sysstr.h"
|
||||
#include "tunables.h"
|
||||
#include "ftpcmdio.h"
|
||||
#include "filestr.h"
|
||||
#include "session.h"
|
||||
#include "sysutil.h"
|
||||
|
||||
/* Definitions */
|
||||
#define VSFTP_MAX_VISIT_REMEMBER 100
|
||||
#define VSFTP_MAX_MSGFILE_SIZE 1000
|
||||
|
||||
void
|
||||
dir_changed(struct vsf_session* p_sess, int ftpcode)
|
||||
{
|
||||
struct mystr dir_str = INIT_MYSTR;
|
||||
/* Do nothing if .message support is off */
|
||||
if (!tunable_dirmessage_enable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (p_sess->p_visited_dir_list == 0)
|
||||
{
|
||||
struct mystr_list the_list = INIT_STRLIST;
|
||||
p_sess->p_visited_dir_list = vsf_sysutil_malloc(sizeof(struct mystr_list));
|
||||
*p_sess->p_visited_dir_list = the_list;
|
||||
}
|
||||
str_getcwd(&dir_str);
|
||||
/* Do nothing if we already visited this directory */
|
||||
if (!str_list_contains_str(p_sess->p_visited_dir_list, &dir_str))
|
||||
{
|
||||
/* Just in case, cap the max. no of visited directories we'll remember */
|
||||
if (str_list_get_length(p_sess->p_visited_dir_list) <
|
||||
VSFTP_MAX_VISIT_REMEMBER)
|
||||
{
|
||||
str_list_add(p_sess->p_visited_dir_list, &dir_str, 0);
|
||||
}
|
||||
/* If we have a .message file, squirt it out prepended by the ftpcode and
|
||||
* the continuation mark '-'
|
||||
*/
|
||||
{
|
||||
struct mystr msg_file_str = INIT_MYSTR;
|
||||
struct mystr msg_line_str = INIT_MYSTR;
|
||||
unsigned int str_pos = 0;
|
||||
(void) str_fileread(&msg_file_str, tunable_message_file,
|
||||
VSFTP_MAX_MSGFILE_SIZE);
|
||||
while (str_getline(&msg_file_str, &msg_line_str, &str_pos))
|
||||
{
|
||||
vsf_cmdio_write_str_hyphen(p_sess, ftpcode, &msg_line_str);
|
||||
}
|
||||
str_free(&msg_file_str);
|
||||
str_free(&msg_line_str);
|
||||
}
|
||||
}
|
||||
str_free(&dir_str);
|
||||
}
|
||||
|
20
dirchange.h
Normal file
20
dirchange.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef VSF_DIRCHANGE_H
|
||||
#define VSF_DIRCHANGE_H
|
||||
|
||||
struct vsf_session;
|
||||
|
||||
/* dir_changed()
|
||||
* PURPOSE
|
||||
* This function, when called, will check if the current directory has just
|
||||
* been entered for the first time in this session. If so, and message file
|
||||
* support is on, a message file is looked for (default .message), and output
|
||||
* to the FTP control connection with the FTP code prefix specified by
|
||||
* "ftpcode".
|
||||
* PARAMETERS
|
||||
* p_sess - the current FTP session object
|
||||
* ftpcode - the FTP code to show with the message
|
||||
*/
|
||||
void dir_changed(struct vsf_session* p_sess, int ftpcode);
|
||||
|
||||
#endif /* VSF_DIRCHANGE_H */
|
||||
|
6
dummyinc/security/pam_appl.h
Normal file
6
dummyinc/security/pam_appl.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef VSF_DUMMYINC_PAM_APPL_H
|
||||
#define VSF_DUMMYINC_PAM_APPL_H
|
||||
|
||||
#undef VSF_SYSDEP_HAVE_PAM
|
||||
|
||||
#endif /* VSF_DUMMYINC_PAM_APPL_H */
|
7
dummyinc/shadow.h
Normal file
7
dummyinc/shadow.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef VSF_DUMMYINC_SHADOW_H
|
||||
#define VSF_DUMMYINC_SHADOW_H
|
||||
|
||||
#undef VSF_SYSDEP_HAVE_SHADOW
|
||||
|
||||
#endif /* VSF_DUMMYINC_SHADOW_H */
|
||||
|
54
filestr.c
Normal file
54
filestr.c
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* filestr.c
|
||||
*
|
||||
* This file contains extensions to the string/buffer API, to load a file
|
||||
* into a buffer.
|
||||
*/
|
||||
|
||||
#include "filestr.h"
|
||||
/* Get access to "private" functions */
|
||||
#define VSFTP_STRING_HELPER
|
||||
#include "str.h"
|
||||
#include "sysutil.h"
|
||||
#include "secbuf.h"
|
||||
|
||||
int
|
||||
str_fileread(struct mystr* p_str, const char* p_filename, unsigned int maxsize)
|
||||
{
|
||||
int fd;
|
||||
int retval;
|
||||
unsigned long size;
|
||||
char* p_sec_buf = 0;
|
||||
struct vsf_sysutil_statbuf* p_stat = 0;
|
||||
/* In case we fail, make sure we return an empty string */
|
||||
str_empty(p_str);
|
||||
fd = vsf_sysutil_open_file(p_filename, kVSFSysUtilOpenReadOnly);
|
||||
if (vsf_sysutil_retval_is_error(fd))
|
||||
{
|
||||
return fd;
|
||||
}
|
||||
vsf_sysutil_fstat(fd, &p_stat);
|
||||
if (vsf_sysutil_statbuf_is_regfile(p_stat))
|
||||
{
|
||||
size = vsf_sysutil_statbuf_get_size(p_stat);
|
||||
if (size > maxsize)
|
||||
{
|
||||
size = maxsize;
|
||||
}
|
||||
vsf_secbuf_alloc(&p_sec_buf, (unsigned int) size);
|
||||
|
||||
retval = vsf_sysutil_read_loop(fd, p_sec_buf, (unsigned int) size);
|
||||
if (!vsf_sysutil_retval_is_error(retval) && (unsigned int) retval == size)
|
||||
{
|
||||
str_alloc_memchunk(p_str, p_sec_buf, size);
|
||||
}
|
||||
}
|
||||
vsf_sysutil_free(p_stat);
|
||||
vsf_secbuf_free(&p_sec_buf);
|
||||
vsf_sysutil_close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
26
filestr.h
Normal file
26
filestr.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef VSF_FILESTR_H
|
||||
#define VSF_FILESTR_H
|
||||
|
||||
/* Forward declares */
|
||||
struct mystr;
|
||||
|
||||
/* str_fileread()
|
||||
* PURPOSE
|
||||
* Read the contents of a file into a string buffer, up to a size limit of
|
||||
* "maxsize"
|
||||
* PARAMETERS
|
||||
* p_str - destination buffer object to contain the file
|
||||
* p_filename - the filename to try and read into the buffer
|
||||
* maxsize - the maximum amount of buffer we will fill. Larger files will
|
||||
* be truncated.
|
||||
* RETURNS
|
||||
* An integer representing the success/failure of opening the file
|
||||
* "p_filename". Zero indicates success. If successful, the file is read into
|
||||
* the "p_str" string object. If not successful, "p_str" will point to an
|
||||
* empty buffer.
|
||||
*/
|
||||
int str_fileread(struct mystr* p_str, const char* p_filename,
|
||||
unsigned int maxsize);
|
||||
|
||||
#endif /* VSF_FILESTR_H */
|
||||
|
186
ftpcmdio.c
Normal file
186
ftpcmdio.c
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* ftpcmdio.c
|
||||
*
|
||||
* Routines applicable to reading and writing the FTP command stream.
|
||||
*/
|
||||
|
||||
#include "ftpcmdio.h"
|
||||
#include "ftpcodes.h"
|
||||
#include "str.h"
|
||||
#include "netstr.h"
|
||||
#include "sysutil.h"
|
||||
#include "tunables.h"
|
||||
#include "defs.h"
|
||||
#include "secbuf.h"
|
||||
#include "utility.h"
|
||||
#include "logging.h"
|
||||
#include "session.h"
|
||||
|
||||
/* 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);
|
||||
static void ftp_write_str_common(struct vsf_session* p_sess, int status,
|
||||
char sep, const struct mystr* p_str,
|
||||
int noblock);
|
||||
static void handle_alarm_timeout(void* p_private);
|
||||
|
||||
void
|
||||
vsf_cmdio_sock_setup(void)
|
||||
{
|
||||
vsf_sysutil_activate_keepalive(VSFTP_COMMAND_FD);
|
||||
vsf_sysutil_set_nodelay(VSFTP_COMMAND_FD);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_alarm_timeout(void* p_private)
|
||||
{
|
||||
struct vsf_session* p_sess = (struct vsf_session*) p_private;
|
||||
vsf_cmdio_write_noblock(p_sess, FTP_IDLE_TIMEOUT, "Timeout. Pay attention.");
|
||||
vsf_sysutil_exit(0);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void
|
||||
ftp_write_text_common(struct vsf_session* p_sess, int status,
|
||||
const char* p_text, int noblock)
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
|
||||
void
|
||||
vsf_cmdio_write_str_hyphen(struct vsf_session* p_sess, int status,
|
||||
const struct mystr* p_str)
|
||||
{
|
||||
ftp_write_str_common(p_sess, status, '-', p_str, 0);
|
||||
}
|
||||
|
||||
void
|
||||
vsf_cmdio_write_str(struct vsf_session* p_sess, int status,
|
||||
const struct mystr* p_str)
|
||||
{
|
||||
ftp_write_str_common(p_sess, status, ' ', p_str, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
ftp_write_str_common(struct vsf_session* p_sess, int status, char sep,
|
||||
const struct mystr* p_str, int noblock)
|
||||
{
|
||||
static struct mystr s_write_buf_str;
|
||||
static struct mystr s_text_mangle_str;
|
||||
if (tunable_log_ftp_protocol)
|
||||
{
|
||||
str_alloc_ulong(&s_write_buf_str, (unsigned long) status);
|
||||
str_append_char(&s_write_buf_str, sep);
|
||||
str_append_str(&s_write_buf_str, p_str);
|
||||
vsf_log_line(p_sess, kVSFLogEntryFTPOutput, &s_write_buf_str);
|
||||
}
|
||||
str_copy(&s_text_mangle_str, p_str);
|
||||
/* Process the output response according to the specifications.. */
|
||||
/* Escape telnet characters properly */
|
||||
str_replace_text(&s_text_mangle_str, "\377", "\377\377");
|
||||
/* Change \n for \0 in response */
|
||||
str_replace_char(&s_text_mangle_str, '\n', '\0');
|
||||
/* Build string to squirt down network */
|
||||
str_alloc_ulong(&s_write_buf_str, (unsigned long) status);
|
||||
str_append_char(&s_write_buf_str, sep);
|
||||
str_append_str(&s_write_buf_str, &s_text_mangle_str);
|
||||
str_append_text(&s_write_buf_str, "\r\n");
|
||||
if (noblock)
|
||||
{
|
||||
(void) str_netfd_write_noblock(&s_write_buf_str, VSFTP_COMMAND_FD);
|
||||
}
|
||||
else
|
||||
{
|
||||
int retval = str_netfd_write(&s_write_buf_str, VSFTP_COMMAND_FD);
|
||||
if (retval != 0)
|
||||
{
|
||||
die("str_netfd_write");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vsf_cmdio_set_alarm(struct vsf_session* p_sess)
|
||||
{
|
||||
if (tunable_idle_session_timeout > 0)
|
||||
{
|
||||
vsf_sysutil_install_sighandler(kVSFSysUtilSigALRM, handle_alarm_timeout,
|
||||
p_sess);
|
||||
vsf_sysutil_set_alarm(tunable_idle_session_timeout);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vsf_cmdio_get_cmd_and_arg(struct vsf_session* p_sess, struct mystr* p_cmd_str,
|
||||
struct mystr* p_arg_str, int set_alarm)
|
||||
{
|
||||
/* Prepare an alarm to timeout the session.. */
|
||||
if (set_alarm)
|
||||
{
|
||||
vsf_cmdio_set_alarm(p_sess);
|
||||
}
|
||||
/* Blocks */
|
||||
ftp_getline(p_cmd_str);
|
||||
str_split_char(p_cmd_str, p_arg_str, ' ');
|
||||
str_upper(p_cmd_str);
|
||||
if (tunable_log_ftp_protocol)
|
||||
{
|
||||
static struct mystr s_log_str;
|
||||
if (str_equal_text(p_cmd_str, "PASS"))
|
||||
{
|
||||
str_alloc_text(&s_log_str, "PASS <password>");
|
||||
}
|
||||
else
|
||||
{
|
||||
str_copy(&s_log_str, p_cmd_str);
|
||||
if (!str_isempty(p_arg_str))
|
||||
{
|
||||
str_append_char(&s_log_str, ' ');
|
||||
str_append_str(&s_log_str, p_arg_str);
|
||||
}
|
||||
}
|
||||
vsf_log_line(p_sess, kVSFLogEntryFTPInput, &s_log_str);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ftp_getline(struct mystr* p_str)
|
||||
{
|
||||
static char* s_p_readline_buf;
|
||||
if (s_p_readline_buf == 0)
|
||||
{
|
||||
vsf_secbuf_alloc(&s_p_readline_buf, VSFTP_MAX_COMMAND_LINE);
|
||||
}
|
||||
str_netfd_alloc(p_str, VSFTP_COMMAND_FD, '\n', s_p_readline_buf,
|
||||
VSFTP_MAX_COMMAND_LINE);
|
||||
/* As mandated by the FTP specifications.. */
|
||||
str_replace_char(p_str, '\0', '\n');
|
||||
/* 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')
|
||||
{
|
||||
str_trunc(p_str, len - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
75
ftpcmdio.h
Normal file
75
ftpcmdio.h
Normal file
@ -0,0 +1,75 @@
|
||||
#ifndef VSF_FTPCMDIO_H
|
||||
#define VSF_FTPCMDIO_H
|
||||
|
||||
struct mystr;
|
||||
struct vsf_session;
|
||||
|
||||
/* vsf_cmdio_sock_setup()
|
||||
* PURPOSE
|
||||
* Initialise a few socket settings (keepalive, nonagle, etc). on the FTP
|
||||
* control connection.
|
||||
*/
|
||||
void vsf_cmdio_sock_setup(void);
|
||||
|
||||
/* vsf_cmdio_write()
|
||||
* PURPOSE
|
||||
* Write a response to the FTP control connection.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* status - the status code to report
|
||||
* p_text - the text to report
|
||||
*/
|
||||
void vsf_cmdio_write(struct vsf_session* p_sess, int status,
|
||||
const char* p_text);
|
||||
|
||||
/* vsf_cmdio_write_noblock()
|
||||
* PURPOSE
|
||||
* The same as vsf_cmdio_write(), apart from the fact we _guarantee_ not to
|
||||
* block (ditching output if neccessary). This is useful for messages as
|
||||
* we exit, to avoid getting stuck on exit.
|
||||
*/
|
||||
void vsf_cmdio_write_noblock(struct vsf_session* p_sess, int status,
|
||||
const char* p_text);
|
||||
|
||||
/* vsf_cmdio_write_str()
|
||||
* PURPOSE
|
||||
* The same as vsf_cmdio_write(), apart from the text is specified as a
|
||||
* string buffer object "p_str".
|
||||
*/
|
||||
void vsf_cmdio_write_str(struct vsf_session* p_sess, int status,
|
||||
const struct mystr* p_str);
|
||||
|
||||
/* vsf_cmdio_write_str_hyphen()
|
||||
* PURPOSE
|
||||
* The same as vsf_cmdio_write_str(), apart from the response line is
|
||||
* output with the continuation indicator '-' between the response code and
|
||||
* the response text. This indicates there are more lines of response.
|
||||
*/
|
||||
void vsf_cmdio_write_str_hyphen(struct vsf_session* p_sess, int status,
|
||||
const struct mystr* p_str);
|
||||
|
||||
/* vsf_cmdio_set_alarm()
|
||||
* PURPOSE
|
||||
* Activate the control connection inactivity timeout. This is explicitly
|
||||
* exposed in the API so that we can play it safe, and activate the alarm
|
||||
* before _any_ potentially blocking calls.
|
||||
* PARAMETERS
|
||||
* p_sess - The current session object
|
||||
*/
|
||||
void vsf_cmdio_set_alarm(struct vsf_session* p_sess);
|
||||
|
||||
/* vsf_cmdio_get_cmd_and_arg()
|
||||
* PURPOSE
|
||||
* Read an FTP command (and optional argument) from the FTP control connection.
|
||||
* PARAMETERS
|
||||
* p_sess - The current session object
|
||||
* p_cmd_str - Where to put the FTP command string (may be empty)
|
||||
* p_arg_str - Where to put the FTP argument string (may be empty)
|
||||
* set_alarm - If true, the control connection inactivity monitor is used
|
||||
*/
|
||||
void vsf_cmdio_get_cmd_and_arg(struct vsf_session* p_sess,
|
||||
struct mystr* p_cmd_str,
|
||||
struct mystr* p_arg_str, int set_alarm);
|
||||
|
||||
#endif /* VSF_FTPCMDIO_H */
|
||||
|
46
ftpcodes.h
Normal file
46
ftpcodes.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef VSF_FTPCODES_H
|
||||
#define VSF_FTPCODES_H
|
||||
|
||||
#define FTP_DATACONN 150
|
||||
|
||||
#define FTP_NOOPOK 200
|
||||
#define FTP_TYPEOK 200
|
||||
#define FTP_PORTOK 200
|
||||
#define FTP_UMASKOK 200
|
||||
#define FTP_CHMODOK 200
|
||||
#define FTP_SIZEOK 213
|
||||
#define FTP_MDTMOK 213
|
||||
#define FTP_SYSTOK 215
|
||||
#define FTP_GREET 220
|
||||
#define FTP_GOODBYE 221
|
||||
#define FTP_ABOR_NOCONN 225
|
||||
#define FTP_TRANSFEROK 226
|
||||
#define FTP_ABOROK 226
|
||||
#define FTP_PASVOK 227
|
||||
#define FTP_LOGINOK 230
|
||||
#define FTP_CWDOK 250
|
||||
#define FTP_RMDIROK 250
|
||||
#define FTP_DELEOK 250
|
||||
#define FTP_RENAMEOK 250
|
||||
#define FTP_PWDOK 257
|
||||
#define FTP_MKDIROK 257
|
||||
|
||||
#define FTP_GIVEPWORD 331
|
||||
#define FTP_RESTOK 350
|
||||
#define FTP_RNFROK 350
|
||||
|
||||
#define FTP_IDLE_TIMEOUT 421
|
||||
#define FTP_DATA_TIMEOUT 421
|
||||
#define FTP_BADSENDCONN 425
|
||||
#define FTP_BADSENDNET 426
|
||||
#define FTP_BADSENDFILE 451
|
||||
|
||||
#define FTP_BADCMD 500
|
||||
#define FTP_BADHELP 502
|
||||
#define FTP_NEEDUSER 503
|
||||
#define FTP_NEEDRNFR 503
|
||||
#define FTP_LOGINERR 530
|
||||
#define FTP_FILEFAIL 550
|
||||
#define FTP_UPLOADFAIL 553
|
||||
|
||||
#endif /* VSF_FTPCODES_H */
|
552
ftpdataio.c
Normal file
552
ftpdataio.c
Normal file
@ -0,0 +1,552 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* ftpdataio.c
|
||||
*
|
||||
* Code to handle FTP data connections. This includes both PORT (server
|
||||
* connects) and PASV (client connects) modes of data transfer. This
|
||||
* includes sends and receives, files and directories.
|
||||
*/
|
||||
|
||||
#include "ftpdataio.h"
|
||||
#include "session.h"
|
||||
#include "ftpcmdio.h"
|
||||
#include "ftpcodes.h"
|
||||
#include "utility.h"
|
||||
#include "tunables.h"
|
||||
#include "defs.h"
|
||||
#include "str.h"
|
||||
#include "strlist.h"
|
||||
#include "sysutil.h"
|
||||
#include "logging.h"
|
||||
#include "secbuf.h"
|
||||
#include "sysstr.h"
|
||||
#include "sysdeputil.h"
|
||||
#include "ascii.h"
|
||||
#include "oneprocess.h"
|
||||
#include "twoprocess.h"
|
||||
#include "ls.h"
|
||||
#include "netstr.h"
|
||||
|
||||
static void init_data_sock_params(struct vsf_session* p_sess, int sock_fd);
|
||||
static struct vsf_transfer_ret do_file_send_binary(struct vsf_session* p_sess,
|
||||
int net_fd, int file_fd);
|
||||
static struct vsf_transfer_ret do_file_send_ascii(struct vsf_session* p_sess,
|
||||
int net_fd, int file_fd);
|
||||
static struct vsf_transfer_ret do_file_recv(
|
||||
struct vsf_session* p_sess, int net_fd, int file_fd, int is_ascii);
|
||||
static void handle_sigalrm(void* p_private);
|
||||
static void start_data_alarm(struct vsf_session* p_sess);
|
||||
static void handle_io(int retval, int fd, void* p_private);
|
||||
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);
|
||||
static int write_dir_list(struct mystr_list* p_dir_list, int remote_fd);
|
||||
|
||||
void
|
||||
vsf_ftpdataio_dispose_transfer_fd(struct vsf_session* p_sess)
|
||||
{
|
||||
int retval;
|
||||
if (p_sess->data_fd == -1)
|
||||
{
|
||||
bug("no data descriptor in vsf_ftpdataio_dispose_transfer_fd");
|
||||
}
|
||||
/* Clear the data connection alarm */
|
||||
vsf_sysutil_clear_alarm();
|
||||
vsf_sysutil_uninstall_io_handler();
|
||||
/* This close() blocks because we set SO_LINGER */
|
||||
retval = vsf_sysutil_close_failok(p_sess->data_fd);
|
||||
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);
|
||||
}
|
||||
p_sess->data_fd = -1;
|
||||
}
|
||||
|
||||
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);
|
||||
if (vsf_sysutil_retval_is_error(remote_fd))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDCONN,
|
||||
"Failed to establish connection.");
|
||||
return remote_fd;
|
||||
}
|
||||
/* SECURITY:
|
||||
* 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)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Security: Bad IP connecting.");
|
||||
vsf_sysutil_close(remote_fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
init_data_sock_params(p_sess, remote_fd);
|
||||
return remote_fd;
|
||||
}
|
||||
|
||||
int
|
||||
vsf_ftpdataio_get_port_fd(struct vsf_session* p_sess)
|
||||
{
|
||||
int retval;
|
||||
int remote_fd;
|
||||
if (tunable_connect_from_port_20)
|
||||
{
|
||||
if (tunable_one_process_model)
|
||||
{
|
||||
remote_fd = vsf_one_process_get_priv_data_sock(p_sess);
|
||||
}
|
||||
else
|
||||
{
|
||||
remote_fd = vsf_two_process_get_priv_data_sock(p_sess);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
remote_fd = vsf_sysutil_get_ipv4_sock();
|
||||
}
|
||||
retval = vsf_sysutil_connect_timeout(remote_fd, p_sess->p_port_sockaddr,
|
||||
tunable_connect_timeout);
|
||||
if (vsf_sysutil_retval_is_error(retval))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDCONN,
|
||||
"Failed to establish connection.");
|
||||
vsf_sysutil_close(remote_fd);
|
||||
return -1;
|
||||
}
|
||||
init_data_sock_params(p_sess, remote_fd);
|
||||
return remote_fd;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_sigalrm(void* p_private)
|
||||
{
|
||||
struct vsf_session* p_sess = (struct vsf_session*) p_private;
|
||||
if (!p_sess->data_progress)
|
||||
{
|
||||
vsf_cmdio_write_noblock(p_sess, FTP_DATA_TIMEOUT,
|
||||
"Data timeout. Reconnect. Sorry.");
|
||||
vsf_sysutil_exit(0);
|
||||
}
|
||||
p_sess->data_progress = 0;
|
||||
start_data_alarm(p_sess);
|
||||
}
|
||||
|
||||
void
|
||||
start_data_alarm(struct vsf_session* p_sess)
|
||||
{
|
||||
if (tunable_data_connection_timeout > 0)
|
||||
{
|
||||
vsf_sysutil_install_sighandler(kVSFSysUtilSigALRM, handle_sigalrm, p_sess);
|
||||
vsf_sysutil_set_alarm(tunable_data_connection_timeout);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
init_data_sock_params(struct vsf_session* p_sess, int sock_fd)
|
||||
{
|
||||
if (p_sess->data_fd != -1)
|
||||
{
|
||||
bug("data descriptor still present in init_data_sock_params");
|
||||
}
|
||||
p_sess->data_fd = sock_fd;
|
||||
p_sess->data_progress = 0;
|
||||
vsf_sysutil_activate_keepalive(sock_fd);
|
||||
/* And in the vague hope it might help... */
|
||||
vsf_sysutil_set_iptos_throughput(sock_fd);
|
||||
/* Set up lingering, so that we wait for all data to transfer, and report
|
||||
* more accurate transfer rates.
|
||||
*/
|
||||
vsf_sysutil_activate_linger(sock_fd);
|
||||
/* Start the timeout monitor */
|
||||
vsf_sysutil_install_io_handler(handle_io, p_sess);
|
||||
start_data_alarm(p_sess);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_io(int retval, int fd, void* p_private)
|
||||
{
|
||||
long curr_sec;
|
||||
long curr_usec;
|
||||
unsigned int bw_rate;
|
||||
double elapsed;
|
||||
double pause_time;
|
||||
double rate_ratio;
|
||||
struct vsf_session* p_sess = (struct vsf_session*) p_private;
|
||||
if (p_sess->data_fd != fd || vsf_sysutil_retval_is_error(retval) ||
|
||||
retval == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* Note that the session hasn't stalled, i.e. don't time it out */
|
||||
p_sess->data_progress = 1;
|
||||
/* Apply bandwidth quotas via a little pause, if necessary */
|
||||
if (p_sess->bw_rate_max == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* Calculate bandwidth rate */
|
||||
vsf_sysutil_update_cached_time();
|
||||
curr_sec = vsf_sysutil_get_cached_time_sec();
|
||||
curr_usec = vsf_sysutil_get_cached_time_usec();
|
||||
elapsed = (double) (curr_sec - p_sess->bw_send_start_sec);
|
||||
elapsed += (double) (curr_usec - p_sess->bw_send_start_usec) /
|
||||
(double) 1000000;
|
||||
if (elapsed == (double) 0)
|
||||
{
|
||||
elapsed = (double) 0.01;
|
||||
}
|
||||
bw_rate = (unsigned int) ((double) retval / elapsed);
|
||||
if (bw_rate <= p_sess->bw_rate_max)
|
||||
{
|
||||
p_sess->bw_send_start_sec = curr_sec;
|
||||
p_sess->bw_send_start_usec = curr_usec;
|
||||
return;
|
||||
}
|
||||
/* Tut! Rate exceeded, calculate a pause to bring things back into line */
|
||||
rate_ratio = (double) bw_rate / (double) p_sess->bw_rate_max;
|
||||
pause_time = (rate_ratio - (double) 1) * elapsed;
|
||||
vsf_sysutil_sleep(pause_time);
|
||||
vsf_sysutil_update_cached_time();
|
||||
p_sess->bw_send_start_sec = vsf_sysutil_get_cached_time_sec();
|
||||
p_sess->bw_send_start_usec = vsf_sysutil_get_cached_time_usec();
|
||||
}
|
||||
|
||||
int
|
||||
vsf_ftpdataio_transfer_dir(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)
|
||||
{
|
||||
return transfer_dir_internal(p_sess, remote_fd, p_dir, p_base_dir_str,
|
||||
p_option_str, p_filter_str, is_verbose, 0);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct mystr_list dir_list = INIT_STRLIST;
|
||||
struct mystr_list subdir_list = INIT_STRLIST;
|
||||
struct mystr dir_prefix_str = INIT_MYSTR;
|
||||
struct mystr_list* p_subdir_list = 0;
|
||||
struct str_locate_result loc_result = str_locate_char(p_option_str, 'R');
|
||||
int failed = 0;
|
||||
if (loc_result.found && tunable_ls_recurse_enable)
|
||||
{
|
||||
p_subdir_list = &subdir_list;
|
||||
}
|
||||
vsf_ls_populate_dir_list(&dir_list, p_subdir_list, p_dir, p_base_dir_str,
|
||||
p_option_str, p_filter_str, is_verbose);
|
||||
if (p_subdir_list)
|
||||
{
|
||||
int retval;
|
||||
str_copy(&dir_prefix_str, p_base_dir_str);
|
||||
str_append_text(&dir_prefix_str, ":\r\n");
|
||||
retval = str_netfd_write(&dir_prefix_str, remote_fd);
|
||||
if (retval != 0)
|
||||
{
|
||||
failed = 1;
|
||||
}
|
||||
}
|
||||
if (!failed)
|
||||
{
|
||||
failed = write_dir_list(&dir_list, remote_fd);
|
||||
}
|
||||
/* Recurse into the subdirectories if required... */
|
||||
if (!failed)
|
||||
{
|
||||
struct mystr sub_str = INIT_MYSTR;
|
||||
unsigned int num_subdirs = str_list_get_length(&subdir_list);
|
||||
unsigned int subdir_index;
|
||||
for (subdir_index = 0; subdir_index < num_subdirs; subdir_index++)
|
||||
{
|
||||
int retval;
|
||||
struct vsf_sysutil_dir* p_subdir;
|
||||
const struct mystr* p_subdir_str =
|
||||
str_list_get_pstr(&subdir_list, subdir_index);
|
||||
if (str_equal_text(p_subdir_str, ".") ||
|
||||
str_equal_text(p_subdir_str, ".."))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
str_copy(&sub_str, p_base_dir_str);
|
||||
str_append_char(&sub_str, '/');
|
||||
str_append_str(&sub_str, p_subdir_str);
|
||||
p_subdir = str_opendir(&sub_str);
|
||||
if (p_subdir == 0)
|
||||
{
|
||||
/* Unreadable, gone missing, etc. - no matter */
|
||||
continue;
|
||||
}
|
||||
str_alloc_text(&dir_prefix_str, "\r\n");
|
||||
retval = str_netfd_write(&dir_prefix_str, remote_fd);
|
||||
if (retval != 0)
|
||||
{
|
||||
failed = 1;
|
||||
break;
|
||||
}
|
||||
retval = transfer_dir_internal(p_sess, remote_fd, p_subdir, &sub_str,
|
||||
p_option_str, p_filter_str,
|
||||
is_verbose, 1);
|
||||
vsf_sysutil_closedir(p_subdir);
|
||||
if (retval != 0)
|
||||
{
|
||||
failed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
str_free(&sub_str);
|
||||
}
|
||||
str_list_free(&dir_list);
|
||||
str_list_free(&subdir_list);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX - really, this should be refactored into a "buffered writer" object */
|
||||
static int
|
||||
write_dir_list(struct mystr_list* p_dir_list, int remote_fd)
|
||||
{
|
||||
/* This function writes out a list of strings to the client, over the
|
||||
* data socket. We now coalesce the strings into fewer write() syscalls,
|
||||
* which saved 33% CPU time writing a large directory.
|
||||
*/
|
||||
int retval = 0;
|
||||
unsigned int dir_index_max = str_list_get_length(p_dir_list);
|
||||
unsigned int dir_index;
|
||||
struct mystr buf_str = INIT_MYSTR;
|
||||
str_reserve(&buf_str, VSFTP_DIR_BUFSIZE);
|
||||
for (dir_index = 0; dir_index < dir_index_max; dir_index++)
|
||||
{
|
||||
str_append_str(&buf_str, str_list_get_pstr(p_dir_list, dir_index));
|
||||
if (dir_index == dir_index_max - 1 ||
|
||||
str_getlen(&buf_str) +
|
||||
str_getlen(str_list_get_pstr(p_dir_list, dir_index + 1)) >
|
||||
VSFTP_DIR_BUFSIZE)
|
||||
{
|
||||
/* Writeout needed - we're either at the end, or we filled the buffer */
|
||||
int writeret = str_netfd_write(&buf_str, remote_fd);
|
||||
if (writeret != 0)
|
||||
{
|
||||
retval = 1;
|
||||
break;
|
||||
}
|
||||
str_empty(&buf_str);
|
||||
}
|
||||
}
|
||||
str_free(&buf_str);
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct vsf_transfer_ret
|
||||
vsf_ftpdataio_transfer_file(struct vsf_session* p_sess, int remote_fd,
|
||||
int file_fd, int is_recv, int is_ascii)
|
||||
{
|
||||
if (!is_recv)
|
||||
{
|
||||
if (is_ascii)
|
||||
{
|
||||
return do_file_send_ascii(p_sess, remote_fd, file_fd);
|
||||
}
|
||||
else
|
||||
{
|
||||
return do_file_send_binary(p_sess, remote_fd, file_fd);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return do_file_recv(p_sess, remote_fd, file_fd, is_ascii);
|
||||
}
|
||||
}
|
||||
|
||||
static struct vsf_transfer_ret
|
||||
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 };
|
||||
if (p_readbuf == 0)
|
||||
{
|
||||
/* NOTE!! * 2 factor because we can double the data by doing our ASCII
|
||||
* linefeed mangling
|
||||
*/
|
||||
vsf_secbuf_alloc(&p_asciibuf, VSFTP_DATA_BUFSIZE * 2);
|
||||
vsf_secbuf_alloc(&p_readbuf, VSFTP_DATA_BUFSIZE);
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
unsigned int num_to_write;
|
||||
int retval = vsf_sysutil_read(file_fd, p_readbuf, VSFTP_DATA_BUFSIZE);
|
||||
if (vsf_sysutil_retval_is_error(retval))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDFILE, "Failure reading local file.");
|
||||
ret_struct.retval = -1;
|
||||
return ret_struct;
|
||||
}
|
||||
else if (retval == 0)
|
||||
{
|
||||
/* Success - cool */
|
||||
vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "File send OK.");
|
||||
return ret_struct;
|
||||
}
|
||||
num_to_write = vsf_ascii_bin_to_ascii(p_readbuf, p_asciibuf,
|
||||
(unsigned int) retval);
|
||||
retval = vsf_sysutil_write_loop(net_fd, p_asciibuf, num_to_write);
|
||||
if (vsf_sysutil_retval_is_error(retval) ||
|
||||
(unsigned int) retval != num_to_write)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDNET,
|
||||
"Failure writing network stream.");
|
||||
ret_struct.retval = -1;
|
||||
return ret_struct;
|
||||
}
|
||||
ret_struct.transferred += (unsigned int) retval;
|
||||
}
|
||||
}
|
||||
|
||||
static struct vsf_transfer_ret
|
||||
do_file_send_binary(struct vsf_session* p_sess, int net_fd, int file_fd)
|
||||
{
|
||||
static struct vsf_sysutil_statbuf* s_p_statbuf;
|
||||
int retval;
|
||||
unsigned long bytes_to_send;
|
||||
unsigned long init_file_offset;
|
||||
unsigned long curr_file_offset;
|
||||
unsigned long bytes_sent;
|
||||
unsigned int chunk_size;
|
||||
struct vsf_transfer_ret ret_struct = { 0, 0 };
|
||||
/* Work out how many bytes to send based on file size minus current offset */
|
||||
/* NOTE - if we're being pedantic here are two duplicated syscalls */
|
||||
vsf_sysutil_fstat(file_fd, &s_p_statbuf);
|
||||
bytes_to_send = vsf_sysutil_statbuf_get_size(s_p_statbuf);
|
||||
init_file_offset = vsf_sysutil_get_file_offset(file_fd);
|
||||
curr_file_offset = init_file_offset;
|
||||
/* Don't underflow if some bonehead sets a REST greater than the file size */
|
||||
if (init_file_offset > bytes_to_send)
|
||||
{
|
||||
bytes_to_send = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bytes_to_send -= init_file_offset;
|
||||
}
|
||||
if (p_sess->bw_rate_max)
|
||||
{
|
||||
chunk_size = VSFTP_DATA_BUFSIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk_size = 0;
|
||||
}
|
||||
/* Just because I can ;-) */
|
||||
retval = vsf_sysutil_sendfile(net_fd, file_fd, &curr_file_offset,
|
||||
bytes_to_send, chunk_size);
|
||||
bytes_sent = curr_file_offset - init_file_offset;
|
||||
ret_struct.transferred = bytes_sent;
|
||||
if (vsf_sysutil_retval_is_error(retval))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDNET, "Failure writing network stream.");
|
||||
ret_struct.retval = -1;
|
||||
return ret_struct;
|
||||
}
|
||||
else if (bytes_sent != bytes_to_send)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDFILE,
|
||||
"Failure writing network stream.");
|
||||
ret_struct.retval = -1;
|
||||
return ret_struct;
|
||||
}
|
||||
vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "File send OK.");
|
||||
return ret_struct;
|
||||
}
|
||||
|
||||
static struct vsf_transfer_ret
|
||||
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 };
|
||||
if (p_recvbuf == 0)
|
||||
{
|
||||
vsf_secbuf_alloc(&p_recvbuf, VSFTP_DATA_BUFSIZE);
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
int retval = vsf_sysutil_read(net_fd, p_recvbuf, VSFTP_DATA_BUFSIZE);
|
||||
if (vsf_sysutil_retval_is_error(retval))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDNET,
|
||||
"Failure reading network stream.");
|
||||
ret_struct.retval = -1;
|
||||
return ret_struct;
|
||||
}
|
||||
else if (retval == 0)
|
||||
{
|
||||
/* Transfer done, nifty */
|
||||
vsf_cmdio_write(p_sess, FTP_TRANSFEROK, "File receive OK.");
|
||||
return ret_struct;
|
||||
}
|
||||
num_to_write = (unsigned int) retval;
|
||||
ret_struct.transferred += num_to_write;
|
||||
if (is_ascii)
|
||||
{
|
||||
/* Handle ASCII conversion if we have to. Note that using the same
|
||||
* buffer for source and destination is safe, because the ASCII ->
|
||||
* binary transform only ever results in a smaller file.
|
||||
*/
|
||||
num_to_write = vsf_ascii_ascii_to_bin(p_recvbuf, p_recvbuf,
|
||||
num_to_write);
|
||||
}
|
||||
retval = vsf_sysutil_write_loop(file_fd, p_recvbuf, num_to_write);
|
||||
if (vsf_sysutil_retval_is_error(retval) ||
|
||||
(unsigned int) retval != num_to_write)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_BADSENDFILE,
|
||||
"Failure writing to local file.");
|
||||
ret_struct.retval = -1;
|
||||
return ret_struct;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
85
ftpdataio.h
Normal file
85
ftpdataio.h
Normal file
@ -0,0 +1,85 @@
|
||||
#ifndef VSF_FTPDATAIO_H
|
||||
#define VSF_FTPDATAIO_H
|
||||
|
||||
struct mystr;
|
||||
struct vsf_sysutil_sockaddr;
|
||||
struct vsf_sysutil_dir;
|
||||
struct vsf_session;
|
||||
|
||||
/* vsf_ftpdataio_dispose_transfer_fd()
|
||||
* PURPOSE
|
||||
* Close down the remote data transfer file descriptor. If unsent data reamins
|
||||
* on the connection, this method blocks until it is transferred. There is an
|
||||
* upper limit of 5 minutes.
|
||||
* PARAMETERS
|
||||
* p_sess - the current FTP session object
|
||||
*/
|
||||
void vsf_ftpdataio_dispose_transfer_fd(struct vsf_session* p_sess);
|
||||
|
||||
/* vsf_ftpdataio_get_pasv_fd()
|
||||
* PURPOSE
|
||||
* Return a connection data file descriptor obtained by the PASV connection
|
||||
* method. This includes accept()'ing a connection from the remote.
|
||||
* PARAMETERS
|
||||
* p_sess - the current FTP session object
|
||||
* RETURNS
|
||||
* The file descriptor upon success, or -1 upon error.
|
||||
*/
|
||||
int vsf_ftpdataio_get_pasv_fd(struct vsf_session* p_sess);
|
||||
|
||||
/* vsf_ftpdataio_get_pasv_fd()
|
||||
* PURPOSE
|
||||
* Return a connection data file descriptor obtained by the PORT connection
|
||||
* method. This includes connect()'ing to the remote.
|
||||
* PARAMETERS
|
||||
* p_sess - the current FTP session object
|
||||
* RETURNS
|
||||
* The file descriptor upon success, or -1 upon error.
|
||||
*/
|
||||
int vsf_ftpdataio_get_port_fd(struct vsf_session* p_sess);
|
||||
|
||||
/* vsf_ftpdataio_transfer_file()
|
||||
* PURPOSE
|
||||
* Send data between the network and a local file. Send and receive are
|
||||
* supported, as well as ASCII mangling.
|
||||
* PARAMETERS
|
||||
* remote_fd - the file descriptor of the remote data connection
|
||||
* file_fd - the file descriptor of the local file
|
||||
* is_recv - 0 for sending to the remote, otherwise receive
|
||||
* is_ascii - non zero for ASCII mangling
|
||||
* RETURNS
|
||||
* A structure, containing
|
||||
* retval - 0 for success, failure otherwise
|
||||
* transferred - number of bytes transferred
|
||||
*/
|
||||
struct vsf_transfer_ret
|
||||
{
|
||||
int retval;
|
||||
unsigned long transferred;
|
||||
};
|
||||
struct vsf_transfer_ret vsf_ftpdataio_transfer_file(
|
||||
struct vsf_session* p_sess,
|
||||
int remote_fd, int file_fd, int is_recv, int is_ascii);
|
||||
|
||||
/* vsf_ftpdataio_transfer_dir()
|
||||
* PURPOSE
|
||||
* Send an ASCII directory lising of the requested directory to the remote
|
||||
* client.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* remote_fd - the file descriptor of the remote data connection
|
||||
* p_dir - the local directory object
|
||||
* p_base_dir_str - the directory we opened relative to the current one
|
||||
* p_option_str - the options list provided to "ls"
|
||||
* p_filter_str - the filter string provided to "ls"
|
||||
* is_verbose - set to 0 if NLST used, 1 if LIST used
|
||||
*/
|
||||
int vsf_ftpdataio_transfer_dir(struct vsf_session* p_sess,
|
||||
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);
|
||||
|
||||
#endif /* VSF_FTPDATAIO_H */
|
||||
|
281
logging.c
Normal file
281
logging.c
Normal file
@ -0,0 +1,281 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
*
|
||||
* logging.c
|
||||
*/
|
||||
|
||||
#include "logging.h"
|
||||
#include "tunables.h"
|
||||
#include "utility.h"
|
||||
#include "str.h"
|
||||
#include "sysutil.h"
|
||||
#include "sysstr.h"
|
||||
#include "session.h"
|
||||
|
||||
/* File local functions */
|
||||
static int vsf_log_type_is_transfer(enum EVSFLogEntryType type);
|
||||
static void vsf_log_common(struct vsf_session* p_sess, int succeeded,
|
||||
enum EVSFLogEntryType what,
|
||||
const struct mystr* p_str);
|
||||
static void vsf_log_do_log_vsftpd_format(struct vsf_session* p_sess,
|
||||
struct mystr* p_str, int succeeded,
|
||||
enum EVSFLogEntryType what,
|
||||
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);
|
||||
|
||||
void
|
||||
vsf_log_init(struct vsf_session* p_sess)
|
||||
{
|
||||
int retval;
|
||||
if (!tunable_xferlog_enable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
retval = vsf_sysutil_create_or_open_file(tunable_xferlog_file, 0600);
|
||||
if (vsf_sysutil_retval_is_error(retval))
|
||||
{
|
||||
die("failed to open ftp log file");
|
||||
}
|
||||
p_sess->log_fd = retval;
|
||||
}
|
||||
|
||||
static int
|
||||
vsf_log_type_is_transfer(enum EVSFLogEntryType type)
|
||||
{
|
||||
return (type == kVSFLogEntryDownload || type == kVSFLogEntryUpload);
|
||||
}
|
||||
|
||||
void
|
||||
vsf_log_start_entry(struct vsf_session* p_sess, enum EVSFLogEntryType what)
|
||||
{
|
||||
if (p_sess->log_type != 0)
|
||||
{
|
||||
bug("non null log_type in vsf_log_start_entry");
|
||||
}
|
||||
p_sess->log_type = (unsigned long) what;
|
||||
p_sess->log_start_sec = 0;
|
||||
p_sess->log_start_usec = 0;
|
||||
p_sess->transfer_size = 0;
|
||||
str_empty(&p_sess->log_str);
|
||||
if (vsf_log_type_is_transfer(what))
|
||||
{
|
||||
vsf_sysutil_update_cached_time();
|
||||
p_sess->log_start_sec = vsf_sysutil_get_cached_time_sec();
|
||||
p_sess->log_start_usec = vsf_sysutil_get_cached_time_usec();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vsf_log_line(struct vsf_session* p_sess, enum EVSFLogEntryType what,
|
||||
struct mystr* p_str)
|
||||
{
|
||||
vsf_log_common(p_sess, 1, what, p_str);
|
||||
}
|
||||
|
||||
void
|
||||
vsf_log_do_log(struct vsf_session* p_sess, int succeeded)
|
||||
{
|
||||
vsf_log_common(p_sess, succeeded, (enum EVSFLogEntryType) p_sess->log_type,
|
||||
&p_sess->log_str);
|
||||
p_sess->log_type = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
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)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
retval = vsf_sysutil_lock_file(p_sess->log_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);
|
||||
}
|
||||
|
||||
static void
|
||||
vsf_log_do_log_wuftpd_format(struct vsf_session* p_sess, struct mystr* p_str,
|
||||
int succeeded)
|
||||
{
|
||||
long delta_sec;
|
||||
enum EVSFLogEntryType what = (enum EVSFLogEntryType) p_sess->log_type;
|
||||
/* Date - vsf_sysutil_get_current_date updates cached time */
|
||||
str_alloc_text(p_str, vsf_sysutil_get_current_date());
|
||||
str_append_char(p_str, ' ');
|
||||
/* Transfer time (in seconds) */
|
||||
delta_sec = vsf_sysutil_get_cached_time_sec() - p_sess->log_start_sec;
|
||||
if (delta_sec <= 0)
|
||||
{
|
||||
delta_sec = 1;
|
||||
}
|
||||
str_append_ulong(p_str, (unsigned long) delta_sec);
|
||||
str_append_char(p_str, ' ');
|
||||
/* Remote host name */
|
||||
str_append_str(p_str, &p_sess->remote_ip_str);
|
||||
str_append_char(p_str, ' ');
|
||||
/* Bytes transferred */
|
||||
str_append_ulong(p_str, p_sess->transfer_size);
|
||||
str_append_char(p_str, ' ');
|
||||
/* Filename */
|
||||
str_append_str(p_str, &p_sess->log_str);
|
||||
str_append_char(p_str, ' ');
|
||||
/* Transfer type (ascii/binary) */
|
||||
if (p_sess->is_ascii)
|
||||
{
|
||||
str_append_text(p_str, "a ");
|
||||
}
|
||||
else
|
||||
{
|
||||
str_append_text(p_str, "b ");
|
||||
}
|
||||
/* Special action flag - tar, gzip etc. */
|
||||
str_append_text(p_str, "_ ");
|
||||
/* Direction of transfer */
|
||||
if (what == kVSFLogEntryUpload)
|
||||
{
|
||||
str_append_text(p_str, "i ");
|
||||
}
|
||||
else
|
||||
{
|
||||
str_append_text(p_str, "o ");
|
||||
}
|
||||
/* Access mode: anonymous/real user, and identity */
|
||||
if (p_sess->is_anonymous)
|
||||
{
|
||||
str_append_text(p_str, "a ");
|
||||
str_append_str(p_str, &p_sess->anon_pass_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
str_append_text(p_str, "r ");
|
||||
str_append_str(p_str, &p_sess->user_str);
|
||||
}
|
||||
str_append_char(p_str, ' ');
|
||||
/* Service name, authentication method, authentication user id */
|
||||
str_append_text(p_str, "ftp 0 * ");
|
||||
/* Completion status */
|
||||
if (succeeded)
|
||||
{
|
||||
str_append_char(p_str, 'c');
|
||||
}
|
||||
else
|
||||
{
|
||||
str_append_char(p_str, 'i');
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
vsf_log_do_log_vsftpd_format(struct vsf_session* p_sess, struct mystr* p_str,
|
||||
int succeeded, enum EVSFLogEntryType what,
|
||||
const struct mystr* p_log_str)
|
||||
{
|
||||
/* Date - vsf_sysutil_get_current_date updates cached time */
|
||||
str_alloc_text(p_str, vsf_sysutil_get_current_date());
|
||||
/* Pid */
|
||||
str_append_text(p_str, " [pid ");
|
||||
str_append_ulong(p_str, vsf_sysutil_getpid());
|
||||
str_append_text(p_str, "] ");
|
||||
/* User */
|
||||
if (!str_isempty(&p_sess->user_str))
|
||||
{
|
||||
str_append_char(p_str, '[');
|
||||
str_append_str(p_str, &p_sess->user_str);
|
||||
str_append_text(p_str, "] ");
|
||||
}
|
||||
/* And the action */
|
||||
if (what != kVSFLogEntryFTPInput && what != kVSFLogEntryFTPOutput)
|
||||
{
|
||||
if (succeeded)
|
||||
{
|
||||
str_append_text(p_str, "OK ");
|
||||
}
|
||||
else
|
||||
{
|
||||
str_append_text(p_str, "FAIL ");
|
||||
}
|
||||
}
|
||||
switch (what)
|
||||
{
|
||||
case kVSFLogEntryDownload:
|
||||
str_append_text(p_str, "DOWNLOAD");
|
||||
break;
|
||||
case kVSFLogEntryUpload:
|
||||
str_append_text(p_str, "UPLOAD");
|
||||
break;
|
||||
case kVSFLogEntryMkdir:
|
||||
str_append_text(p_str, "MKDIR");
|
||||
break;
|
||||
case kVSFLogEntryLogin:
|
||||
str_append_text(p_str, "LOGIN");
|
||||
break;
|
||||
case kVSFLogEntryFTPInput:
|
||||
str_append_text(p_str, "FTP command");
|
||||
break;
|
||||
case kVSFLogEntryFTPOutput:
|
||||
str_append_text(p_str, "FTP response");
|
||||
break;
|
||||
default:
|
||||
bug("bad entry_type in vsf_log_do_log");
|
||||
break;
|
||||
}
|
||||
str_append_text(p_str, ": Client \"");
|
||||
str_append_str(p_str, &p_sess->remote_ip_str);
|
||||
str_append_char(p_str, '"');
|
||||
if (what == kVSFLogEntryLogin && !str_isempty(&p_sess->anon_pass_str))
|
||||
{
|
||||
str_append_text(p_str, ", anon password \"");
|
||||
str_append_str(p_str, &p_sess->anon_pass_str);
|
||||
str_append_char(p_str, '"');
|
||||
}
|
||||
if (!str_isempty(p_log_str))
|
||||
{
|
||||
str_append_text(p_str, ", \"");
|
||||
str_append_str(p_str, p_log_str);
|
||||
str_append_char(p_str, '"');
|
||||
}
|
||||
if (what != kVSFLogEntryFTPInput && what != kVSFLogEntryFTPOutput)
|
||||
{
|
||||
if (p_sess->transfer_size)
|
||||
{
|
||||
str_append_text(p_str, ", ");
|
||||
str_append_ulong(p_str, p_sess->transfer_size);
|
||||
str_append_text(p_str, " bytes");
|
||||
}
|
||||
if (vsf_log_type_is_transfer(what))
|
||||
{
|
||||
long delta_sec = vsf_sysutil_get_cached_time_sec() -
|
||||
p_sess->log_start_sec;
|
||||
long delta_usec = vsf_sysutil_get_cached_time_usec() -
|
||||
p_sess->log_start_usec;
|
||||
double time_delta = (double) delta_sec + ((double) delta_usec /
|
||||
(double) 1000000);
|
||||
double kbyte_rate =
|
||||
((double) p_sess->transfer_size / time_delta) / (double) 1024;
|
||||
str_append_text(p_str, ", ");
|
||||
str_append_double(p_str, kbyte_rate);
|
||||
str_append_text(p_str, "Kbyte/sec");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
64
logging.h
Normal file
64
logging.h
Normal file
@ -0,0 +1,64 @@
|
||||
#ifndef VSF_LOGGING_H
|
||||
#define VSF_LOGGING_H
|
||||
|
||||
/* Forward delcarations */
|
||||
struct mystr;
|
||||
struct vsf_session;
|
||||
|
||||
enum EVSFLogEntryType
|
||||
{
|
||||
kVSFLogEntryNull = 1,
|
||||
kVSFLogEntryDownload,
|
||||
kVSFLogEntryUpload,
|
||||
kVSFLogEntryMkdir,
|
||||
kVSFLogEntryLogin,
|
||||
kVSFLogEntryFTPInput,
|
||||
kVSFLogEntryFTPOutput
|
||||
};
|
||||
|
||||
/* vsf_log_init()
|
||||
* PURPOSE
|
||||
* Initialize the logging services, by opening a writable file descriptor to
|
||||
* the log file (should logging be enabled).
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
*/
|
||||
void vsf_log_init(struct vsf_session* p_sess);
|
||||
|
||||
/* vsf_log_start_entry()
|
||||
* PURPOSE
|
||||
* Denote the start of a logged operation. Importantly, timing information
|
||||
* (if applicable) will be taken starting from this call.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* what - the type of operation which just started
|
||||
*/
|
||||
void vsf_log_start_entry(struct vsf_session* p_sess,
|
||||
enum EVSFLogEntryType what);
|
||||
|
||||
/* vsf_log_do_log()
|
||||
* PURPOSE
|
||||
* Denote the end of a logged operation, specifying whether the operation
|
||||
* was successful or not.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* succeeded - 0 for a failed operation, 1 for a successful operation
|
||||
*/
|
||||
void vsf_log_do_log(struct vsf_session* p_sess, int succeeded);
|
||||
|
||||
/* vsf_log_line()
|
||||
* PURPOSE
|
||||
* Logs a single line of information, without disturbing any pending log
|
||||
* operations (e.g. a download log spans a period of time).
|
||||
* This call must be used for any logging calls nested within a call to
|
||||
* the vsf_log_start_entry() function.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* what - the type of operation to log
|
||||
* p_str - the string to log
|
||||
*/
|
||||
void vsf_log_line(struct vsf_session* p_sess, enum EVSFLogEntryType what,
|
||||
struct mystr* p_str);
|
||||
|
||||
#endif /* VSF_LOGGING_H */
|
||||
|
324
ls.c
Normal file
324
ls.c
Normal file
@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* ls.c
|
||||
*
|
||||
* Would you believe, code to handle directory listing.
|
||||
*/
|
||||
|
||||
#include "ls.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);
|
||||
|
||||
void
|
||||
vsf_ls_populate_dir_list(struct mystr_list* p_list,
|
||||
struct mystr_list* p_subdir_list,
|
||||
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)
|
||||
{
|
||||
struct mystr dirline_str = INIT_MYSTR;
|
||||
struct mystr normalised_base_dir_str = INIT_MYSTR;
|
||||
struct str_locate_result loc_result;
|
||||
int a_option;
|
||||
int r_option;
|
||||
int t_option;
|
||||
int do_stat = 0;
|
||||
loc_result = str_locate_char(p_option_str, 'a');
|
||||
a_option = loc_result.found;
|
||||
loc_result = str_locate_char(p_option_str, 'r');
|
||||
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, 'l');
|
||||
if (loc_result.found)
|
||||
{
|
||||
is_verbose = 1;
|
||||
}
|
||||
/* Invert "reverse" arg for "-t", the time sorting */
|
||||
if (t_option)
|
||||
{
|
||||
r_option = !r_option;
|
||||
}
|
||||
if (is_verbose || t_option || p_subdir_list != 0)
|
||||
{
|
||||
do_stat = 1;
|
||||
}
|
||||
/* "Normalise" the incoming base directory string by making sure it
|
||||
* ends in a '/' if it is nonempty
|
||||
*/
|
||||
if (!str_equal_text(p_base_dir_str, "."))
|
||||
{
|
||||
str_copy(&normalised_base_dir_str, p_base_dir_str);
|
||||
}
|
||||
if (!str_isempty(&normalised_base_dir_str))
|
||||
{
|
||||
unsigned int len = str_getlen(&normalised_base_dir_str);
|
||||
if (str_get_char_at(&normalised_base_dir_str, len - 1) != '/')
|
||||
{
|
||||
str_append_char(&normalised_base_dir_str, '/');
|
||||
}
|
||||
}
|
||||
/* If we're going to need to do time comparisions, cache the local time */
|
||||
if (is_verbose)
|
||||
{
|
||||
vsf_sysutil_update_cached_time();
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
static struct mystr s_next_filename_str;
|
||||
static struct mystr s_next_path_and_filename_str;
|
||||
static struct vsf_sysutil_statbuf* s_p_statbuf;
|
||||
str_next_dirent(&s_next_filename_str, p_dir);
|
||||
if (str_isempty(&s_next_filename_str))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!a_option && str_getlen(&s_next_filename_str) > 0 &&
|
||||
str_get_char_at(&s_next_filename_str, 0) == '.')
|
||||
{
|
||||
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))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* Calculate the full path (relative to CWD) for lstat() and
|
||||
* output purposes
|
||||
*/
|
||||
str_copy(&s_next_path_and_filename_str, &normalised_base_dir_str);
|
||||
str_append_str(&s_next_path_and_filename_str, &s_next_filename_str);
|
||||
if (do_stat)
|
||||
{
|
||||
/* lstat() the file. Of course there's a race condition - the
|
||||
* directory entry may have gone away whilst we read it, so
|
||||
* ignore failure to stat
|
||||
*/
|
||||
int retval = str_lstat(&s_next_path_and_filename_str, &s_p_statbuf);
|
||||
if (vsf_sysutil_retval_is_error(retval))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (is_verbose)
|
||||
{
|
||||
static struct mystr s_final_file_str;
|
||||
/* If it's a damn symlink, we need to append the target */
|
||||
str_copy(&s_final_file_str, &s_next_filename_str);
|
||||
if (vsf_sysutil_statbuf_is_symlink(s_p_statbuf))
|
||||
{
|
||||
static struct mystr s_temp_str;
|
||||
int retval = str_readlink(&s_temp_str, &s_next_path_and_filename_str);
|
||||
if (retval == 0 && !str_isempty(&s_temp_str))
|
||||
{
|
||||
str_append_text(&s_final_file_str, " -> ");
|
||||
str_append_str(&s_final_file_str, &s_temp_str);
|
||||
}
|
||||
}
|
||||
build_dir_line(&dirline_str, &s_final_file_str, s_p_statbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Just emit the filenames - note, we prepend the directory for NLST
|
||||
* but not for LIST
|
||||
*/
|
||||
str_copy(&dirline_str, &s_next_path_and_filename_str);
|
||||
str_append_text(&dirline_str, "\r\n");
|
||||
}
|
||||
/* Add filename into our sorted list - sorting by filename or time. Also,
|
||||
* if we are required to, maintain a distinct list of direct
|
||||
* subdirectories.
|
||||
*/
|
||||
{
|
||||
static struct mystr s_temp_str;
|
||||
const struct mystr* p_sort_str = 0;
|
||||
const struct mystr* p_sort_subdir_str = 0;
|
||||
if (!t_option)
|
||||
{
|
||||
p_sort_str = &s_next_filename_str;
|
||||
}
|
||||
else
|
||||
{
|
||||
str_alloc_text(&s_temp_str,
|
||||
vsf_sysutil_statbuf_get_sortkey_mtime(s_p_statbuf));
|
||||
p_sort_str = &s_temp_str;
|
||||
p_sort_subdir_str = &s_temp_str;
|
||||
}
|
||||
str_list_add(p_list, &dirline_str, p_sort_str);
|
||||
if (p_subdir_list != 0 && vsf_sysutil_statbuf_is_dir(s_p_statbuf))
|
||||
{
|
||||
str_list_add(p_subdir_list, &s_next_filename_str, p_sort_subdir_str);
|
||||
}
|
||||
}
|
||||
} /* END: while(1) */
|
||||
str_list_sort(p_list, r_option);
|
||||
if (p_subdir_list != 0)
|
||||
{
|
||||
str_list_sort(p_subdir_list, r_option);
|
||||
}
|
||||
str_free(&dirline_str);
|
||||
str_free(&normalised_base_dir_str);
|
||||
}
|
||||
|
||||
static int
|
||||
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
|
||||
* reluctant to trust the latter. fnmatch(3) involves _lots_ of string
|
||||
* parsing and handling. There is broad potential for any given fnmatch(3)
|
||||
* implementation to be buggy.
|
||||
*
|
||||
* Currently supported pattern(s):
|
||||
* - any number of wildcards, "*"
|
||||
*/
|
||||
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;
|
||||
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);
|
||||
|
||||
while (!str_isempty(&s_filter_remain_str))
|
||||
{
|
||||
static struct mystr s_match_needed_str;
|
||||
/* Locate next wildcard */
|
||||
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 */
|
||||
if (locate_result.found)
|
||||
{
|
||||
unsigned int index = locate_result.index;
|
||||
str_left(&s_filter_remain_str, &s_match_needed_str, index);
|
||||
str_mid_to_end(&s_filter_remain_str, &s_temp_str, index + 1);
|
||||
str_copy(&s_filter_remain_str, &s_temp_str);
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (!str_isempty(&s_match_needed_str))
|
||||
{
|
||||
/* Need to match something.. could be a match which has to start at
|
||||
* current position, or we could allow it to start anywhere
|
||||
*/
|
||||
unsigned int index;
|
||||
locate_result = str_locate_str(&s_name_remain_str, &s_match_needed_str);
|
||||
if (!locate_result.found)
|
||||
{
|
||||
/* Fail */
|
||||
return 0;
|
||||
}
|
||||
index = locate_result.index;
|
||||
if (must_match_at_current_pos && index > 0)
|
||||
{
|
||||
/* Fail */
|
||||
return 0;
|
||||
}
|
||||
/* Chop matched string out of remainder */
|
||||
str_mid_to_end(&s_name_remain_str, &s_temp_str,
|
||||
index + str_getlen(&s_match_needed_str));
|
||||
str_copy(&s_name_remain_str, &s_temp_str);
|
||||
}
|
||||
/* 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)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/* OK, a match */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
build_dir_line(struct mystr* p_str, const struct mystr* p_filename_str,
|
||||
const struct vsf_sysutil_statbuf* p_stat)
|
||||
{
|
||||
static struct mystr s_tmp_str;
|
||||
unsigned long size = vsf_sysutil_statbuf_get_size(p_stat);
|
||||
/* Permissions */
|
||||
str_alloc_text(p_str, vsf_sysutil_statbuf_get_perms(p_stat));
|
||||
str_append_char(p_str, ' ');
|
||||
/* Hard link count */
|
||||
str_alloc_ulong(&s_tmp_str, vsf_sysutil_statbuf_get_links(p_stat));
|
||||
str_lpad(&s_tmp_str, 4);
|
||||
str_append_str(p_str, &s_tmp_str);
|
||||
str_append_char(p_str, ' ');
|
||||
/* User */
|
||||
{
|
||||
int uid = vsf_sysutil_statbuf_get_uid(p_stat);
|
||||
struct vsf_sysutil_user* p_user = 0;
|
||||
if (tunable_text_userdb_names)
|
||||
{
|
||||
p_user = vsf_sysutil_getpwuid(uid);
|
||||
}
|
||||
if (p_user == 0)
|
||||
{
|
||||
str_alloc_ulong(&s_tmp_str, (unsigned long) uid);
|
||||
}
|
||||
else
|
||||
{
|
||||
str_alloc_text(&s_tmp_str, vsf_sysutil_user_getname(p_user));
|
||||
}
|
||||
}
|
||||
str_rpad(&s_tmp_str, 8);
|
||||
str_append_str(p_str, &s_tmp_str);
|
||||
str_append_char(p_str, ' ');
|
||||
/* Group */
|
||||
{
|
||||
int gid = vsf_sysutil_statbuf_get_gid(p_stat);
|
||||
struct vsf_sysutil_group* p_group = 0;
|
||||
if (tunable_text_userdb_names)
|
||||
{
|
||||
p_group = vsf_sysutil_getgrgid(gid);
|
||||
}
|
||||
if (p_group == 0)
|
||||
{
|
||||
str_alloc_ulong(&s_tmp_str, (unsigned long) gid);
|
||||
}
|
||||
else
|
||||
{
|
||||
str_alloc_text(&s_tmp_str, vsf_sysutil_group_getname(p_group));
|
||||
}
|
||||
}
|
||||
str_rpad(&s_tmp_str, 8);
|
||||
str_append_str(p_str, &s_tmp_str);
|
||||
str_append_char(p_str, ' ');
|
||||
/* Size in bytes */
|
||||
str_alloc_ulong(&s_tmp_str, size);
|
||||
str_lpad(&s_tmp_str, 8);
|
||||
str_append_str(p_str, &s_tmp_str);
|
||||
str_append_char(p_str, ' ');
|
||||
/* Date stamp */
|
||||
str_append_text(p_str, vsf_sysutil_statbuf_get_date(p_stat));
|
||||
str_append_char(p_str, ' ');
|
||||
/* Filename */
|
||||
str_append_str(p_str, p_filename_str);
|
||||
str_append_text(p_str, "\r\n");
|
||||
}
|
||||
|
31
ls.h
Normal file
31
ls.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef VSF_LS_H
|
||||
#define VSF_LS_H
|
||||
|
||||
struct mystr;
|
||||
struct mystr_list;
|
||||
struct vsf_sysutil_dir;
|
||||
|
||||
/* vsf_ls_populate_dir_list()
|
||||
* PURPOSE
|
||||
* Given a directory handle, populate a formatted directory entry list (/bin/ls
|
||||
* format). Also optionally populate a list of subdirectories.
|
||||
* PARAMETERS
|
||||
* p_list - the string list object for the result list of entries
|
||||
* p_subdir_list - the string list object for the result list of
|
||||
* subdirectories. May be 0 if client is not interested.
|
||||
* p_dir - the directory object to be listed
|
||||
* p_base_dir_str - the directory name we are listing, relative to current
|
||||
* p_option_str - the string of options given to the LIST/NLST command
|
||||
* p_filter_str - the filter string given to LIST/NLST - e.g. "*.mp3"
|
||||
* is_verbose - set to 1 for LIST, 0 for NLST
|
||||
*/
|
||||
void vsf_ls_populate_dir_list(struct mystr_list* p_list,
|
||||
struct mystr_list* p_subdir_list,
|
||||
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);
|
||||
|
||||
#endif /* VSF_LS_H */
|
||||
|
229
main.c
Normal file
229
main.c
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* main.c
|
||||
*/
|
||||
|
||||
#include "session.h"
|
||||
#include "utility.h"
|
||||
#include "tunables.h"
|
||||
#include "logging.h"
|
||||
#include "str.h"
|
||||
#include "filestr.h"
|
||||
#include "ftpcmdio.h"
|
||||
#include "sysutil.h"
|
||||
#include "sysdeputil.h"
|
||||
#include "defs.h"
|
||||
#include "parseconf.h"
|
||||
#include "oneprocess.h"
|
||||
#include "twoprocess.h"
|
||||
|
||||
/*
|
||||
* Forward decls of helper functions
|
||||
*/
|
||||
static void die_unless_privileged(void);
|
||||
static void do_sanity_checks(void);
|
||||
static void session_init(struct vsf_session* p_sess);
|
||||
static void env_init(void);
|
||||
|
||||
int
|
||||
main(int argc, const char* argv[])
|
||||
{
|
||||
struct vsf_session the_session =
|
||||
{
|
||||
/* Control connection */
|
||||
0, 0,
|
||||
/* Data connection */
|
||||
-1, 0, -1, 0, 0, 0, 0,
|
||||
/* Login */
|
||||
1, INIT_MYSTR, INIT_MYSTR,
|
||||
/* Protocol state */
|
||||
0, 1, INIT_MYSTR, 0,
|
||||
/* Session state */
|
||||
0,
|
||||
/* Userids */
|
||||
-1, -1,
|
||||
/* Pre-chroot() cache */
|
||||
INIT_MYSTR, INIT_MYSTR,
|
||||
/* Logging */
|
||||
-1, INIT_MYSTR, 0, 0, 0, INIT_MYSTR, 0,
|
||||
/* Buffers */
|
||||
INIT_MYSTR, INIT_MYSTR,
|
||||
/* Parent <-> child comms */
|
||||
0, -1, -1
|
||||
};
|
||||
int config_specified = 0;
|
||||
const char* p_config_name = VSFTP_DEFAULT_CONFIG;
|
||||
/* Zero or one argument supported. If one argument is passed, it is the
|
||||
* path to the config file
|
||||
*/
|
||||
if (argc > 2)
|
||||
{
|
||||
die("vsftpd: too many arguments (I take an optional config file only)");
|
||||
}
|
||||
else if (argc == 0)
|
||||
{
|
||||
die("vsftpd: missing argv[0]");
|
||||
}
|
||||
if (argc == 2)
|
||||
{
|
||||
p_config_name = argv[1];
|
||||
config_specified = 1;
|
||||
}
|
||||
/* This might need to open /dev/zero on systems lacking MAP_ANON. Needs
|
||||
* to be done early (i.e. before config file parse, which may use
|
||||
* anonymous pages
|
||||
*/
|
||||
vsf_sysutil_map_anon_pages_init();
|
||||
/* Parse config file if it's there */
|
||||
{
|
||||
struct vsf_sysutil_statbuf* p_statbuf = 0;
|
||||
int retval = vsf_sysutil_stat(p_config_name, &p_statbuf);
|
||||
if (!vsf_sysutil_retval_is_error(retval))
|
||||
{
|
||||
vsf_parseconf_load_file(p_config_name);
|
||||
}
|
||||
else if (config_specified)
|
||||
{
|
||||
die("vsftpd: cannot open specified config file");
|
||||
}
|
||||
vsf_sysutil_free(p_statbuf);
|
||||
}
|
||||
/* Sanity checks - exit with a graceful error message if our STDIN is not
|
||||
* a socket. Also check various config options don't collide.
|
||||
*/
|
||||
do_sanity_checks();
|
||||
/* Just get out unless we start with requisite privilege */
|
||||
die_unless_privileged();
|
||||
/* Initializes session globals - e.g. IP addr's etc. */
|
||||
session_init(&the_session);
|
||||
/* Set up "environment", e.g. process group etc. */
|
||||
env_init();
|
||||
/* Set up logging - must come after global init because we need the remote
|
||||
* address to convert into text
|
||||
*/
|
||||
vsf_log_init(&the_session);
|
||||
str_alloc_text(&the_session.remote_ip_str,
|
||||
vsf_sysutil_inet_ntoa(the_session.p_remote_addr));
|
||||
/* Set up options on the command socket */
|
||||
vsf_cmdio_sock_setup();
|
||||
if (tunable_setproctitle_enable)
|
||||
{
|
||||
/* Warning -- warning -- may nuke argv, environ */
|
||||
vsf_sysutil_setproctitle_init(argc, argv);
|
||||
vsf_sysutil_set_proctitle_prefix(&the_session.remote_ip_str);
|
||||
vsf_sysutil_setproctitle("connected");
|
||||
}
|
||||
/* We might chroot() very soon (one process model), so we need to open
|
||||
* any required config files here.
|
||||
*/
|
||||
if (tunable_deny_email_enable)
|
||||
{
|
||||
int retval = str_fileread(&the_session.banned_email_str,
|
||||
tunable_banned_email_file, VSFTP_CONF_FILE_MAX);
|
||||
if (vsf_sysutil_retval_is_error(retval))
|
||||
{
|
||||
die("cannot open banned e-mail list file");
|
||||
}
|
||||
}
|
||||
/* Special case - can force one process model if we've got a setup
|
||||
* needing _no_ privs
|
||||
*/
|
||||
if (!tunable_local_enable && !tunable_connect_from_port_20 &&
|
||||
!tunable_chown_uploads)
|
||||
{
|
||||
tunable_one_process_model = 1;
|
||||
}
|
||||
if (tunable_one_process_model)
|
||||
{
|
||||
vsf_one_process_start(&the_session);
|
||||
}
|
||||
else
|
||||
{
|
||||
vsf_two_process_start(&the_session);
|
||||
}
|
||||
/* NOTREACHED */
|
||||
bug("should not get here: main");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
die_unless_privileged(void)
|
||||
{
|
||||
if (!vsf_sysutil_running_as_root())
|
||||
{
|
||||
die("vsftpd: must be started as root");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_sanity_checks(void)
|
||||
{
|
||||
{
|
||||
struct vsf_sysutil_statbuf* p_statbuf = 0;
|
||||
vsf_sysutil_fstat(VSFTP_COMMAND_FD, &p_statbuf);
|
||||
if (!vsf_sysutil_statbuf_is_socket(p_statbuf))
|
||||
{
|
||||
die("vsftpd: does not run standalone, must be started from inetd");
|
||||
}
|
||||
vsf_sysutil_free(p_statbuf);
|
||||
}
|
||||
if (tunable_one_process_model)
|
||||
{
|
||||
if (tunable_local_enable)
|
||||
{
|
||||
die("vsftpd: security: 'tunable_one_process_model' is anonymous only");
|
||||
}
|
||||
if (!vsf_sysdep_has_capabilities_as_non_root())
|
||||
{
|
||||
die("vsftpd: security: 'tunable_one_process_model' needs a better OS");
|
||||
}
|
||||
}
|
||||
if (!tunable_local_enable && !tunable_anonymous_enable)
|
||||
{
|
||||
die("vsftpd: both local and anonymous access disabled!");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
env_init(void)
|
||||
{
|
||||
vsf_sysutil_make_session_leader();
|
||||
/* Set up a secure umask - we'll set the proper one after login */
|
||||
vsf_sysutil_set_umask(VSFTP_SECURE_UMASK);
|
||||
/* Fire up libc's timezone initialisation, before we chroot()! */
|
||||
vsf_sysutil_tzset();
|
||||
/* Signals. We'll always take -EPIPE rather than a rude signal, thanks */
|
||||
vsf_sysutil_install_null_sighandler(kVSFSysUtilSigPIPE);
|
||||
}
|
||||
|
||||
static void
|
||||
session_init(struct vsf_session* p_sess)
|
||||
{
|
||||
/* Get the addresses of the control connection */
|
||||
vsf_sysutil_getpeername(VSFTP_COMMAND_FD, &p_sess->p_remote_addr);
|
||||
vsf_sysutil_getsockname(VSFTP_COMMAND_FD, &p_sess->p_local_addr);
|
||||
/* If anonymous mode is active, fetch the uid of the anonymous user */
|
||||
if (tunable_anonymous_enable)
|
||||
{
|
||||
const struct vsf_sysutil_user* p_user =
|
||||
vsf_sysutil_getpwnam(tunable_ftp_username);
|
||||
if (p_user == 0)
|
||||
{
|
||||
die("vsftpd: cannot locate user specified in 'tunable_ftp_username'");
|
||||
}
|
||||
p_sess->anon_ftp_uid = vsf_sysutil_user_getuid(p_user);
|
||||
|
||||
if (tunable_chown_uploads)
|
||||
{
|
||||
p_user = vsf_sysutil_getpwnam(tunable_chown_username);
|
||||
if (p_user == 0)
|
||||
{
|
||||
die("vsf_sysutil_getpwnam");
|
||||
}
|
||||
p_sess->anon_upload_chown_uid = vsf_sysutil_user_getuid(p_user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
125
netstr.c
Normal file
125
netstr.c
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* netstr.c
|
||||
*
|
||||
* The netstr interface extends the standard string interface, adding
|
||||
* functions which can cope safely with building strings from the network,
|
||||
* and send them out too.
|
||||
*/
|
||||
|
||||
#include "netstr.h"
|
||||
#include "str.h"
|
||||
#include "sysstr.h"
|
||||
#include "utility.h"
|
||||
#include "sysutil.h"
|
||||
|
||||
static int str_netfd_write_common(const struct mystr* p_str, int fd,
|
||||
int noblock);
|
||||
|
||||
void
|
||||
str_netfd_alloc(struct mystr* p_str, int fd, char term, char* p_readbuf,
|
||||
unsigned int maxlen)
|
||||
{
|
||||
int retval;
|
||||
unsigned int bytes_read;
|
||||
unsigned int i;
|
||||
char* p_readpos = p_readbuf;
|
||||
unsigned int left = maxlen;
|
||||
while (1)
|
||||
{
|
||||
if (p_readpos + left != p_readbuf + maxlen)
|
||||
{
|
||||
bug("poor buffer accounting in str_netfd_alloc");
|
||||
}
|
||||
/* Did we hit the max? */
|
||||
if (left == 0)
|
||||
{
|
||||
str_empty(p_str);
|
||||
return;
|
||||
}
|
||||
retval = vsf_sysutil_recv_peek(fd, p_readpos, left);
|
||||
if (vsf_sysutil_retval_is_error(retval))
|
||||
{
|
||||
die("vsf_sysutil_recv_peek");
|
||||
}
|
||||
else if (retval == 0)
|
||||
{
|
||||
die("vsf_sysutil_recv_peek: no data");
|
||||
}
|
||||
bytes_read = (unsigned int) retval;
|
||||
/* Search for the terminator */
|
||||
for (i=0; i < bytes_read; i++)
|
||||
{
|
||||
if (p_readpos[i] == term)
|
||||
{
|
||||
/* Got it! */
|
||||
retval = vsf_sysutil_read_loop(fd, p_readpos, i + 1);
|
||||
if (vsf_sysutil_retval_is_error(retval) ||
|
||||
(unsigned int) retval != i + 1)
|
||||
{
|
||||
die("vsf_sysutil_read_loop");
|
||||
}
|
||||
if (p_readpos[i] != term)
|
||||
{
|
||||
die("missing terminator in str_netfd_alloc");
|
||||
}
|
||||
str_alloc_alt_term(p_str, p_readbuf, term);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Not found in this read chunk, so consume the data and re-loop */
|
||||
if (bytes_read > left)
|
||||
{
|
||||
bug("bytes_read > left in str_netfd_alloc");
|
||||
}
|
||||
left -= bytes_read;
|
||||
retval = vsf_sysutil_read_loop(fd, p_readpos, bytes_read);
|
||||
if (vsf_sysutil_retval_is_error(retval) ||
|
||||
(unsigned int) retval != bytes_read)
|
||||
{
|
||||
die("vsf_sysutil_read_loop");
|
||||
}
|
||||
p_readpos += bytes_read;
|
||||
} /* END: while(1) */
|
||||
}
|
||||
|
||||
static int
|
||||
str_netfd_write_common(const struct mystr* p_str, int fd, int noblock)
|
||||
{
|
||||
int ret = 0;
|
||||
int retval;
|
||||
unsigned int str_len = str_getlen(p_str);
|
||||
if (str_len == 0)
|
||||
{
|
||||
bug("zero str_len in str_netfd_write_common");
|
||||
}
|
||||
if (noblock)
|
||||
{
|
||||
vsf_sysutil_activate_noblock(fd);
|
||||
}
|
||||
retval = str_write_loop(p_str, fd);
|
||||
if (vsf_sysutil_retval_is_error(retval) || (unsigned int) retval != str_len)
|
||||
{
|
||||
ret = -1;
|
||||
}
|
||||
if (noblock)
|
||||
{
|
||||
vsf_sysutil_deactivate_noblock(fd);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
str_netfd_write(const struct mystr* p_str, int fd)
|
||||
{
|
||||
return str_netfd_write_common(p_str, fd, 0);
|
||||
}
|
||||
|
||||
int
|
||||
str_netfd_write_noblock(const struct mystr* p_str, int fd)
|
||||
{
|
||||
return str_netfd_write_common(p_str, fd, 1);
|
||||
}
|
||||
|
52
netstr.h
Normal file
52
netstr.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef VSFTP_NETSTR_H
|
||||
#define VSFTP_NETSTR_H
|
||||
|
||||
struct mystr;
|
||||
|
||||
/* str_netfd_alloc()
|
||||
* PURPOSE
|
||||
* Read a string from a network socket into a string buffer object. The string
|
||||
* is delimited by a specified string terminator character.
|
||||
* If any network related errors occur trying to read the string, this call
|
||||
* will exit the program.
|
||||
* This method avoids reading one character at a time from the network.
|
||||
* PARAMETERS
|
||||
* p_str - the destination string object
|
||||
* fd - the file descriptor of the remote network socket
|
||||
* term - the character which will terminate the string. This character
|
||||
* is included in the returned string.
|
||||
* p_readbuf - pointer to a scratch buffer into which to read from the
|
||||
* network. This buffer must be at least "maxlen" characters!
|
||||
* maxlen - maximum length of string to return. If this limit is passed,
|
||||
* an empty string will be returned.
|
||||
*/
|
||||
void str_netfd_alloc(struct mystr* p_str, int fd, char term,
|
||||
char* p_readbuf, unsigned int maxlen);
|
||||
|
||||
/* str_netfd_write()
|
||||
* PURPOSE
|
||||
* Write the contents of a string buffer object out to a network file
|
||||
* descriptor. Failure will cause this call to exit the program.
|
||||
* PARAMETERS
|
||||
* p_str - the string object to send
|
||||
* fd - the file descriptor of the remote network socket
|
||||
* RETURNS
|
||||
* 0 on success, -1 on failure
|
||||
*/
|
||||
int str_netfd_write(const struct mystr* p_str, int fd);
|
||||
|
||||
/* str_netfd_write_noblock()
|
||||
* PURPOSE
|
||||
* Write the contents of a string buffer object out to a network file
|
||||
* descriptor. This call will NOT BLOCK. Furthermore, any errors encountered
|
||||
* will be ignored.
|
||||
* PARAMETERS
|
||||
* p_str - the string object to send
|
||||
* fd - the file descriptor of the remote network socket
|
||||
* RETURNS
|
||||
* 0 on success, -1 on failure
|
||||
*/
|
||||
int str_netfd_write_noblock(const struct mystr* p_str, int fd);
|
||||
|
||||
#endif /* VSFTP_NETSTR_H */
|
||||
|
78
oneprocess.c
Normal file
78
oneprocess.c
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* oneprocess.c
|
||||
*
|
||||
* Code for the "one process" security model. The one process security model
|
||||
* is born for the purposes of raw speed at the expense of compromising the
|
||||
* purity of the security model.
|
||||
* The one process model will typically be disabled, for security reasons.
|
||||
* Only sites with huge numbers of concurrent users are likely to feel the
|
||||
* pain of two processes per session.
|
||||
*/
|
||||
|
||||
#include "prelogin.h"
|
||||
#include "postlogin.h"
|
||||
#include "privops.h"
|
||||
#include "session.h"
|
||||
#include "secutil.h"
|
||||
#include "str.h"
|
||||
#include "tunables.h"
|
||||
#include "utility.h"
|
||||
#include "sysdeputil.h"
|
||||
|
||||
void
|
||||
vsf_one_process_start(struct vsf_session* p_sess)
|
||||
{
|
||||
unsigned int caps = 0;
|
||||
if (tunable_chown_uploads)
|
||||
{
|
||||
caps |= kCapabilityCAP_CHOWN;
|
||||
}
|
||||
if (tunable_connect_from_port_20)
|
||||
{
|
||||
caps |= kCapabilityCAP_NET_BIND_SERVICE;
|
||||
}
|
||||
{
|
||||
struct mystr user_name = INIT_MYSTR;
|
||||
str_alloc_text(&user_name, tunable_ftp_username);
|
||||
vsf_secutil_change_credentials(&user_name, 0, 1, 1, caps);
|
||||
str_free(&user_name);
|
||||
}
|
||||
init_connection(p_sess);
|
||||
}
|
||||
|
||||
void
|
||||
vsf_one_process_login(struct vsf_session* p_sess,
|
||||
const struct mystr* p_pass_str)
|
||||
{
|
||||
enum EVSFPrivopLoginResult login_result =
|
||||
vsf_privop_do_login(p_sess, p_pass_str);
|
||||
switch (login_result)
|
||||
{
|
||||
case kVSFLoginFail:
|
||||
return;
|
||||
break;
|
||||
case kVSFLoginAnon:
|
||||
p_sess->is_anonymous = 1;
|
||||
process_post_login(p_sess);
|
||||
break;
|
||||
default:
|
||||
bug("bad state in vsf_one_process_login");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
vsf_one_process_get_priv_data_sock(struct vsf_session* p_sess)
|
||||
{
|
||||
return vsf_privop_get_ftp_port_sock(p_sess);
|
||||
}
|
||||
|
||||
void
|
||||
vsf_one_process_chown_upload(struct vsf_session* p_sess, int fd)
|
||||
{
|
||||
vsf_privop_do_file_chown(p_sess, fd);
|
||||
}
|
||||
|
47
oneprocess.h
Normal file
47
oneprocess.h
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef VSF_ONEPROCESS_H
|
||||
#define VSF_ONEPROCESS_H
|
||||
|
||||
struct mystr;
|
||||
struct vsf_session;
|
||||
|
||||
/* vsf_one_process_start()
|
||||
* PURPOSE
|
||||
* Called to start FTP login processing using the one process model. Before
|
||||
* processing starts, all possible privileges are dropped.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
*/
|
||||
void vsf_one_process_start(struct vsf_session* p_sess);
|
||||
|
||||
/* vsf_one_process_login()
|
||||
* PURPOSE
|
||||
* Called to propose a login using the one process model. Only anonymous
|
||||
* logins supported!
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* p_pass_str - the proposed password
|
||||
*/
|
||||
void vsf_one_process_login(struct vsf_session* p_sess,
|
||||
const struct mystr* p_pass_str);
|
||||
|
||||
/* vsf_one_process_get_priv_data_sock()
|
||||
* PURPOSE
|
||||
* Get a privileged port 20 bound data socket using the one process model.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* RETURNS
|
||||
* The file descriptor of the privileged socket
|
||||
*/
|
||||
int vsf_one_process_get_priv_data_sock(struct vsf_session* p_sess);
|
||||
|
||||
/* vsf_one_process_chown_upload()
|
||||
* PURPOSE
|
||||
* Change ownership of an uploaded file using the one process model.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* fd - the file descriptor to change ownership on
|
||||
*/
|
||||
void vsf_one_process_chown_upload(struct vsf_session* p_sess, int fd);
|
||||
|
||||
#endif /* VSF_ONEPROCESS_H */
|
||||
|
211
parseconf.c
Normal file
211
parseconf.c
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* parseconf.c
|
||||
*
|
||||
* Routines and support to load in a file full of tunable variables and
|
||||
* settings, populating corresponding runtime variables.
|
||||
*/
|
||||
|
||||
#include "parseconf.h"
|
||||
#include "tunables.h"
|
||||
#include "str.h"
|
||||
#include "filestr.h"
|
||||
#include "defs.h"
|
||||
#include "sysutil.h"
|
||||
#include "utility.h"
|
||||
|
||||
/* File local functions */
|
||||
static void handle_config_setting(struct mystr* p_setting_str,
|
||||
struct mystr* p_value_str);
|
||||
|
||||
/* Tables mapping setting names to runtime variables */
|
||||
/* Boolean settings */
|
||||
static struct parseconf_bool_setting
|
||||
{
|
||||
const char* p_setting_name;
|
||||
int* p_variable;
|
||||
}
|
||||
parseconf_bool_array[] =
|
||||
{
|
||||
{ "anonymous_enable", &tunable_anonymous_enable },
|
||||
{ "local_enable", &tunable_local_enable },
|
||||
{ "pasv_enable", &tunable_pasv_enable },
|
||||
{ "port_enable", &tunable_port_enable },
|
||||
{ "chroot_local_user", &tunable_chroot_local_user },
|
||||
{ "write_enable", &tunable_write_enable },
|
||||
{ "anon_upload_enable", &tunable_anon_upload_enable },
|
||||
{ "anon_mkdir_write_enable", &tunable_anon_mkdir_write_enable },
|
||||
{ "anon_other_write_enable", &tunable_anon_other_write_enable },
|
||||
{ "chown_uploads", &tunable_chown_uploads },
|
||||
{ "connect_from_port_20", &tunable_connect_from_port_20 },
|
||||
{ "xferlog_enable", &tunable_xferlog_enable },
|
||||
{ "dirmessage_enable", &tunable_dirmessage_enable },
|
||||
{ "anon_world_readable_only", &tunable_anon_world_readable_only },
|
||||
{ "async_abor_enable", &tunable_async_abor_enable },
|
||||
{ "ascii_upload_enable", &tunable_ascii_upload_enable },
|
||||
{ "ascii_download_enable", &tunable_ascii_download_enable },
|
||||
{ "one_process_model", &tunable_one_process_model },
|
||||
{ "xferlog_std_format", &tunable_xferlog_std_format },
|
||||
{ "pasv_promiscuous", &tunable_pasv_promiscuous },
|
||||
{ "deny_email_enable", &tunable_deny_email_enable },
|
||||
{ "chroot_list_enable", &tunable_chroot_list_enable },
|
||||
{ "setproctitle_enable", &tunable_setproctitle_enable },
|
||||
{ "text_userdb_names", &tunable_text_userdb_names },
|
||||
{ "ls_recurse_enable", &tunable_ls_recurse_enable },
|
||||
{ "log_ftp_protocol", &tunable_log_ftp_protocol },
|
||||
{ "guest_enable", &tunable_guest_enable },
|
||||
{ "userlist_enable", &tunable_userlist_enable },
|
||||
{ "userlist_deny", &tunable_userlist_deny },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static struct parseconf_uint_setting
|
||||
{
|
||||
const char* p_setting_name;
|
||||
unsigned int* p_variable;
|
||||
}
|
||||
parseconf_uint_array[] =
|
||||
{
|
||||
{ "accept_timeout", &tunable_accept_timeout },
|
||||
{ "connect_timeout", &tunable_connect_timeout },
|
||||
{ "local_umask", &tunable_local_umask },
|
||||
{ "anon_umask", &tunable_anon_umask },
|
||||
{ "ftp_data_port", &tunable_ftp_data_port },
|
||||
{ "idle_session_timeout", &tunable_idle_session_timeout },
|
||||
{ "data_connection_timeout", &tunable_data_connection_timeout },
|
||||
{ "pasv_min_port", &tunable_pasv_min_port },
|
||||
{ "pasv_max_port", &tunable_pasv_max_port },
|
||||
{ "anon_max_rate", &tunable_anon_max_rate },
|
||||
{ "local_max_rate", &tunable_local_max_rate },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static struct parseconf_str_setting
|
||||
{
|
||||
const char* p_setting_name;
|
||||
const char** p_variable;
|
||||
}
|
||||
parseconf_str_array[] =
|
||||
{
|
||||
{ "secure_chroot_dir", &tunable_secure_chroot_dir },
|
||||
{ "ftp_username", &tunable_ftp_username },
|
||||
{ "chown_username", &tunable_chown_username },
|
||||
{ "xferlog_file", &tunable_xferlog_file },
|
||||
{ "message_file", &tunable_message_file },
|
||||
{ "nopriv_user", &tunable_nopriv_user },
|
||||
{ "ftpd_banner", &tunable_ftpd_banner },
|
||||
{ "banned_email_file", &tunable_banned_email_file },
|
||||
{ "chroot_list_file", &tunable_chroot_list_file },
|
||||
{ "pam_service_name", &tunable_pam_service_name },
|
||||
{ "guest_username", &tunable_guest_username },
|
||||
{ "userlist_file", &tunable_userlist_file },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
void
|
||||
vsf_parseconf_load_file(const char* p_filename)
|
||||
{
|
||||
struct mystr config_file_str = INIT_MYSTR;
|
||||
struct mystr config_setting_str = INIT_MYSTR;
|
||||
struct mystr config_value_str = INIT_MYSTR;
|
||||
unsigned int str_pos = 0;
|
||||
int retval = str_fileread(&config_file_str, p_filename, VSFTP_CONF_FILE_MAX);
|
||||
if (vsf_sysutil_retval_is_error(retval))
|
||||
{
|
||||
die("cannot open config file");
|
||||
}
|
||||
while (str_getline(&config_file_str, &config_setting_str, &str_pos))
|
||||
{
|
||||
if (str_isempty(&config_setting_str) ||
|
||||
str_get_char_at(&config_setting_str, 0) == '#')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
/* Split into name=value pair */
|
||||
str_split_char(&config_setting_str, &config_value_str, '=');
|
||||
handle_config_setting(&config_setting_str, &config_value_str);
|
||||
}
|
||||
str_free(&config_file_str);
|
||||
str_free(&config_setting_str);
|
||||
str_free(&config_value_str);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_config_setting(struct mystr* p_setting_str, struct mystr* p_value_str)
|
||||
{
|
||||
if (str_isempty(p_value_str))
|
||||
{
|
||||
die("missing value in config file");
|
||||
}
|
||||
/* Is it a boolean value? */
|
||||
{
|
||||
const struct parseconf_bool_setting* p_bool_setting = parseconf_bool_array;
|
||||
while (p_bool_setting->p_setting_name != 0)
|
||||
{
|
||||
if (str_equal_text(p_setting_str, p_bool_setting->p_setting_name))
|
||||
{
|
||||
/* Got it */
|
||||
str_upper(p_value_str);
|
||||
if (str_equal_text(p_value_str, "YES") ||
|
||||
str_equal_text(p_value_str, "TRUE") ||
|
||||
str_equal_text(p_value_str, "1"))
|
||||
{
|
||||
*(p_bool_setting->p_variable) = 1;
|
||||
}
|
||||
else if (str_equal_text(p_value_str, "NO") ||
|
||||
str_equal_text(p_value_str, "FALSE") ||
|
||||
str_equal_text(p_value_str, "0"))
|
||||
{
|
||||
*(p_bool_setting->p_variable) = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
die("bad bool value in config file");
|
||||
}
|
||||
return;
|
||||
}
|
||||
p_bool_setting++;
|
||||
}
|
||||
}
|
||||
/* Is it an unsigned integer setting? */
|
||||
{
|
||||
const struct parseconf_uint_setting* p_uint_setting = parseconf_uint_array;
|
||||
while (p_uint_setting->p_setting_name != 0)
|
||||
{
|
||||
if (str_equal_text(p_setting_str, p_uint_setting->p_setting_name))
|
||||
{
|
||||
/* Got it */
|
||||
/* If the value starts with 0, assume it's an octal value */
|
||||
if (!str_isempty(p_value_str) &&
|
||||
str_get_char_at(p_value_str, 0) == '0')
|
||||
{
|
||||
*(p_uint_setting->p_variable) = str_octal_to_uint(p_value_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
*(p_uint_setting->p_variable) = str_atoi(p_value_str);
|
||||
}
|
||||
return;
|
||||
}
|
||||
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 */
|
||||
*(p_str_setting->p_variable) = str_strdup(p_value_str);
|
||||
return;
|
||||
}
|
||||
p_str_setting++;
|
||||
}
|
||||
}
|
||||
die("unrecognised variable in config file");
|
||||
}
|
||||
|
17
parseconf.h
Normal file
17
parseconf.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef VSF_PARSECONF_H
|
||||
#define VSF_PARSECONF_H
|
||||
|
||||
/* vsf_parseconf_load_file()
|
||||
* PURPOSE
|
||||
* Parse the given file as a vsftpd config file. If the file cannot be
|
||||
* opened for whatever reason, a fatal error is raised. If the file contains
|
||||
* any syntax errors, a fatal error is raised.
|
||||
* If the call returns (no fatal error raised), then the config file was
|
||||
* parsed and the global config settings will have been updated.
|
||||
* PARAMETERS
|
||||
* p_filename - the name of the config file to parse
|
||||
*/
|
||||
void vsf_parseconf_load_file(const char* p_filename);
|
||||
|
||||
#endif /* VSF_PARSECONF_H */
|
||||
|
23
port/cmsg_extras.h
Normal file
23
port/cmsg_extras.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef VSF_CMSG_EXTRAS_H
|
||||
#define VSF_CMSG_EXTRAS_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
/* These are from Linux glibc-2.2 */
|
||||
#ifndef CMSG_ALIGN
|
||||
#define CMSG_ALIGN(len) (((len) + sizeof (size_t) - 1) \
|
||||
& ~(sizeof (size_t) - 1))
|
||||
#endif
|
||||
|
||||
#ifndef CMSG_SPACE
|
||||
#define CMSG_SPACE(len) (CMSG_ALIGN (len) \
|
||||
+ CMSG_ALIGN (sizeof (struct cmsghdr)))
|
||||
#endif
|
||||
|
||||
#ifndef CMSG_LEN
|
||||
#define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len))
|
||||
#endif
|
||||
|
||||
#endif /* VSF_CMSG_EXTRAS_H */
|
||||
|
7
port/dirfd_extras.h
Normal file
7
port/dirfd_extras.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef VSF_DIRFD_EXTRAS_H
|
||||
#define VSF_DIRFD_EXTRAS_H
|
||||
|
||||
#define dirfd(x) ((x)->dd_fd)
|
||||
|
||||
#endif /* VSF_DIRFD_EXTRAS_H */
|
||||
|
23
port/hpux_bogons.h
Normal file
23
port/hpux_bogons.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef VSF_HPUX_BOGONS_H
|
||||
#define VSF_HPUX_BOGONS_H
|
||||
|
||||
#include <sys/mman.h>
|
||||
/* HP-UX has MAP_ANONYMOUS but not MAP_ANON - I'm not sure which is more
|
||||
* standard!
|
||||
*/
|
||||
#ifdef MAP_ANONYMOUS
|
||||
#ifndef MAP_ANON
|
||||
#define MAP_ANON MAP_ANONYMOUS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Ancient versions of HP-UX don't have MAP_FAILED */
|
||||
#ifndef MAP_FAILED
|
||||
#define MAP_FAILED (void *) -1L
|
||||
#endif
|
||||
|
||||
/* Need dirfd() */
|
||||
#include "dirfd_extras.h"
|
||||
|
||||
#endif /* VSF_HPUX_BOGONS_H */
|
||||
|
8
port/irix_bogons.h
Normal file
8
port/irix_bogons.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef VSF_IRIX_BOGONS_H
|
||||
#define VSF_IRIX_BOGONS_H
|
||||
|
||||
/* Need dirfd() */
|
||||
#include "dirfd_extras.h"
|
||||
|
||||
#endif /* VSF_IRIX_BOGONS_H */
|
||||
|
22
port/porting_junk.h
Normal file
22
port/porting_junk.h
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef VSF_PORTINGJUNK_H
|
||||
#define VSF_PORTINGJUNK_H
|
||||
|
||||
#ifdef __sun
|
||||
#include "solaris_bogons.h"
|
||||
#endif
|
||||
|
||||
#ifdef __sgi
|
||||
#include "irix_bogons.h"
|
||||
#endif
|
||||
|
||||
#ifdef __hpux
|
||||
#include "hpux_bogons.h"
|
||||
#endif
|
||||
|
||||
/* So many older systems lack these, that it's too much hassle to list all
|
||||
* the errant systems
|
||||
*/
|
||||
#include "cmsg_extras.h"
|
||||
|
||||
#endif /* VSF_PORTINGJUNK_H */
|
||||
|
14
port/solaris_bogons.h
Normal file
14
port/solaris_bogons.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef VSF_SOLARIS_BOGONS_H
|
||||
#define VSF_SOLARIS_BOGONS_H
|
||||
|
||||
/* This bogon ensures we get access to CMSG_DATA, CMSG_FIRSTHDR */
|
||||
#define _XPG4_2
|
||||
|
||||
/* This bogon prevents _XPG4_2 breaking the include of signal.h! */
|
||||
#define __EXTENSIONS__
|
||||
|
||||
/* Need dirfd() */
|
||||
#include "dirfd_extras.h"
|
||||
|
||||
#endif /* VSF_SOLARIS_BOGONS_H */
|
||||
|
1125
postlogin.c
Normal file
1125
postlogin.c
Normal file
File diff suppressed because it is too large
Load Diff
15
postlogin.h
Normal file
15
postlogin.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef VSF_POSTLOGIN_H
|
||||
#define VSF_POSTLOGIN_H
|
||||
|
||||
struct vsf_session;
|
||||
|
||||
/* process_post_login()
|
||||
* PURPOSE
|
||||
* Called to begin FTP protocol parsing for a logged in session.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
*/
|
||||
void process_post_login(struct vsf_session* p_sess);
|
||||
|
||||
#endif /* VSF_POSTLOGIN_H */
|
||||
|
113
postprivparent.c
Normal file
113
postprivparent.c
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* postprivparent.c
|
||||
*
|
||||
* This file contains all privileged parent services offered after logging
|
||||
* in. This includes e.g. chown() of uploaded files, issuing of port 20
|
||||
* sockets.
|
||||
*/
|
||||
|
||||
#include "postprivparent.h"
|
||||
#include "session.h"
|
||||
#include "privops.h"
|
||||
#include "privsock.h"
|
||||
#include "utility.h"
|
||||
#include "tunables.h"
|
||||
#include "defs.h"
|
||||
#include "sysutil.h"
|
||||
#include "str.h"
|
||||
#include "secutil.h"
|
||||
#include "sysstr.h"
|
||||
#include "sysdeputil.h"
|
||||
|
||||
static void minimize_privilege(struct vsf_session* p_sess);
|
||||
static void process_post_login_req(struct vsf_session* p_sess);
|
||||
static void cmd_process_chown(struct vsf_session* p_sess);
|
||||
static void cmd_process_get_data_sock(struct vsf_session* p_sess);
|
||||
|
||||
void
|
||||
vsf_priv_parent_postlogin(struct vsf_session* p_sess)
|
||||
{
|
||||
minimize_privilege(p_sess);
|
||||
/* We're still here... */
|
||||
while (1)
|
||||
{
|
||||
process_post_login_req(p_sess);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
process_post_login_req(struct vsf_session* p_sess)
|
||||
{
|
||||
/* Blocks */
|
||||
char cmd = priv_sock_get_cmd(p_sess);
|
||||
if (tunable_chown_uploads && cmd == PRIV_SOCK_CHOWN)
|
||||
{
|
||||
cmd_process_chown(p_sess);
|
||||
}
|
||||
else if (tunable_connect_from_port_20 && cmd == PRIV_SOCK_GET_DATA_SOCK)
|
||||
{
|
||||
cmd_process_get_data_sock(p_sess);
|
||||
}
|
||||
else
|
||||
{
|
||||
die("bad post login request");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
minimize_privilege(struct vsf_session* p_sess)
|
||||
{
|
||||
/* So, we logged in and forked a totally unprivileged child. Our job
|
||||
* now is to minimize the privilege we need in order to act as a helper
|
||||
* to the child.
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
/* Cool. We're outta here. */
|
||||
vsf_sysutil_exit(0);
|
||||
}
|
||||
{
|
||||
unsigned int caps = 0;
|
||||
struct mystr user_str = INIT_MYSTR;
|
||||
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)
|
||||
{
|
||||
caps |= kCapabilityCAP_CHOWN;
|
||||
}
|
||||
if (tunable_connect_from_port_20)
|
||||
{
|
||||
caps |= kCapabilityCAP_NET_BIND_SERVICE;
|
||||
}
|
||||
vsf_secutil_change_credentials(&user_str, &dir_str, 1, 0, caps);
|
||||
str_free(&user_str);
|
||||
str_free(&dir_str);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_process_chown(struct vsf_session* p_sess)
|
||||
{
|
||||
int the_fd = priv_sock_parent_recv_fd(p_sess);
|
||||
vsf_privop_do_file_chown(p_sess, the_fd);
|
||||
vsf_sysutil_close(the_fd);
|
||||
priv_sock_send_result(p_sess, PRIV_SOCK_RESULT_OK);
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_process_get_data_sock(struct vsf_session* p_sess)
|
||||
{
|
||||
int sock_fd = vsf_privop_get_ftp_port_sock(p_sess);
|
||||
priv_sock_send_result(p_sess, PRIV_SOCK_RESULT_OK);
|
||||
priv_sock_parent_send_fd(p_sess, sock_fd);
|
||||
vsf_sysutil_close(sock_fd);
|
||||
}
|
||||
|
16
postprivparent.h
Normal file
16
postprivparent.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef VSF_LOGINPRIVPARENT_H
|
||||
#define VSF_LOGINPRIVPARENT_H
|
||||
|
||||
struct vsf_session;
|
||||
|
||||
/* vsf_priv_parent_postlogin()
|
||||
* PURPOSE
|
||||
* Called in the two process security model to commence "listening" for
|
||||
* requests from the unprivileged child.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
*/
|
||||
void vsf_priv_parent_postlogin(struct vsf_session* p_sess);
|
||||
|
||||
#endif /* VSF_LOGINPRIVPARENT_H */
|
||||
|
138
prelogin.c
Normal file
138
prelogin.c
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* prelogin.c
|
||||
*
|
||||
* Code to parse the FTP protocol prior to a successful login.
|
||||
*/
|
||||
|
||||
#include "prelogin.h"
|
||||
#include "ftpcmdio.h"
|
||||
#include "ftpcodes.h"
|
||||
#include "str.h"
|
||||
#include "vsftpver.h"
|
||||
#include "tunables.h"
|
||||
#include "oneprocess.h"
|
||||
#include "twoprocess.h"
|
||||
#include "sysdeputil.h"
|
||||
#include "sysutil.h"
|
||||
#include "session.h"
|
||||
|
||||
/* Functions used */
|
||||
static void emit_greeting(struct vsf_session* p_sess);
|
||||
static void parse_username_password(struct vsf_session* p_sess);
|
||||
static void handle_user_command(struct vsf_session* p_sess);
|
||||
static void handle_pass_command(struct vsf_session* p_sess);
|
||||
|
||||
void
|
||||
init_connection(struct vsf_session* p_sess)
|
||||
{
|
||||
if (tunable_setproctitle_enable)
|
||||
{
|
||||
vsf_sysutil_setproctitle("not logged in");
|
||||
}
|
||||
/* Before we talk to the remote, make sure an alarm is set up in case
|
||||
* writing the initial greetings should block.
|
||||
*/
|
||||
vsf_cmdio_set_alarm(p_sess);
|
||||
emit_greeting(p_sess);
|
||||
parse_username_password(p_sess);
|
||||
}
|
||||
|
||||
static void
|
||||
emit_greeting(struct vsf_session* p_sess)
|
||||
{
|
||||
if (tunable_ftpd_banner == 0)
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_GREET, "ready, dude (vsFTPd " VSF_VERSION
|
||||
": beat me, break me)");
|
||||
}
|
||||
else
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_GREET, tunable_ftpd_banner);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parse_username_password(struct vsf_session* p_sess)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
vsf_cmdio_get_cmd_and_arg(p_sess, &p_sess->ftp_cmd_str,
|
||||
&p_sess->ftp_arg_str, 1);
|
||||
if (str_equal_text(&p_sess->ftp_cmd_str, "USER"))
|
||||
{
|
||||
handle_user_command(p_sess);
|
||||
}
|
||||
else if (str_equal_text(&p_sess->ftp_cmd_str, "PASS"))
|
||||
{
|
||||
handle_pass_command(p_sess);
|
||||
}
|
||||
else if (str_equal_text(&p_sess->ftp_cmd_str, "QUIT"))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_GOODBYE, "Goodbye.");
|
||||
vsf_sysutil_exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_LOGINERR,
|
||||
"Please login with USER and PASS.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_user_command(struct vsf_session* p_sess)
|
||||
{
|
||||
/* SECURITY: If we're in anonymous only-mode, immediately reject
|
||||
* non-anonymous usernames in the hope we save passwords going plaintext
|
||||
* over the network
|
||||
*/
|
||||
str_copy(&p_sess->user_str, &p_sess->ftp_arg_str);
|
||||
str_upper(&p_sess->ftp_arg_str);
|
||||
if (!tunable_local_enable &&
|
||||
!str_equal_text(&p_sess->ftp_arg_str, "FTP") &&
|
||||
!str_equal_text(&p_sess->ftp_arg_str, "ANONYMOUS"))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_LOGINERR,
|
||||
"This FTP server is anonymous only.");
|
||||
str_empty(&p_sess->user_str);
|
||||
return;
|
||||
}
|
||||
if (!str_isempty(&p_sess->userlist_str))
|
||||
{
|
||||
int located = str_contains_line(&p_sess->userlist_str, &p_sess->user_str);
|
||||
if ((located && tunable_userlist_deny) ||
|
||||
(!located && !tunable_userlist_deny))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_LOGINERR, "Permission denied.");
|
||||
str_empty(&p_sess->user_str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
vsf_cmdio_write(p_sess, FTP_GIVEPWORD, "Please specify the password.");
|
||||
}
|
||||
|
||||
static void
|
||||
handle_pass_command(struct vsf_session* p_sess)
|
||||
{
|
||||
if (str_isempty(&p_sess->user_str))
|
||||
{
|
||||
vsf_cmdio_write(p_sess, FTP_NEEDUSER, "Login with USER first.");
|
||||
return;
|
||||
}
|
||||
/* These login calls never return if successful */
|
||||
if (tunable_one_process_model)
|
||||
{
|
||||
vsf_one_process_login(p_sess, &p_sess->ftp_arg_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
vsf_two_process_login(p_sess, &p_sess->ftp_arg_str);
|
||||
}
|
||||
vsf_cmdio_write(p_sess, FTP_LOGINERR, "Login incorrect.");
|
||||
str_empty(&p_sess->user_str);
|
||||
/* FALLTHRU if login fails */
|
||||
}
|
||||
|
16
prelogin.h
Normal file
16
prelogin.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef VSF_PRELOGIN_H
|
||||
#define VSF_PRELOGIN_H
|
||||
|
||||
struct vsf_session;
|
||||
|
||||
/* init_connection()
|
||||
* PURPOSE
|
||||
* Called as an entry point to FTP protocol processing, when a client connects.
|
||||
* This function will emit the FTP greeting, then start talking FTP protocol
|
||||
* to the client.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
*/
|
||||
void init_connection(struct vsf_session* p_sess);
|
||||
|
||||
#endif /* VSF_PRELOGIN_H */
|
206
privops.c
Normal file
206
privops.c
Normal file
@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* License: GPL
|
||||
* Author: Chris Evans
|
||||
* privops.c
|
||||
*
|
||||
* Code implementing the privileged operations that the unprivileged client
|
||||
* might request.
|
||||
* Look for suitable paranoia in this file.
|
||||
*/
|
||||
|
||||
#include "privops.h"
|
||||
#include "session.h"
|
||||
#include "sysdeputil.h"
|
||||
#include "sysutil.h"
|
||||
#include "utility.h"
|
||||
#include "str.h"
|
||||
#include "tunables.h"
|
||||
#include "defs.h"
|
||||
#include "logging.h"
|
||||
|
||||
/* File private functions */
|
||||
static enum EVSFPrivopLoginResult handle_anonymous_login(
|
||||
struct vsf_session* p_sess, const struct mystr* p_pass_str);
|
||||
static enum EVSFPrivopLoginResult handle_local_login(
|
||||
struct vsf_session* p_sess, const struct mystr* p_user_str,
|
||||
const struct mystr* p_pass_str);
|
||||
static void setup_username_globals(struct vsf_session* p_sess,
|
||||
const struct mystr* p_str);
|
||||
static enum EVSFPrivopLoginResult handle_login(
|
||||
struct vsf_session* p_sess, const struct mystr* p_user_str,
|
||||
const struct mystr* p_pass_str);
|
||||
|
||||
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();
|
||||
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));
|
||||
retval = vsf_sysutil_bind(s, p_sockaddr);
|
||||
if (retval != 0)
|
||||
{
|
||||
die("vsf_sysutil_bind");
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
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);
|
||||
/* 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 ||
|
||||
!vsf_sysutil_statbuf_is_regfile(s_p_statbuf) ||
|
||||
vsf_sysutil_statbuf_get_uid(s_p_statbuf) != p_sess->anon_ftp_uid)
|
||||
{
|
||||
die("invalid fd in cmd_process_chown");
|
||||
}
|
||||
/* SECURITY! You need an OS which strips SUID/SGID bits on chown(),
|
||||
* otherwise a compromise of the FTP user will lead to compromise of
|
||||
* the "anon_upload_chown_uid" user (think chmod +s).
|
||||
*/
|
||||
vsf_sysutil_fchown(fd, p_sess->anon_upload_chown_uid, -1);
|
||||
}
|
||||
|
||||
enum EVSFPrivopLoginResult
|
||||
vsf_privop_do_login(struct vsf_session* p_sess,
|
||||
const struct mystr* p_pass_str)
|
||||
{
|
||||
enum EVSFPrivopLoginResult result =
|
||||
handle_login(p_sess, &p_sess->user_str, p_pass_str);
|
||||
vsf_log_start_entry(p_sess, kVSFLogEntryLogin);
|
||||
if (result == kVSFLoginFail)
|
||||
{
|
||||
vsf_log_do_log(p_sess, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
vsf_log_do_log(p_sess, 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static enum EVSFPrivopLoginResult
|
||||
handle_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
|
||||
const struct mystr* p_pass_str)
|
||||
{
|
||||
/* Do not assume PAM can cope with dodgy input, even though it
|
||||
* almost certainly can.
|
||||
*/
|
||||
int anonymous_login = 0;
|
||||
unsigned int len = str_getlen(p_user_str);
|
||||
if (len == 0 || len > VSFTP_USERNAME_MAX)
|
||||
{
|
||||
return kVSFLoginFail;
|
||||
}
|
||||
/* Throw out dodgy start characters */
|
||||
if (!vsf_sysutil_isalnum(str_get_char_at(p_user_str, 0)))
|
||||
{
|
||||
return kVSFLoginFail;
|
||||
}
|
||||
/* Throw out non-printable characters and space in username */
|
||||
if (str_contains_space(p_user_str) ||
|
||||
str_contains_unprintable(p_user_str))
|
||||
{
|
||||
return kVSFLoginFail;
|
||||
}
|
||||
/* Throw out excessive length passwords */
|
||||
len = str_getlen(p_pass_str);
|
||||
if (len > VSFTP_PASSWORD_MAX)
|
||||
{
|
||||
return kVSFLoginFail;
|
||||
}
|
||||
/* Check for an anonymous login or "real" login */
|
||||
if (tunable_anonymous_enable)
|
||||
{
|
||||
struct mystr upper_str = INIT_MYSTR;
|
||||
str_copy(&upper_str, p_user_str);
|
||||
str_upper(&upper_str);
|
||||
if (str_equal_text(&upper_str, "FTP") ||
|
||||
str_equal_text(&upper_str, "ANONYMOUS"))
|
||||
{
|
||||
anonymous_login = 1;
|
||||
}
|
||||
str_free(&upper_str);
|
||||
}
|
||||
{
|
||||
enum EVSFPrivopLoginResult result = kVSFLoginFail;
|
||||
if (anonymous_login)
|
||||
{
|
||||
result = handle_anonymous_login(p_sess, p_pass_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = handle_local_login(p_sess, p_user_str, p_pass_str);
|
||||
}
|
||||
str_free(&p_sess->banned_email_str);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static enum EVSFPrivopLoginResult
|
||||
handle_anonymous_login(struct vsf_session* p_sess,
|
||||
const struct mystr* p_pass_str)
|
||||
{
|
||||
if (!str_isempty(&p_sess->banned_email_str) &&
|
||||
str_contains_line(&p_sess->banned_email_str, 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))
|
||||
{
|
||||
str_alloc_text(&p_sess->anon_pass_str, "?");
|
||||
}
|
||||
/* "Fix" any characters which might upset the log processing */
|
||||
str_replace_char(&p_sess->anon_pass_str, ' ', '_');
|
||||
str_replace_char(&p_sess->anon_pass_str, '\n', '?');
|
||||
{
|
||||
struct mystr ftp_username_str = INIT_MYSTR;
|
||||
str_alloc_text(&ftp_username_str, tunable_ftp_username);
|
||||
setup_username_globals(p_sess, &ftp_username_str);
|
||||
str_free(&ftp_username_str);
|
||||
}
|
||||
return kVSFLoginAnon;
|
||||
}
|
||||
|
||||
static enum EVSFPrivopLoginResult
|
||||
handle_local_login(struct vsf_session* p_sess,
|
||||
const struct mystr* p_user_str,
|
||||
const struct mystr* p_pass_str)
|
||||
{
|
||||
if (!vsf_sysdep_check_auth(p_user_str, p_pass_str, &p_sess->remote_ip_str))
|
||||
{
|
||||
return kVSFLoginFail;
|
||||
}
|
||||
setup_username_globals(p_sess, p_user_str);
|
||||
return kVSFLoginReal;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_username_globals(struct vsf_session* p_sess, const struct mystr* p_str)
|
||||
{
|
||||
str_copy(&p_sess->user_str, p_str);
|
||||
if (tunable_setproctitle_enable)
|
||||
{
|
||||
struct mystr prefix_str = INIT_MYSTR;
|
||||
str_copy(&prefix_str, &p_sess->remote_ip_str);
|
||||
str_append_char(&prefix_str, '/');
|
||||
str_append_str(&prefix_str, p_str);
|
||||
vsf_sysutil_set_proctitle_prefix(&prefix_str);
|
||||
str_free(&prefix_str);
|
||||
}
|
||||
}
|
||||
|
50
privops.h
Normal file
50
privops.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef VSF_PRIVOPS_H
|
||||
#define VSF_PRIVOPS_H
|
||||
|
||||
struct mystr;
|
||||
struct vsf_session;
|
||||
|
||||
/* vsf_privop_get_ftp_port_sock()
|
||||
* PURPOSE
|
||||
* Return a network socket bound to a privileged port (less than 1024).
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* RETURNS
|
||||
* A file descriptor which is a socket bound to the privileged port.
|
||||
*/
|
||||
int vsf_privop_get_ftp_port_sock(struct vsf_session* p_sess);
|
||||
|
||||
/* vsf_privop_do_file_chown()
|
||||
* PURPOSE
|
||||
* Takes a file owned by the unprivileged FTP user, and change the ownership
|
||||
* to the value defined in the config file.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* fd - the file descriptor of the regular file
|
||||
*/
|
||||
void vsf_privop_do_file_chown(struct vsf_session* p_sess, int fd);
|
||||
|
||||
enum EVSFPrivopLoginResult
|
||||
{
|
||||
kVSFLoginNull = 0,
|
||||
kVSFLoginFail,
|
||||
kVSFLoginAnon,
|
||||
kVSFLoginReal
|
||||
};
|
||||
/* vsf_privop_do_login()
|
||||
* PURPOSE
|
||||
* Check if the supplied username/password combination is valid. This
|
||||
* interface caters for checking both anonymous and real logins.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* p_pass_str - the proposed password
|
||||
* RETURNS
|
||||
* kVSFLoginFail - access denied
|
||||
* kVSFLoginAnon - anonymous login credentials OK
|
||||
* kVSFLoginReal - real login credentials OK
|
||||
*/
|
||||
enum EVSFPrivopLoginResult vsf_privop_do_login(
|
||||
struct vsf_session* p_sess, const struct mystr* p_pass_str);
|
||||
|
||||
#endif /* VSF_PRIVOPS_H */
|
||||
|
132
privsock.c
Normal file
132
privsock.c
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* privsock.c
|
||||
*
|
||||
* This file contains code for a simple message and file descriptor passing
|
||||
* API, over a pair of UNIX sockets.
|
||||
* The messages are typically travelling across a privilege boundary, with
|
||||
* heavy distrust of messages on the side of more privilege.
|
||||
*/
|
||||
|
||||
#include "privsock.h"
|
||||
|
||||
#include "utility.h"
|
||||
#include "defs.h"
|
||||
#include "str.h"
|
||||
#include "netstr.h"
|
||||
#include "sysutil.h"
|
||||
#include "sysdeputil.h"
|
||||
#include "secbuf.h"
|
||||
#include "session.h"
|
||||
|
||||
void
|
||||
priv_sock_init(struct vsf_session* p_sess)
|
||||
{
|
||||
const struct vsf_sysutil_socketpair_retval retval =
|
||||
vsf_sysutil_unix_dgram_socketpair();
|
||||
if (p_sess->privsock_inited)
|
||||
{
|
||||
bug("priv_sock_init called twice");
|
||||
}
|
||||
p_sess->parent_fd = retval.socket_one;
|
||||
p_sess->child_fd = retval.socket_two;
|
||||
p_sess->privsock_inited = 1;
|
||||
}
|
||||
|
||||
void
|
||||
priv_sock_send_cmd(struct vsf_session* p_sess, char cmd)
|
||||
{
|
||||
/* DGRAM socket -> message boundaries retained -> use plain write */
|
||||
int retval = vsf_sysutil_write(p_sess->child_fd, &cmd, sizeof(cmd));
|
||||
if (retval != sizeof(cmd))
|
||||
{
|
||||
die("vsf_sysutil_write");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
priv_sock_send_str(struct vsf_session* p_sess, const struct mystr* p_str)
|
||||
{
|
||||
struct mystr null_term_str = INIT_MYSTR;
|
||||
str_copy(&null_term_str, p_str);
|
||||
str_append_char(&null_term_str, '\0');
|
||||
str_netfd_write(&null_term_str, p_sess->child_fd);
|
||||
str_free(&null_term_str);
|
||||
}
|
||||
|
||||
char
|
||||
priv_sock_get_result(struct vsf_session* p_sess)
|
||||
{
|
||||
char res;
|
||||
/* DGRAM socket -> message boundaries retained -> use plain read */
|
||||
int retval = vsf_sysutil_read(p_sess->child_fd, &res, sizeof(res));
|
||||
if (retval != sizeof(res))
|
||||
{
|
||||
die("vsf_sysutil_read");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
char
|
||||
priv_sock_get_cmd(struct vsf_session* p_sess)
|
||||
{
|
||||
char res;
|
||||
/* DGRAM socket -> message boundaries retained -> use plain read */
|
||||
int retval = vsf_sysutil_read(p_sess->parent_fd, &res, sizeof(res));
|
||||
if (retval != sizeof(res))
|
||||
{
|
||||
die("vsf_sysutil_read");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
priv_sock_get_str(struct vsf_session* p_sess, struct mystr* p_dest)
|
||||
{
|
||||
static char* s_p_privsock_str_buf;
|
||||
if (s_p_privsock_str_buf == 0)
|
||||
{
|
||||
vsf_secbuf_alloc(&s_p_privsock_str_buf, VSFTP_PRIVSOCK_MAXSTR);
|
||||
}
|
||||
/* XXX - alert - will return truncated string if sender embedded a \0 */
|
||||
str_netfd_alloc(p_dest, p_sess->parent_fd, '\0', s_p_privsock_str_buf,
|
||||
VSFTP_PRIVSOCK_MAXSTR);
|
||||
}
|
||||
|
||||
void
|
||||
priv_sock_send_result(struct vsf_session* p_sess, char res)
|
||||
{
|
||||
/* DGRAM socket -> message boundaries retained -> use plain write */
|
||||
int retval = vsf_sysutil_write(p_sess->parent_fd, &res, sizeof(res));
|
||||
if (retval != sizeof(res))
|
||||
{
|
||||
die("vsf_sysutil_write");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
priv_sock_child_send_fd(struct vsf_session* p_sess, int fd)
|
||||
{
|
||||
vsf_sysutil_send_fd(p_sess->child_fd, fd);
|
||||
}
|
||||
|
||||
void
|
||||
priv_sock_parent_send_fd(struct vsf_session* p_sess, int fd)
|
||||
{
|
||||
vsf_sysutil_send_fd(p_sess->parent_fd, fd);
|
||||
}
|
||||
|
||||
int
|
||||
priv_sock_parent_recv_fd(struct vsf_session* p_sess)
|
||||
{
|
||||
return vsf_sysutil_recv_fd(p_sess->parent_fd);
|
||||
}
|
||||
|
||||
int
|
||||
priv_sock_child_recv_fd(struct vsf_session* p_sess)
|
||||
{
|
||||
return vsf_sysutil_recv_fd(p_sess->child_fd);
|
||||
}
|
||||
|
117
privsock.h
Normal file
117
privsock.h
Normal file
@ -0,0 +1,117 @@
|
||||
#ifndef VSF_PRIVSOCK_H
|
||||
#define VSF_PRIVSOCK_H
|
||||
|
||||
struct mystr;
|
||||
struct vsf_session;
|
||||
|
||||
/* priv_sock_init()
|
||||
* PURPOSE
|
||||
* Initialize the priv_sock system, by opening the communications sockets.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
*/
|
||||
void priv_sock_init(struct vsf_session* p_sess);
|
||||
|
||||
/* priv_sock_send_cmd()
|
||||
* PURPOSE
|
||||
* Sends a command to the privileged side of the channel.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* cmd - the command to send
|
||||
*/
|
||||
void priv_sock_send_cmd(struct vsf_session* p_sess, char cmd);
|
||||
|
||||
/* priv_sock_send_str()
|
||||
* PURPOSE
|
||||
* Sends a string to the privileged side of the channel.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* p_str - the string to send
|
||||
*/
|
||||
void priv_sock_send_str(struct vsf_session* p_sess, const struct mystr* p_str);
|
||||
|
||||
/* priv_sock_get_result()
|
||||
* PURPOSE
|
||||
* Receives a response from the privileged side of the channel.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* RETURNS
|
||||
* The response code.
|
||||
*/
|
||||
char priv_sock_get_result(struct vsf_session* p_sess);
|
||||
|
||||
/* priv_sock_get_cmd()
|
||||
* PURPOSE
|
||||
* Receives a command on the privileged side of the channel.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* RETURNS
|
||||
* The command that was sent.
|
||||
*/
|
||||
char priv_sock_get_cmd(struct vsf_session* p_sess);
|
||||
|
||||
/* priv_sock_get_str()
|
||||
* PURPOSE
|
||||
* Receives a string on the privileged side of the channel.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* p_dest - where to copy the received string
|
||||
*/
|
||||
void priv_sock_get_str(struct vsf_session* p_sess, struct mystr* p_dest);
|
||||
|
||||
/* priv_sock_send_result()
|
||||
* PURPOSE
|
||||
* Sends a command result to the unprivileged side of the channel.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* res - the result to send
|
||||
*/
|
||||
void priv_sock_send_result(struct vsf_session* p_sess, char res);
|
||||
|
||||
/* priv_sock_child_send_fd()
|
||||
* PURPOSE
|
||||
* Sends a file descriptor to the privileged side of the channel.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* fd - the descriptor to send
|
||||
*/
|
||||
void priv_sock_child_send_fd(struct vsf_session* p_sess, int fd);
|
||||
|
||||
/* priv_sock_parent_recv_fd()
|
||||
* PURPOSE
|
||||
* Receives a file descriptor on the privileged side of the channel.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* RETURNS
|
||||
* The received file descriptor
|
||||
*/
|
||||
int priv_sock_parent_recv_fd(struct vsf_session* p_sess);
|
||||
|
||||
/* priv_sock_parent_send_fd()
|
||||
* PURPOSE
|
||||
* Sends a file descriptor to the unprivileged side of the channel.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* fd - the descriptor to send
|
||||
*/
|
||||
void priv_sock_parent_send_fd(struct vsf_session* p_sess, int fd);
|
||||
|
||||
/* priv_sock_child_recv_fd()
|
||||
* PURPOSE
|
||||
* Receives a file descriptor on the unprivileged side of the channel.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* RETURNS
|
||||
* The received file descriptor
|
||||
*/
|
||||
int priv_sock_child_recv_fd(struct vsf_session* p_sess);
|
||||
|
||||
#define PRIV_SOCK_LOGIN 1
|
||||
#define PRIV_SOCK_CHOWN 2
|
||||
#define PRIV_SOCK_GET_DATA_SOCK 3
|
||||
|
||||
#define PRIV_SOCK_RESULT_OK 1
|
||||
#define PRIV_SOCK_RESULT_BAD 2
|
||||
|
||||
#endif /* VSF_PRIVSOCK_H */
|
||||
|
89
secbuf.c
Normal file
89
secbuf.c
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* secbuf.c
|
||||
*
|
||||
* Here are some routines providing the (possibly silly) concept of a secure
|
||||
* buffer. A secure buffer may not be overflowed. A single byte overflow
|
||||
* will cause the program to safely terminate.
|
||||
*/
|
||||
|
||||
#include "secbuf.h"
|
||||
#include "utility.h"
|
||||
#include "sysutil.h"
|
||||
#include "sysdeputil.h"
|
||||
|
||||
void
|
||||
vsf_secbuf_alloc(char** p_ptr, unsigned int size)
|
||||
{
|
||||
unsigned int page_offset;
|
||||
unsigned int round_up;
|
||||
char* p_mmap;
|
||||
char* p_no_access_page;
|
||||
unsigned int page_size = vsf_sysutil_getpagesize();
|
||||
|
||||
/* Free any previous buffer */
|
||||
vsf_secbuf_free(p_ptr);
|
||||
/* Round up to next page size */
|
||||
page_offset = size % page_size;
|
||||
if (page_offset)
|
||||
{
|
||||
unsigned int num_pages = size / page_size;
|
||||
num_pages++;
|
||||
round_up = num_pages * page_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Allocation is on a page-size boundary */
|
||||
round_up = size;
|
||||
}
|
||||
/* Add on another two pages to make inaccessible */
|
||||
round_up += page_size * 2;
|
||||
|
||||
p_mmap = vsf_sysutil_map_anon_pages(round_up);
|
||||
/* Map the first and last page inaccessible */
|
||||
p_no_access_page = p_mmap + round_up - page_size;
|
||||
vsf_sysutil_memprotect(p_no_access_page, page_size, kVSFSysUtilMapProtNone);
|
||||
/* Before we make the "before" page inaccessible, store the size in it.
|
||||
* A little hack so that we don't need to explicitly be passed the size
|
||||
* when freeing an existing secure buffer
|
||||
*/
|
||||
*((unsigned int*)p_mmap) = round_up;
|
||||
p_no_access_page = p_mmap;
|
||||
vsf_sysutil_memprotect(p_no_access_page, page_size, kVSFSysUtilMapProtNone);
|
||||
|
||||
p_mmap += page_size;
|
||||
if (page_offset)
|
||||
{
|
||||
p_mmap += (page_size - page_offset);
|
||||
}
|
||||
*p_ptr = p_mmap;
|
||||
}
|
||||
|
||||
void
|
||||
vsf_secbuf_free(char** p_ptr)
|
||||
{
|
||||
unsigned int map_size;
|
||||
unsigned long page_offset;
|
||||
char* p_mmap = *p_ptr;
|
||||
unsigned int page_size = vsf_sysutil_getpagesize();
|
||||
if (p_mmap == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* Calculate the actual start of the mmap region */
|
||||
page_offset = (unsigned long) p_mmap % page_size;
|
||||
if (page_offset)
|
||||
{
|
||||
p_mmap -= page_offset;
|
||||
}
|
||||
p_mmap -= page_size;
|
||||
/* First make the first page readable so we can get the size */
|
||||
vsf_sysutil_memprotect(p_mmap, page_size, kVSFSysUtilMapProtReadOnly);
|
||||
/* Extract the mapping size */
|
||||
map_size = *((unsigned int*)p_mmap);
|
||||
/* Lose the mapping */
|
||||
vsf_sysutil_memunmap(p_mmap, map_size);
|
||||
}
|
||||
|
27
secbuf.h
Normal file
27
secbuf.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef VSF_SECBUF_H
|
||||
#define VSF_SECBUF_H
|
||||
|
||||
/* vsf_secbuf_alloc()
|
||||
* PURPOSE
|
||||
* Allocate a "secure buffer". A secure buffer is one which will attempt to
|
||||
* catch out of bounds accesses by crashing the program (rather than
|
||||
* corrupting memory). It works by using UNIX memory protection. It isn't
|
||||
* foolproof.
|
||||
* PARAMETERS
|
||||
* p_ptr - pointer to a pointer which is to contain the secure buffer.
|
||||
* Any previous buffer pointed to is freed.
|
||||
* size - size in bytes required for the secure buffer.
|
||||
*/
|
||||
void vsf_secbuf_alloc(char** p_ptr, unsigned int size);
|
||||
|
||||
/* vsf_secbuf_free()
|
||||
* PURPOSE
|
||||
* Frees a "secure buffer".
|
||||
* PARAMETERS
|
||||
* p_ptr - pointer to a pointer containing the buffer to be freed. The
|
||||
* buffer pointer is nullified by this call.
|
||||
*/
|
||||
void vsf_secbuf_free(char** p_ptr);
|
||||
|
||||
#endif /* VSF_SECBUF_H */
|
||||
|
94
secutil.c
Normal file
94
secutil.c
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* secutil.c
|
||||
*/
|
||||
|
||||
#include "secutil.h"
|
||||
#include "str.h"
|
||||
#include "sysutil.h"
|
||||
#include "sysstr.h"
|
||||
#include "utility.h"
|
||||
#include "sysdeputil.h"
|
||||
|
||||
void
|
||||
vsf_secutil_change_credentials(const struct mystr* p_user_str,
|
||||
const struct mystr* p_dir_str,
|
||||
int do_chroot,
|
||||
int activate_supplementary_groups,
|
||||
unsigned int caps)
|
||||
{
|
||||
struct vsf_sysutil_user* p_user;
|
||||
if (!vsf_sysutil_running_as_root())
|
||||
{
|
||||
bug("vsf_secutil_change_credentials: not running as root");
|
||||
}
|
||||
p_user = str_getpwnam(p_user_str);
|
||||
if (p_user == 0)
|
||||
{
|
||||
die("str_getpwnam");
|
||||
}
|
||||
{
|
||||
struct mystr dir_str = INIT_MYSTR;
|
||||
/* Work out where the chroot() jail is */
|
||||
if (p_dir_str == 0)
|
||||
{
|
||||
str_alloc_text(&dir_str, vsf_sysutil_user_get_homedir(p_user));
|
||||
}
|
||||
else
|
||||
{
|
||||
str_copy(&dir_str, p_dir_str);
|
||||
}
|
||||
/* Sort out supplementary groups before the chroot(). We need to access
|
||||
* /etc/groups
|
||||
*/
|
||||
if (activate_supplementary_groups)
|
||||
{
|
||||
vsf_sysutil_initgroups(p_user);
|
||||
}
|
||||
else
|
||||
{
|
||||
vsf_sysutil_clear_supp_groups();
|
||||
}
|
||||
|
||||
/* Always do the chdir() regardless of whether we are chroot()'ing */
|
||||
{
|
||||
int retval = str_chdir(&dir_str);
|
||||
if (retval != 0)
|
||||
{
|
||||
die("chdir");
|
||||
}
|
||||
/* Do the chroot() if required */
|
||||
if (do_chroot)
|
||||
{
|
||||
vsf_sysutil_chroot(".");
|
||||
}
|
||||
}
|
||||
str_free(&dir_str);
|
||||
}
|
||||
/* Handle capabilities */
|
||||
if (caps)
|
||||
{
|
||||
if (!vsf_sysdep_has_capabilities())
|
||||
{
|
||||
/* Need privilege but OS has no capabilities - have to keep root */
|
||||
return;
|
||||
}
|
||||
if (!vsf_sysdep_has_capabilities_as_non_root())
|
||||
{
|
||||
vsf_sysdep_adopt_capabilities(caps);
|
||||
return;
|
||||
}
|
||||
vsf_sysdep_keep_capabilities();
|
||||
}
|
||||
/* Set group id */
|
||||
vsf_sysutil_setgid(p_user);
|
||||
/* Finally set user id */
|
||||
vsf_sysutil_setuid(p_user);
|
||||
if (caps)
|
||||
{
|
||||
vsf_sysdep_adopt_capabilities(caps);
|
||||
}
|
||||
}
|
||||
|
29
secutil.h
Normal file
29
secutil.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef VSF_SECUTIL_H
|
||||
#define VSF_SECUTIL_H
|
||||
|
||||
struct mystr;
|
||||
|
||||
/* vsf_secutil_change_credentials()
|
||||
* PURPOSE
|
||||
* This function securely switches process credentials to the user specified.
|
||||
* There are options to enter a chroot() jail, and supplementary groups may
|
||||
* or may not be activated.
|
||||
* PARAMETERS
|
||||
* p_user_str - the name of the user to become
|
||||
* p_dir_str - the directory to chdir() and possibly chroot() to.
|
||||
* (if NULL, the user's home directory is used)
|
||||
* do_chroot - if non-zero, chroot() the new user into the directory
|
||||
* activate_supplementary_groups -
|
||||
* if non-zero, activate any supplementary groups
|
||||
* caps - bitmap of capabilities to adopt. NOTE, if the underlying
|
||||
* OS does not support capabilities as a non-root user, and
|
||||
* the capability bitset is non-empty, then root privileges
|
||||
* will have to be retained.
|
||||
*/
|
||||
void vsf_secutil_change_credentials(const struct mystr* p_user_str,
|
||||
const struct mystr* p_dir_str,
|
||||
int do_chroot,
|
||||
int activate_supplementary_groups,
|
||||
unsigned int caps);
|
||||
#endif /* VSF_SECUTIL_H */
|
||||
|
71
session.h
Normal file
71
session.h
Normal file
@ -0,0 +1,71 @@
|
||||
#ifndef VSF_SESSION_H
|
||||
#define VSF_SESSION_H
|
||||
|
||||
#ifndef VSFTP_STR_H
|
||||
#include "str.h"
|
||||
#endif
|
||||
|
||||
struct vsf_sysutil_sockaddr;
|
||||
struct mystr_list;
|
||||
|
||||
/* This struct contains variables specific to the state of the current FTP
|
||||
* session
|
||||
*/
|
||||
struct vsf_session
|
||||
{
|
||||
/* Details of the control connection */
|
||||
struct vsf_sysutil_sockaddr* p_local_addr;
|
||||
struct vsf_sysutil_sockaddr* p_remote_addr;
|
||||
|
||||
/* Details of the data connection */
|
||||
int pasv_listen_fd;
|
||||
struct vsf_sysutil_sockaddr* p_port_sockaddr;
|
||||
int data_fd;
|
||||
int data_progress;
|
||||
unsigned int bw_rate_max;
|
||||
long bw_send_start_sec;
|
||||
long bw_send_start_usec;
|
||||
|
||||
/* Details of the login */
|
||||
int is_anonymous;
|
||||
struct mystr user_str;
|
||||
struct mystr anon_pass_str;
|
||||
|
||||
/* Details of the FTP protocol state */
|
||||
unsigned long restart_pos;
|
||||
int is_ascii;
|
||||
struct mystr rnfr_filename_str;
|
||||
int abor_received;
|
||||
|
||||
/* 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 anon_upload_chown_uid;
|
||||
|
||||
/* Things we need to cache before we chroot() */
|
||||
struct mystr banned_email_str;
|
||||
struct mystr userlist_str;
|
||||
|
||||
/* Logging related details */
|
||||
int log_fd;
|
||||
struct mystr remote_ip_str;
|
||||
unsigned long log_type;
|
||||
long log_start_sec;
|
||||
long log_start_usec;
|
||||
struct mystr log_str;
|
||||
unsigned long transfer_size;
|
||||
|
||||
/* Buffers */
|
||||
struct mystr ftp_cmd_str;
|
||||
struct mystr ftp_arg_str;
|
||||
|
||||
/* Parent<->child comms channel */
|
||||
int privsock_inited;
|
||||
int parent_fd;
|
||||
int child_fd;
|
||||
};
|
||||
|
||||
#endif /* VSF_SESSION_H */
|
||||
|
618
str.c
Normal file
618
str.c
Normal file
@ -0,0 +1,618 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* str.c
|
||||
*
|
||||
* Generic string handling functions. The fact that a string is implemented
|
||||
* internally using a buffer is not exposed in the API. If you can't see
|
||||
* the buffers, you can't handle them in a screwed way. Or so goes the
|
||||
* theory, anyway...
|
||||
*/
|
||||
|
||||
/* Anti-lamer measures deployed, sir! */
|
||||
#define PRIVATE_HANDS_OFF_p_buf p_buf
|
||||
#define PRIVATE_HANDS_OFF_len len
|
||||
#define PRIVATE_HANDS_OFF_alloc_bytes alloc_bytes
|
||||
#include "str.h"
|
||||
|
||||
/* Ick. Its for die() */
|
||||
#include "utility.h"
|
||||
#include "sysutil.h"
|
||||
|
||||
/* File local functions */
|
||||
static void str_split_text_common(struct mystr* p_src, struct mystr* p_rhs,
|
||||
const char* p_text, int is_reverse);
|
||||
static int str_equal_internal(const char* p_buf1, unsigned int buf1_len,
|
||||
const char* p_buf2, unsigned int buf2_len);
|
||||
|
||||
/* Private functions */
|
||||
static void
|
||||
s_setbuf(struct mystr* p_str, char* p_newbuf)
|
||||
{
|
||||
if (p_str->p_buf != 0)
|
||||
{
|
||||
bug("p_buf not NULL when setting it");
|
||||
}
|
||||
p_str->p_buf = p_newbuf;
|
||||
}
|
||||
|
||||
void
|
||||
private_str_alloc_memchunk(struct mystr* p_str, const char* p_src,
|
||||
unsigned int len)
|
||||
{
|
||||
/* Make sure this will fit in the buffer */
|
||||
unsigned int buf_needed = len + 1;
|
||||
if (buf_needed > p_str->alloc_bytes)
|
||||
{
|
||||
str_free(p_str);
|
||||
s_setbuf(p_str, vsf_sysutil_malloc(buf_needed));
|
||||
p_str->alloc_bytes = buf_needed;
|
||||
}
|
||||
vsf_sysutil_memcpy(p_str->p_buf, p_src, len);
|
||||
p_str->p_buf[len] = '\0';
|
||||
p_str->len = len;
|
||||
}
|
||||
|
||||
void
|
||||
private_str_append_memchunk(struct mystr* p_str, const char* p_src,
|
||||
unsigned int len)
|
||||
{
|
||||
unsigned int buf_needed = p_str->len + len + 1;
|
||||
if (buf_needed > p_str->alloc_bytes)
|
||||
{
|
||||
p_str->p_buf = vsf_sysutil_realloc(p_str->p_buf, buf_needed);
|
||||
p_str->alloc_bytes = buf_needed;
|
||||
}
|
||||
vsf_sysutil_memcpy(p_str->p_buf + p_str->len, p_src, len);
|
||||
p_str->p_buf[p_str->len + len] = '\0';
|
||||
p_str->len += len;
|
||||
}
|
||||
|
||||
/* Public functions */
|
||||
void
|
||||
str_alloc_text(struct mystr* p_str, const char* p_src)
|
||||
{
|
||||
unsigned int len = vsf_sysutil_strlen(p_src);
|
||||
private_str_alloc_memchunk(p_str, p_src, len);
|
||||
}
|
||||
|
||||
void
|
||||
str_copy(struct mystr* p_dest, const struct mystr* p_src)
|
||||
{
|
||||
private_str_alloc_memchunk(p_dest, p_src->p_buf, p_src->len);
|
||||
}
|
||||
|
||||
const char*
|
||||
str_strdup(const struct mystr* p_str)
|
||||
{
|
||||
return vsf_sysutil_strdup(str_getbuf(p_str));
|
||||
}
|
||||
|
||||
void
|
||||
str_alloc_alt_term(struct mystr* p_str, const char* p_src, char term)
|
||||
{
|
||||
const char* p_search = p_src;
|
||||
unsigned int len = 0;
|
||||
while (*p_search != term)
|
||||
{
|
||||
p_search++;
|
||||
len++;
|
||||
}
|
||||
private_str_alloc_memchunk(p_str, p_src, len);
|
||||
}
|
||||
|
||||
void
|
||||
str_alloc_ulong(struct mystr* p_str, unsigned long the_long)
|
||||
{
|
||||
str_alloc_text(p_str, vsf_sysutil_ulong_to_str(the_long));
|
||||
}
|
||||
|
||||
void
|
||||
str_free(struct mystr* p_str)
|
||||
{
|
||||
if (p_str->p_buf != 0)
|
||||
{
|
||||
vsf_sysutil_free(p_str->p_buf);
|
||||
}
|
||||
p_str->p_buf = 0;
|
||||
p_str->len = 0;
|
||||
p_str->alloc_bytes = 0;
|
||||
}
|
||||
|
||||
void
|
||||
str_empty(struct mystr* p_str)
|
||||
{
|
||||
p_str->len = 0;
|
||||
}
|
||||
|
||||
void
|
||||
str_trunc(struct mystr* p_str, unsigned int trunc_len)
|
||||
{
|
||||
if (trunc_len >= p_str->len)
|
||||
{
|
||||
bug("trunc_len not smaller than len in str_trunc");
|
||||
}
|
||||
p_str->len = trunc_len;
|
||||
p_str->p_buf[p_str->len] = '\0';
|
||||
}
|
||||
|
||||
void
|
||||
str_reserve(struct mystr* p_str, unsigned int res_len)
|
||||
{
|
||||
if (res_len > p_str->alloc_bytes)
|
||||
{
|
||||
p_str->p_buf = vsf_sysutil_realloc(p_str->p_buf, res_len);
|
||||
p_str->alloc_bytes = res_len;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
str_isempty(const struct mystr* p_str)
|
||||
{
|
||||
return (p_str->len == 0);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
str_getlen(const struct mystr* p_str)
|
||||
{
|
||||
return p_str->len;
|
||||
}
|
||||
|
||||
const char*
|
||||
str_getbuf(const struct mystr* p_str)
|
||||
{
|
||||
if (p_str->p_buf == 0)
|
||||
{
|
||||
if (p_str->len != 0 || p_str->alloc_bytes != 0)
|
||||
{
|
||||
bug("p_buf NULL and len or alloc_bytes != 0 in str_getbuf");
|
||||
}
|
||||
private_str_alloc_memchunk((struct mystr*)p_str, 0, 0);
|
||||
}
|
||||
return p_str->p_buf;
|
||||
}
|
||||
|
||||
int
|
||||
str_strcmp(const struct mystr* p_str1, const struct mystr* p_str2)
|
||||
{
|
||||
return str_equal_internal(p_str1->p_buf, p_str1->len,
|
||||
p_str2->p_buf, p_str2->len);
|
||||
}
|
||||
|
||||
static int
|
||||
str_equal_internal(const char* p_buf1, unsigned int buf1_len,
|
||||
const char* p_buf2, unsigned int buf2_len)
|
||||
{
|
||||
int retval;
|
||||
unsigned int minlen = buf1_len;
|
||||
if (buf2_len < minlen)
|
||||
{
|
||||
minlen = buf2_len;
|
||||
}
|
||||
retval = vsf_sysutil_memcmp(p_buf1, p_buf2, minlen);
|
||||
if (retval != 0 || buf1_len == buf2_len)
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
/* Strings equal but lengths differ. The greater one, then, is the longer */
|
||||
return (int) (buf1_len - buf2_len);
|
||||
}
|
||||
|
||||
int
|
||||
str_equal(const struct mystr* p_str1, const struct mystr* p_str2)
|
||||
{
|
||||
return (str_strcmp(p_str1, p_str2) == 0);
|
||||
}
|
||||
|
||||
int
|
||||
str_equal_text(const struct mystr* p_str, const char* p_text)
|
||||
{
|
||||
unsigned int cmplen = vsf_sysutil_strlen(p_text);
|
||||
return (str_equal_internal(p_str->p_buf, p_str->len, p_text, cmplen) == 0);
|
||||
}
|
||||
|
||||
void
|
||||
str_append_str(struct mystr* p_str, const struct mystr* p_other)
|
||||
{
|
||||
private_str_append_memchunk(p_str, p_other->p_buf, p_other->len);
|
||||
}
|
||||
|
||||
void
|
||||
str_append_text(struct mystr* p_str, const char* p_src)
|
||||
{
|
||||
unsigned int len = vsf_sysutil_strlen(p_src);
|
||||
private_str_append_memchunk(p_str, p_src, len);
|
||||
}
|
||||
|
||||
void
|
||||
str_append_char(struct mystr* p_str, char the_char)
|
||||
{
|
||||
private_str_append_memchunk(p_str, &the_char, sizeof(the_char));
|
||||
}
|
||||
|
||||
void
|
||||
str_append_ulong(struct mystr* p_str, unsigned long the_ulong)
|
||||
{
|
||||
str_append_text(p_str, vsf_sysutil_ulong_to_str(the_ulong));
|
||||
}
|
||||
|
||||
void
|
||||
str_append_double(struct mystr* p_str, double the_double)
|
||||
{
|
||||
str_append_text(p_str, vsf_sysutil_double_to_str(the_double));
|
||||
}
|
||||
|
||||
void
|
||||
str_upper(struct mystr* p_str)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i=0; i < p_str->len; i++)
|
||||
{
|
||||
p_str->p_buf[i] = vsf_sysutil_toupper(p_str->p_buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
str_rpad(struct mystr* p_str, const unsigned int min_width)
|
||||
{
|
||||
unsigned int to_pad;
|
||||
if (p_str->len >= min_width)
|
||||
{
|
||||
return;
|
||||
}
|
||||
to_pad = min_width - p_str->len;
|
||||
while (to_pad--)
|
||||
{
|
||||
str_append_char(p_str, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
str_lpad(struct mystr* p_str, const unsigned int min_width)
|
||||
{
|
||||
static struct mystr s_tmp_str;
|
||||
unsigned int to_pad;
|
||||
if (p_str->len >= min_width)
|
||||
{
|
||||
return;
|
||||
}
|
||||
to_pad = min_width - p_str->len;
|
||||
str_empty(&s_tmp_str);
|
||||
while (to_pad--)
|
||||
{
|
||||
str_append_char(&s_tmp_str, ' ');
|
||||
}
|
||||
str_append_str(&s_tmp_str, p_str);
|
||||
str_copy(p_str, &s_tmp_str);
|
||||
}
|
||||
|
||||
void
|
||||
str_replace_char(struct mystr* p_str, char from, char to)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i=0; i < p_str->len; i++)
|
||||
{
|
||||
if (p_str->p_buf[i] == from)
|
||||
{
|
||||
p_str->p_buf[i] = to;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
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;
|
||||
str_copy(&s_lhs_chunk_str, p_str);
|
||||
str_free(p_str);
|
||||
do
|
||||
{
|
||||
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))
|
||||
{
|
||||
str_append_text(p_str, p_to);
|
||||
}
|
||||
/* Current rhs becomes new lhs */
|
||||
str_copy(&s_lhs_chunk_str, &s_rhs_chunk_str);
|
||||
} while (!str_isempty(&s_lhs_chunk_str));
|
||||
}
|
||||
|
||||
void
|
||||
str_split_char(struct mystr* p_src, struct mystr* p_rhs, char c)
|
||||
{
|
||||
/* Just use str_split_text */
|
||||
char ministr[2];
|
||||
ministr[0] = c;
|
||||
ministr[1] = '\0';
|
||||
str_split_text(p_src, p_rhs, ministr);
|
||||
}
|
||||
|
||||
void
|
||||
str_split_char_reverse(struct mystr* p_src, struct mystr* p_rhs, char c)
|
||||
{
|
||||
/* Just use str_split_text_reverse */
|
||||
char ministr[2];
|
||||
ministr[0] = c;
|
||||
ministr[1] = '\0';
|
||||
str_split_text_reverse(p_src, p_rhs, ministr);
|
||||
}
|
||||
|
||||
void
|
||||
str_split_text(struct mystr* p_src, struct mystr* p_rhs, const char* p_text)
|
||||
{
|
||||
str_split_text_common(p_src, p_rhs, p_text, 0);
|
||||
}
|
||||
|
||||
void
|
||||
str_split_text_reverse(struct mystr* p_src, struct mystr* p_rhs,
|
||||
const char* p_text)
|
||||
{
|
||||
str_split_text_common(p_src, p_rhs, p_text, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
str_split_text_common(struct mystr* p_src, struct mystr* p_rhs,
|
||||
const char* p_text, int is_reverse)
|
||||
{
|
||||
struct str_locate_result locate_result;
|
||||
unsigned int index;
|
||||
if (is_reverse)
|
||||
{
|
||||
locate_result = str_locate_text_reverse(p_src, p_text);
|
||||
}
|
||||
else
|
||||
{
|
||||
locate_result = str_locate_text(p_src, p_text);
|
||||
}
|
||||
/* Not found? */
|
||||
if (!locate_result.found)
|
||||
{
|
||||
str_empty(p_rhs);
|
||||
return;
|
||||
}
|
||||
index = locate_result.index;
|
||||
if (index + vsf_sysutil_strlen(p_text) > p_src->len)
|
||||
{
|
||||
bug("index invalid in str_split_text");
|
||||
}
|
||||
/* Build rhs */
|
||||
private_str_alloc_memchunk(p_rhs, p_src->p_buf + index + 1,
|
||||
p_src->len - index - 1);
|
||||
/* Build lhs */
|
||||
str_trunc(p_src, index);
|
||||
}
|
||||
|
||||
struct str_locate_result
|
||||
str_locate_str(const struct mystr* p_str, const struct mystr* p_look_str)
|
||||
{
|
||||
return str_locate_text(p_str, str_getbuf(p_look_str));
|
||||
}
|
||||
|
||||
struct str_locate_result
|
||||
str_locate_str_reverse(const struct mystr* p_str,
|
||||
const struct mystr* p_look_str)
|
||||
{
|
||||
return str_locate_text_reverse(p_str, str_getbuf(p_look_str));
|
||||
}
|
||||
|
||||
struct str_locate_result
|
||||
str_locate_char(const struct mystr* p_str, char look_char)
|
||||
{
|
||||
char look_str[2];
|
||||
look_str[0] = look_char;
|
||||
look_str[1] = '\0';
|
||||
return str_locate_text(p_str, look_str);
|
||||
}
|
||||
|
||||
struct str_locate_result
|
||||
str_locate_text(const struct mystr* p_str, const char* p_text)
|
||||
{
|
||||
struct str_locate_result retval;
|
||||
unsigned int i;
|
||||
unsigned int text_len = vsf_sysutil_strlen(p_text);
|
||||
retval.found = 0;
|
||||
retval.index = 0;
|
||||
if (text_len == 0 || text_len > p_str->len)
|
||||
{
|
||||
/* Not found */
|
||||
return retval;
|
||||
}
|
||||
for (i=0; i <= (p_str->len - text_len); i++)
|
||||
{
|
||||
if (vsf_sysutil_memcmp(p_str->p_buf + i, p_text, text_len) == 0)
|
||||
{
|
||||
retval.found = 1;
|
||||
retval.index = i;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
/* Not found */
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct str_locate_result
|
||||
str_locate_text_reverse(const struct mystr* p_str, const char* p_text)
|
||||
{
|
||||
struct str_locate_result retval;
|
||||
unsigned int i;
|
||||
unsigned int text_len = vsf_sysutil_strlen(p_text);
|
||||
retval.found = 0;
|
||||
retval.index = 0;
|
||||
if (text_len == 0 || text_len > p_str->len)
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
i = p_str->len - text_len;
|
||||
/* Want to go through loop once even if i==0 */
|
||||
while (1)
|
||||
{
|
||||
if (vsf_sysutil_memcmp(p_str->p_buf + i, p_text, text_len) == 0)
|
||||
{
|
||||
retval.found = 1;
|
||||
retval.index = i;
|
||||
return retval;
|
||||
}
|
||||
if (i == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
/* Not found */
|
||||
return retval;
|
||||
}
|
||||
|
||||
void
|
||||
str_left(const struct mystr* p_str, struct mystr* p_out, unsigned int chars)
|
||||
{
|
||||
if (chars > p_str->len)
|
||||
{
|
||||
bug("chars invalid in str_left");
|
||||
}
|
||||
private_str_alloc_memchunk(p_out, p_str->p_buf, chars);
|
||||
}
|
||||
|
||||
void
|
||||
str_right(const struct mystr* p_str, struct mystr* p_out, unsigned int chars)
|
||||
{
|
||||
unsigned int index = p_str->len - chars;
|
||||
if (chars > p_str->len)
|
||||
{
|
||||
bug("chars invalid in str_right");
|
||||
}
|
||||
private_str_alloc_memchunk(p_out, p_str->p_buf + index, chars);
|
||||
}
|
||||
|
||||
void
|
||||
str_mid_to_end(const struct mystr* p_str, struct mystr* p_out,
|
||||
unsigned int index)
|
||||
{
|
||||
if (index > p_str->len)
|
||||
{
|
||||
bug("invalid index in str_mid_to_end");
|
||||
}
|
||||
private_str_alloc_memchunk(p_out, p_str->p_buf + index, p_str->len - index);
|
||||
}
|
||||
|
||||
char
|
||||
str_get_char_at(const struct mystr* p_str, const unsigned int index)
|
||||
{
|
||||
if (index >= p_str->len)
|
||||
{
|
||||
bug("bad index in str_get_char_at");
|
||||
}
|
||||
return p_str->p_buf[index];
|
||||
}
|
||||
|
||||
int
|
||||
str_contains_space(const struct mystr* p_str)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i=0; i < p_str->len; i++)
|
||||
{
|
||||
if (vsf_sysutil_isspace(p_str->p_buf[i]))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
str_contains_unprintable(const struct mystr* p_str)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i=0; i < p_str->len; i++)
|
||||
{
|
||||
if (!vsf_sysutil_isprint(p_str->p_buf[i]))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
str_atoi(const struct mystr* p_str)
|
||||
{
|
||||
return vsf_sysutil_atoi(str_getbuf(p_str));
|
||||
}
|
||||
|
||||
long
|
||||
str_atol(const struct mystr* p_str)
|
||||
{
|
||||
return vsf_sysutil_atol(str_getbuf(p_str));
|
||||
}
|
||||
|
||||
unsigned int
|
||||
str_octal_to_uint(const struct mystr* p_str)
|
||||
{
|
||||
return vsf_sysutil_octal_to_uint(str_getbuf(p_str));
|
||||
}
|
||||
|
||||
int
|
||||
str_getline(const struct mystr* p_str, struct mystr* p_line_str,
|
||||
unsigned int* p_pos)
|
||||
{
|
||||
unsigned int start_pos = *p_pos;
|
||||
unsigned int curr_pos = start_pos;
|
||||
unsigned int buf_len = str_getlen(p_str);
|
||||
const char* p_buf = str_getbuf(p_str);
|
||||
unsigned int out_len;
|
||||
if (start_pos > buf_len)
|
||||
{
|
||||
bug("p_pos out of range in str_getline");
|
||||
}
|
||||
str_empty(p_line_str);
|
||||
if (start_pos == buf_len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
while (curr_pos < buf_len && p_buf[curr_pos] != '\n')
|
||||
{
|
||||
curr_pos++;
|
||||
}
|
||||
out_len = curr_pos - start_pos;
|
||||
/* If we ended on a \n - skip it */
|
||||
if (curr_pos < buf_len && p_buf[curr_pos] == '\n')
|
||||
{
|
||||
curr_pos++;
|
||||
}
|
||||
private_str_alloc_memchunk(p_line_str, p_buf + start_pos, out_len);
|
||||
*p_pos = curr_pos;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
str_contains_line(const struct mystr* p_str, const struct mystr* p_line_str)
|
||||
{
|
||||
static struct mystr s_curr_line_str;
|
||||
unsigned int pos = 0;
|
||||
while (str_getline(p_str, &s_curr_line_str, &pos))
|
||||
{
|
||||
if (str_equal(&s_curr_line_str, p_line_str))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
str_replace_unprintable(struct mystr* p_str, char new_char)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i=0; i < p_str->len; i++)
|
||||
{
|
||||
if (!vsf_sysutil_isprint(p_str->p_buf[i]))
|
||||
{
|
||||
p_str->p_buf[i] = new_char;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
115
str.h
Normal file
115
str.h
Normal file
@ -0,0 +1,115 @@
|
||||
#ifndef VSFTP_STR_H
|
||||
#define VSFTP_STR_H
|
||||
|
||||
/* TODO - document these functions ;-) */
|
||||
|
||||
struct mystr
|
||||
{
|
||||
char* PRIVATE_HANDS_OFF_p_buf;
|
||||
/* Internally, EXCLUDES trailing null */
|
||||
unsigned int PRIVATE_HANDS_OFF_len;
|
||||
unsigned int PRIVATE_HANDS_OFF_alloc_bytes;
|
||||
};
|
||||
|
||||
#define INIT_MYSTR \
|
||||
{ (void*)0, 0, 0 }
|
||||
|
||||
#ifdef VSFTP_STRING_HELPER
|
||||
#define str_alloc_memchunk private_str_alloc_memchunk
|
||||
#endif
|
||||
void private_str_alloc_memchunk(struct mystr* p_str, const char* p_src,
|
||||
unsigned int len);
|
||||
|
||||
void str_alloc_text(struct mystr* p_str, const char* p_src);
|
||||
/* NOTE: String buffer data does NOT include terminating character */
|
||||
void str_alloc_alt_term(struct mystr* p_str, const char* p_src, char term);
|
||||
void str_alloc_ulong(struct mystr* p_str, unsigned long the_ulong);
|
||||
void str_copy(struct mystr* p_dest, const struct mystr* p_src);
|
||||
const char* str_strdup(const struct mystr* p_str);
|
||||
void str_empty(struct mystr* p_str);
|
||||
void str_free(struct mystr* p_str);
|
||||
void str_trunc(struct mystr* p_str, unsigned int trunc_len);
|
||||
void str_reserve(struct mystr* p_str, unsigned int res_len);
|
||||
|
||||
int str_isempty(const struct mystr* p_str);
|
||||
unsigned int str_getlen(const struct mystr* p_str);
|
||||
const char* str_getbuf(const struct mystr* p_str);
|
||||
|
||||
int str_strcmp(const struct mystr* p_str1, const struct mystr* p_str2);
|
||||
int str_equal(const struct mystr* p_str1, const struct mystr* p_str2);
|
||||
int str_equal_text(const struct mystr* p_str, const char* p_text);
|
||||
|
||||
void str_append_str(struct mystr* p_str, const struct mystr* p_other);
|
||||
void str_append_text(struct mystr* p_str, const char* p_src);
|
||||
void str_append_ulong(struct mystr* p_str, unsigned long the_long);
|
||||
void str_append_char(struct mystr* p_str, char the_char);
|
||||
void str_append_double(struct mystr* p_str, double the_double);
|
||||
|
||||
void str_upper(struct mystr* p_str);
|
||||
void str_rpad(struct mystr* p_str, const unsigned int min_width);
|
||||
void str_lpad(struct mystr* p_str, const unsigned int min_width);
|
||||
void str_replace_char(struct mystr* p_str, char from, char to);
|
||||
void str_replace_text(struct mystr* p_str, const char* p_from,
|
||||
const char* p_to);
|
||||
|
||||
void str_split_char(struct mystr* p_src, struct mystr* p_rhs, char c);
|
||||
void str_split_char_reverse(struct mystr* p_src, struct mystr* p_rhs, char c);
|
||||
void str_split_text(struct mystr* p_src, struct mystr* p_rhs,
|
||||
const char* p_text);
|
||||
void str_split_text_reverse(struct mystr* p_src, struct mystr* p_rhs,
|
||||
const char* p_text);
|
||||
|
||||
struct str_locate_result
|
||||
{
|
||||
int found;
|
||||
unsigned int index;
|
||||
};
|
||||
|
||||
struct str_locate_result str_locate_char(
|
||||
const struct mystr* p_str, char look_char);
|
||||
struct str_locate_result str_locate_str(
|
||||
const struct mystr* p_str, const struct mystr* p_look_str);
|
||||
struct str_locate_result str_locate_str_reverse(
|
||||
const struct mystr* p_str, const struct mystr* p_look_str);
|
||||
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);
|
||||
|
||||
void str_left(const struct mystr* p_str, struct mystr* p_out,
|
||||
unsigned int chars);
|
||||
void str_right(const struct mystr* p_str, struct mystr* p_out,
|
||||
unsigned int chars);
|
||||
void str_mid_to_end(const struct mystr* p_str, struct mystr* p_out,
|
||||
unsigned int index);
|
||||
|
||||
char str_get_char_at(const struct mystr* p_str, const unsigned int index);
|
||||
int str_contains_space(const struct mystr* p_str);
|
||||
int str_contains_unprintable(const struct mystr* p_str);
|
||||
void str_replace_unprintable(struct mystr* p_str, char new_char);
|
||||
int str_atoi(const struct mystr* p_str);
|
||||
long str_atol(const struct mystr* p_str);
|
||||
unsigned int str_octal_to_uint(const struct mystr* p_str);
|
||||
|
||||
/* PURPOSE: Extract a line of text (delimited by \n or EOF) from a string
|
||||
* buffer, starting at character position 'p_pos'. The extracted line will
|
||||
* not contain the '\n' terminator.
|
||||
*
|
||||
* RETURNS: 0 if no more lines are available, 1 otherwise.
|
||||
* The extracted text line is stored in 'p_line_str', which is
|
||||
* emptied if there are no more lines. 'p_pos' is updated to point to the
|
||||
* first character after the end of the line just extracted.
|
||||
*/
|
||||
int str_getline(const struct mystr* p_str, struct mystr* p_line_str,
|
||||
unsigned int* p_pos);
|
||||
|
||||
/* PURPOSE: Detect whether or not a string buffer contains a specific line
|
||||
* of text (delimited by \n or EOF).
|
||||
*
|
||||
* RETURNS: 1 if there is a matching line, 0 otherwise.
|
||||
*/
|
||||
int str_contains_line(const struct mystr* p_str,
|
||||
const struct mystr* p_line_str);
|
||||
|
||||
#endif /* VSFTP_STR_H */
|
||||
|
174
strlist.c
Normal file
174
strlist.c
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* strlist.c
|
||||
*/
|
||||
|
||||
/* Anti-lamer measures deployed, sir! */
|
||||
#define PRIVATE_HANDS_OFF_alloc_len alloc_len
|
||||
#define PRIVATE_HANDS_OFF_list_len list_len
|
||||
#define PRIVATE_HANDS_OFF_p_nodes p_nodes
|
||||
#include "strlist.h"
|
||||
|
||||
#include "str.h"
|
||||
#include "utility.h"
|
||||
#include "sysutil.h"
|
||||
|
||||
struct mystr_list_node
|
||||
{
|
||||
struct mystr str;
|
||||
struct mystr sort_key_str;
|
||||
};
|
||||
|
||||
/* File locals */
|
||||
static struct mystr s_null_str;
|
||||
|
||||
static int sort_compare_func(const void* p1, const void* p2);
|
||||
static int sort_compare_func_reverse(const void* p1, const void* p2);
|
||||
static int sort_compare_common(const void* p1, const void* p2, int reverse);
|
||||
|
||||
void
|
||||
str_list_free(struct mystr_list* p_list)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i=0; i < p_list->list_len; ++i)
|
||||
{
|
||||
str_free(&p_list->p_nodes[i].str);
|
||||
str_free(&p_list->p_nodes[i].sort_key_str);
|
||||
}
|
||||
p_list->list_len = 0;
|
||||
p_list->alloc_len = 0;
|
||||
if (p_list->p_nodes)
|
||||
{
|
||||
vsf_sysutil_free(p_list->p_nodes);
|
||||
p_list->p_nodes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
str_list_get_length(const struct mystr_list* p_list)
|
||||
{
|
||||
return p_list->list_len;
|
||||
}
|
||||
|
||||
int
|
||||
str_list_contains_str(const struct mystr_list* p_list,
|
||||
const struct mystr* p_str)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i=0; i < p_list->list_len; ++i)
|
||||
{
|
||||
if (str_equal(p_str, &p_list->p_nodes[i].str))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
str_list_add(struct mystr_list* p_list, const struct mystr* p_str,
|
||||
const struct mystr* p_sort_key_str)
|
||||
{
|
||||
struct mystr_list_node* p_node;
|
||||
/* Expand the node allocation if we have to */
|
||||
if (p_list->list_len == p_list->alloc_len)
|
||||
{
|
||||
if (p_list->alloc_len == 0)
|
||||
{
|
||||
p_list->alloc_len = 32;
|
||||
p_list->p_nodes = vsf_sysutil_malloc(p_list->alloc_len *
|
||||
sizeof(struct mystr_list_node));
|
||||
}
|
||||
else
|
||||
{
|
||||
p_list->alloc_len *= 2;
|
||||
p_list->p_nodes = vsf_sysutil_realloc(p_list->p_nodes,
|
||||
p_list->alloc_len *
|
||||
sizeof(struct mystr_list_node));
|
||||
}
|
||||
}
|
||||
p_node = &p_list->p_nodes[p_list->list_len];
|
||||
p_node->str = s_null_str;
|
||||
p_node->sort_key_str = s_null_str;
|
||||
str_copy(&p_node->str, p_str);
|
||||
if (p_sort_key_str)
|
||||
{
|
||||
str_copy(&p_node->sort_key_str, p_sort_key_str);
|
||||
}
|
||||
p_list->list_len++;
|
||||
}
|
||||
|
||||
void
|
||||
str_list_sort(struct mystr_list* p_list, int reverse)
|
||||
{
|
||||
if (!reverse)
|
||||
{
|
||||
vsf_sysutil_qsort(p_list->p_nodes, p_list->list_len,
|
||||
sizeof(struct mystr_list_node), sort_compare_func);
|
||||
}
|
||||
else
|
||||
{
|
||||
vsf_sysutil_qsort(p_list->p_nodes, p_list->list_len,
|
||||
sizeof(struct mystr_list_node),
|
||||
sort_compare_func_reverse);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
sort_compare_func(const void* p1, const void* p2)
|
||||
{
|
||||
return sort_compare_common(p1, p2, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
sort_compare_func_reverse(const void* p1, const void* p2)
|
||||
{
|
||||
return sort_compare_common(p1, p2, 1);
|
||||
}
|
||||
|
||||
static int
|
||||
sort_compare_common(const void* p1, const void* p2, int reverse)
|
||||
{
|
||||
const struct mystr* p_cmp1;
|
||||
const struct mystr* p_cmp2;
|
||||
const struct mystr_list_node* p_node1 = (const struct mystr_list_node*) p1;
|
||||
const struct mystr_list_node* p_node2 = (const struct mystr_list_node*) p2;
|
||||
if (!str_isempty(&p_node1->sort_key_str))
|
||||
{
|
||||
p_cmp1 = &p_node1->sort_key_str;
|
||||
}
|
||||
else
|
||||
{
|
||||
p_cmp1 = &p_node1->str;
|
||||
}
|
||||
if (!str_isempty(&p_node2->sort_key_str))
|
||||
{
|
||||
p_cmp2 = &p_node2->sort_key_str;
|
||||
}
|
||||
else
|
||||
{
|
||||
p_cmp2 = &p_node2->str;
|
||||
}
|
||||
|
||||
if (reverse)
|
||||
{
|
||||
return str_strcmp(p_cmp2, p_cmp1);
|
||||
}
|
||||
else
|
||||
{
|
||||
return str_strcmp(p_cmp1, p_cmp2);
|
||||
}
|
||||
}
|
||||
|
||||
const struct mystr*
|
||||
str_list_get_pstr(const struct mystr_list* p_list, unsigned int index)
|
||||
{
|
||||
if (index >= p_list->list_len)
|
||||
{
|
||||
bug("index out of range in str_list_get_str");
|
||||
}
|
||||
return &p_list->p_nodes[index].str;
|
||||
}
|
||||
|
32
strlist.h
Normal file
32
strlist.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef VSF_STRLIST_H
|
||||
#define VSF_STRLIST_H
|
||||
|
||||
/* Forward declarations */
|
||||
struct mystr;
|
||||
struct mystr_list_node;
|
||||
|
||||
struct mystr_list
|
||||
{
|
||||
unsigned int PRIVATE_HANDS_OFF_alloc_len;
|
||||
unsigned int PRIVATE_HANDS_OFF_list_len;
|
||||
struct mystr_list_node* PRIVATE_HANDS_OFF_p_nodes;
|
||||
};
|
||||
|
||||
#define INIT_STRLIST \
|
||||
{ 0, 0, (void*)0 }
|
||||
|
||||
void str_list_free(struct mystr_list* p_list);
|
||||
|
||||
void str_list_add(struct mystr_list* p_list, const struct mystr* p_str,
|
||||
const struct mystr* p_sort_key_str);
|
||||
void str_list_sort(struct mystr_list* p_list, int reverse);
|
||||
|
||||
int str_list_get_length(const struct mystr_list* p_list);
|
||||
int str_list_contains_str(const struct mystr_list* p_list,
|
||||
const struct mystr* p_str);
|
||||
|
||||
const struct mystr* str_list_get_pstr(const struct mystr_list* p_list,
|
||||
unsigned int index);
|
||||
|
||||
#endif /* VSF_STRLIST_H */
|
||||
|
903
sysdeputil.c
Normal file
903
sysdeputil.c
Normal file
@ -0,0 +1,903 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* sysdeputil.c
|
||||
*
|
||||
* Highly system dependent utilities - e.g. authentication, capabilities.
|
||||
*/
|
||||
|
||||
#include "sysdeputil.h"
|
||||
#include "str.h"
|
||||
#include "sysutil.h"
|
||||
#include "utility.h"
|
||||
#include "secbuf.h"
|
||||
#include "defs.h"
|
||||
#include "tunables.h"
|
||||
|
||||
/* For Linux, this adds nothing :-) */
|
||||
#include "port/porting_junk.h"
|
||||
|
||||
/* For INT_MAX */
|
||||
#include <limits.h>
|
||||
|
||||
/* For fd passing */
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
/* For FreeBSD */
|
||||
#include <sys/param.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
/* Configuration.. here are the possibilities */
|
||||
#undef VSF_SYSDEP_HAVE_CAPABILITIES
|
||||
#undef VSF_SYSDEP_HAVE_SETKEEPCAPS
|
||||
#undef VSF_SYSDEP_HAVE_LINUX_SENDFILE
|
||||
#undef VSF_SYSDEP_HAVE_FREEBSD_SENDFILE
|
||||
#undef VSF_SYSDEP_HAVE_HPUX_SENDFILE
|
||||
#undef VSF_SYSDEP_HAVE_SETPROCTITLE
|
||||
#undef VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
|
||||
#undef VSF_SYSDEP_HAVE_HPUX_SETPROCTITLE
|
||||
#undef VSF_SYSDEP_HAVE_MAP_ANON
|
||||
#undef VSF_SYSDEP_NEED_OLD_FD_PASSING
|
||||
#define VSF_SYSDEP_HAVE_PAM
|
||||
#define VSF_SYSDEP_HAVE_SHADOW
|
||||
#define VSF_SYSDEP_HAVE_USERSHELL
|
||||
|
||||
/* BEGIN config */
|
||||
#ifdef __linux__
|
||||
#define VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
|
||||
#include <linux/version.h>
|
||||
#if defined(LINUX_VERSION_CODE) && defined(KERNEL_VERSION)
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0))
|
||||
#define VSF_SYSDEP_HAVE_CAPABILITIES
|
||||
#define VSF_SYSDEP_HAVE_LINUX_SENDFILE
|
||||
#include <sys/prctl.h>
|
||||
#ifdef PR_SET_KEEPCAPS
|
||||
#define VSF_SYSDEP_HAVE_SETKEEPCAPS
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (defined(__FreeBSD__) && __FreeBSD__ >= 3)
|
||||
#define VSF_SYSDEP_HAVE_FREEBSD_SENDFILE
|
||||
#define VSF_SYSDEP_HAVE_SETPROCTITLE
|
||||
#endif
|
||||
|
||||
#ifdef __hpux
|
||||
#include <sys/socket.h>
|
||||
#ifdef SF_DISCONNECT
|
||||
#define VSF_SYSDEP_HAVE_HPUX_SENDFILE
|
||||
#endif
|
||||
#include <sys/param.h>
|
||||
#include <sys/pstat.h>
|
||||
#ifdef PSTAT_SETCMD
|
||||
#define VSF_SYSDEP_HAVE_HPUX_SETPROCTITLE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#ifdef MAP_ANON
|
||||
#define VSF_SYSDEP_HAVE_MAP_ANON
|
||||
#endif
|
||||
|
||||
#ifdef __sgi
|
||||
#undef VSF_SYSDEP_HAVE_USERSHELL
|
||||
#endif
|
||||
|
||||
#if (defined(__sgi) || defined(__hpux))
|
||||
#define VSF_SYSDEP_NEED_OLD_FD_PASSING
|
||||
#endif
|
||||
/* END config */
|
||||
|
||||
/* PAM support - we include our own dummy version if the system lacks this */
|
||||
#include <security/pam_appl.h>
|
||||
|
||||
/* No PAM? Try getspnam() with a getpwnam() fallback */
|
||||
#ifndef VSF_SYSDEP_HAVE_PAM
|
||||
/* This may hit our own "dummy" include and undef VSF_SYSDEP_HAVE_SHADOW */
|
||||
#include <shadow.h>
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef VSF_SYSDEP_HAVE_CAPABILITIES
|
||||
#include <linux/capability.h>
|
||||
#include <errno.h>
|
||||
#include <syscall.h>
|
||||
_syscall2(int, capset, cap_user_header_t, header, const cap_user_data_t, data)
|
||||
/* Gross HACK to avoid warnings - linux headers overlap glibc headers */
|
||||
#undef __NFDBITS
|
||||
#undef __FDMASK
|
||||
#endif /* VSF_SYSDEP_HAVE_CAPABILITIES */
|
||||
|
||||
#ifdef VSF_SYSDEP_HAVE_LINUX_SENDFILE
|
||||
#include <sys/sendfile.h>
|
||||
#elif defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE)
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#elif defined(VSF_SYSDEP_HAVE_HPUX_SENDFILE)
|
||||
#include <sys/socket.h>
|
||||
#else /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */
|
||||
#include <unistd.h>
|
||||
#endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */
|
||||
|
||||
#ifdef VSF_SYSDEP_HAVE_SETPROCTITLE
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK
|
||||
extern char** environ;
|
||||
static unsigned int s_proctitle_space = 0;
|
||||
static int s_proctitle_inited = 0;
|
||||
static char* s_p_proctitle = 0;
|
||||
#endif
|
||||
|
||||
#ifndef VSF_SYSDEP_HAVE_MAP_ANON
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
static int s_zero_fd = -1;
|
||||
#endif
|
||||
|
||||
/* File private functions/variables */
|
||||
static int do_sendfile(const int out_fd, const int in_fd,
|
||||
long* p_offset, unsigned int num_send);
|
||||
static void vsf_sysutil_setproctitle_internal(const char* p_text);
|
||||
static struct mystr s_proctitle_prefix_str;
|
||||
|
||||
#ifndef VSF_SYSDEP_HAVE_PAM
|
||||
int
|
||||
vsf_sysdep_check_auth(const struct mystr* p_user_str,
|
||||
const struct mystr* p_pass_str,
|
||||
const struct mystr* p_remote_host)
|
||||
{
|
||||
const char* p_shell;
|
||||
const char* p_crypted;
|
||||
(void) p_remote_host;
|
||||
const struct passwd* p_pwd = getpwnam(str_getbuf(p_user_str));
|
||||
if (p_pwd == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#ifdef VSF_SYSDEP_HAVE_USERSHELL
|
||||
while ((p_shell = getusershell()) != NULL)
|
||||
{
|
||||
if (!vsf_sysutil_strcmp(p_shell, p_pwd->pw_shell))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
endusershell();
|
||||
if (p_shell == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#ifdef VSF_SYSDEP_HAVE_SHADOW
|
||||
{
|
||||
const struct spwd* p_spwd = getspnam(str_getbuf(p_user_str));
|
||||
if (p_spwd != NULL)
|
||||
{
|
||||
long curr_time;
|
||||
int days;
|
||||
vsf_sysutil_update_cached_time();
|
||||
curr_time = vsf_sysutil_get_cached_time_sec();
|
||||
days = curr_time / (60 * 60 * 24);
|
||||
if (p_spwd->sp_expire > 0 && p_spwd->sp_expire < days)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (p_spwd->sp_lstchg > 0 && p_spwd->sp_max > 0 &&
|
||||
p_spwd->sp_lstchg + p_spwd->sp_max < days)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
p_crypted = crypt(str_getbuf(p_pass_str), p_spwd->sp_pwdp);
|
||||
if (!vsf_sysutil_strcmp(p_crypted, p_spwd->sp_pwdp))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* VSF_SYSDEP_HAVE_SHADOW */
|
||||
p_crypted = crypt(str_getbuf(p_pass_str), p_pwd->pw_passwd);
|
||||
if (!vsf_sysutil_strcmp(p_crypted, p_pwd->pw_passwd))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* VSF_SYSDEP_HAVE_PAM */
|
||||
|
||||
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);
|
||||
|
||||
int
|
||||
vsf_sysdep_check_auth(const struct mystr* p_user_str,
|
||||
const struct mystr* p_pass_str,
|
||||
const struct mystr* p_remote_host)
|
||||
{
|
||||
int retval;
|
||||
pam_handle_t* pamh = 0;
|
||||
struct pam_conv the_conv =
|
||||
{
|
||||
&pam_conv_func,
|
||||
0
|
||||
};
|
||||
str_copy(&s_pword_str, p_pass_str);
|
||||
retval = pam_start(tunable_pam_service_name,
|
||||
str_getbuf(p_user_str), &the_conv, &pamh);
|
||||
if (retval != PAM_SUCCESS)
|
||||
{
|
||||
pam_end(pamh, 0);
|
||||
return 0;
|
||||
}
|
||||
#ifdef PAM_RHOST
|
||||
retval = pam_set_item(pamh, PAM_RHOST, str_getbuf(p_remote_host));
|
||||
if (retval != PAM_SUCCESS)
|
||||
{
|
||||
pam_end(pamh, 0);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
retval = pam_authenticate(pamh, 0);
|
||||
if (retval != PAM_SUCCESS)
|
||||
{
|
||||
pam_end(pamh, 0);
|
||||
return 0;
|
||||
}
|
||||
retval = pam_acct_mgmt(pamh, 0);
|
||||
if (retval != PAM_SUCCESS)
|
||||
{
|
||||
pam_end(pamh, 0);
|
||||
return 0;
|
||||
}
|
||||
retval = pam_setcred(pamh, PAM_ESTABLISH_CRED);
|
||||
if (retval != PAM_SUCCESS)
|
||||
{
|
||||
pam_end(pamh, 0);
|
||||
return 0;
|
||||
}
|
||||
retval = pam_end(pamh, PAM_SUCCESS);
|
||||
if (retval != PAM_SUCCESS)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/* It worked, cool */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
pam_conv_func(int nmsg, const struct pam_message** p_msg,
|
||||
struct pam_response** p_reply, void* p_addata)
|
||||
{
|
||||
int i;
|
||||
struct pam_response* p_resps = 0;
|
||||
(void) p_addata;
|
||||
if (nmsg < 0)
|
||||
{
|
||||
bug("dodgy nmsg in pam_conv_func");
|
||||
}
|
||||
/* XXX sometimes leaks */
|
||||
p_resps = vsf_sysutil_malloc(sizeof(struct pam_response) * nmsg);
|
||||
if (p_resps == 0)
|
||||
{
|
||||
return PAM_CONV_ERR;
|
||||
}
|
||||
for (i=0; i<nmsg; i++)
|
||||
{
|
||||
switch (p_msg[i]->msg_style)
|
||||
{
|
||||
case PAM_PROMPT_ECHO_ON:
|
||||
return PAM_CONV_ERR;
|
||||
break;
|
||||
case PAM_PROMPT_ECHO_OFF:
|
||||
p_resps[i].resp_retcode = PAM_SUCCESS;
|
||||
p_resps[i].resp = (char*) str_strdup(&s_pword_str);
|
||||
break;
|
||||
case PAM_TEXT_INFO:
|
||||
case PAM_ERROR_MSG:
|
||||
p_resps[i].resp_retcode = PAM_SUCCESS;
|
||||
p_resps[i].resp = 0;
|
||||
break;
|
||||
default:
|
||||
return PAM_CONV_ERR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*p_reply = p_resps;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
#endif /* VSF_SYSDEP_HAVE_PAM */
|
||||
|
||||
/* Capabilities support (or lack thereof) */
|
||||
void
|
||||
vsf_sysdep_keep_capabilities(void)
|
||||
{
|
||||
if (!vsf_sysdep_has_capabilities_as_non_root())
|
||||
{
|
||||
bug("asked to keep capabilities, but no support exists");
|
||||
}
|
||||
#ifdef VSF_SYSDEP_HAVE_SETKEEPCAPS
|
||||
{
|
||||
int retval = prctl(PR_SET_KEEPCAPS, 1);
|
||||
if (vsf_sysutil_retval_is_error(retval))
|
||||
{
|
||||
die("prctl");
|
||||
}
|
||||
}
|
||||
#endif /* VSF_SYSDEP_HAVE_SETKEEPCAPS */
|
||||
}
|
||||
#ifndef VSF_SYSDEP_HAVE_CAPABILITIES
|
||||
|
||||
int
|
||||
vsf_sysdep_has_capabilities(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
vsf_sysdep_has_capabilities_as_non_root(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
vsf_sysdep_adopt_capabilities(unsigned int caps)
|
||||
{
|
||||
(void) caps;
|
||||
bug("asked to adopt capabilities, but no support exists");
|
||||
}
|
||||
|
||||
#else /* VSF_SYSDEP_HAVE_CAPABILITIES */
|
||||
|
||||
int
|
||||
vsf_sysdep_has_capabilities(void)
|
||||
{
|
||||
/* Even though compiled with capabilities, the runtime system may lack them.
|
||||
* Also, RH7.0 kernel headers advertise a 2.4.0 box, but on a 2.2.x kernel!
|
||||
*/
|
||||
static int s_caps_checked;
|
||||
static int s_runtime_has_caps;
|
||||
if (!s_caps_checked)
|
||||
{
|
||||
/* EFAULT (EINVAL if page 0 mapped) vs. ENOSYS */
|
||||
int retval = capset(0, 0);
|
||||
if (!vsf_sysutil_retval_is_error(retval) ||
|
||||
vsf_sysutil_get_error() != kVSFSysUtilErrNOSYS)
|
||||
{
|
||||
s_runtime_has_caps = 1;
|
||||
}
|
||||
s_caps_checked = 1;
|
||||
}
|
||||
return s_runtime_has_caps;
|
||||
}
|
||||
|
||||
int
|
||||
vsf_sysdep_has_capabilities_as_non_root(void)
|
||||
{
|
||||
static int s_prctl_checked;
|
||||
static int s_runtime_prctl_works;
|
||||
if (!s_prctl_checked)
|
||||
{
|
||||
#ifdef VSF_SYSDEP_HAVE_SETKEEPCAPS
|
||||
/* Clarity: note embedded call to prctl() syscall */
|
||||
if (!vsf_sysutil_retval_is_error(prctl(PR_SET_KEEPCAPS, 0)))
|
||||
{
|
||||
s_runtime_prctl_works = 1;
|
||||
}
|
||||
#endif /* VSF_SYSDEP_HAVE_SETKEEPCAPS */
|
||||
s_prctl_checked = 1;
|
||||
}
|
||||
return s_runtime_prctl_works;
|
||||
}
|
||||
|
||||
void
|
||||
vsf_sysdep_adopt_capabilities(unsigned int caps)
|
||||
{
|
||||
/* n.b. yes I know I should be using libcap!! */
|
||||
int retval;
|
||||
struct __user_cap_header_struct cap_head;
|
||||
struct __user_cap_data_struct cap_data;
|
||||
__u32 cap_mask = 0;
|
||||
if (!caps)
|
||||
{
|
||||
bug("asked to adopt no capabilities");
|
||||
}
|
||||
vsf_sysutil_memclr(&cap_head, sizeof(cap_head));
|
||||
vsf_sysutil_memclr(&cap_data, sizeof(cap_data));
|
||||
cap_head.version = _LINUX_CAPABILITY_VERSION;
|
||||
cap_head.pid = 0;
|
||||
if (caps & kCapabilityCAP_CHOWN)
|
||||
{
|
||||
cap_mask |= (1 << CAP_CHOWN);
|
||||
}
|
||||
if (caps & kCapabilityCAP_NET_BIND_SERVICE)
|
||||
{
|
||||
cap_mask |= (1 << CAP_NET_BIND_SERVICE);
|
||||
}
|
||||
cap_data.effective = cap_data.permitted = cap_mask;
|
||||
cap_data.inheritable = 0;
|
||||
retval = capset(&cap_head, &cap_data);
|
||||
if (retval != 0)
|
||||
{
|
||||
die("capset");
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* VSF_SYSDEP_HAVE_CAPABILITIES */
|
||||
|
||||
int
|
||||
vsf_sysutil_sendfile(const int out_fd, const int in_fd,
|
||||
unsigned long* p_offset, unsigned long num_send,
|
||||
unsigned int max_chunk)
|
||||
{
|
||||
/* Grr - why is off_t signed? */
|
||||
long real_offset = *p_offset;
|
||||
if (real_offset < 0)
|
||||
{
|
||||
die("invalid offset in vsf_sysutil_sendfile");
|
||||
}
|
||||
while (num_send > 0)
|
||||
{
|
||||
int retval;
|
||||
unsigned int send_this_time;
|
||||
/* For 64-bit platforms */
|
||||
if (num_send > INT_MAX)
|
||||
{
|
||||
send_this_time = INT_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
send_this_time = (unsigned int) num_send;
|
||||
}
|
||||
if (max_chunk != 0 && send_this_time > max_chunk)
|
||||
{
|
||||
send_this_time = max_chunk;
|
||||
}
|
||||
retval = do_sendfile(out_fd, in_fd, &real_offset, send_this_time);
|
||||
if (real_offset < 0)
|
||||
{
|
||||
die("invalid offset returned in vsf_sysutil_sendfile");
|
||||
}
|
||||
*p_offset = real_offset;
|
||||
if (vsf_sysutil_retval_is_error(retval) || retval == 0)
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
num_send -= (unsigned long) retval;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_sendfile(const int out_fd, const int in_fd,
|
||||
long* p_offset, unsigned int num_send)
|
||||
{
|
||||
/* Probably should one day be shared with instance in ftpdataio.c */
|
||||
static char* p_recvbuf;
|
||||
unsigned int total_written = 0;
|
||||
int retval;
|
||||
#if defined(VSF_SYSDEP_HAVE_LINUX_SENDFILE) || \
|
||||
defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE) || \
|
||||
defined(VSF_SYSDEP_HAVE_HPUX_SENDFILE)
|
||||
{
|
||||
static int s_sendfile_checked;
|
||||
static int s_runtime_sendfile_works;
|
||||
if (!s_sendfile_checked || s_runtime_sendfile_works)
|
||||
{
|
||||
do
|
||||
{
|
||||
#ifdef VSF_SYSDEP_HAVE_LINUX_SENDFILE
|
||||
retval = sendfile(out_fd, in_fd, p_offset, num_send);
|
||||
#elif defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE)
|
||||
{
|
||||
off_t written = 0;
|
||||
retval = sendfile(in_fd, out_fd, *p_offset, num_send, NULL,
|
||||
&written, 0);
|
||||
/* Translate to Linux-like retval */
|
||||
if (written > 0)
|
||||
{
|
||||
retval = (int) written;
|
||||
*p_offset += retval;
|
||||
}
|
||||
}
|
||||
#else /* must be VSF_SYSDEP_HAVE_HPUX_SENDFILE */
|
||||
{
|
||||
retval = sendfile(out_fd, in_fd, *p_offset, num_send, NULL, 0);
|
||||
/* Translate to Linux-like retval */
|
||||
if (retval > 0)
|
||||
{
|
||||
*p_offset += retval;
|
||||
}
|
||||
}
|
||||
#endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */
|
||||
vsf_sysutil_check_pending_actions(kVSFSysUtilIO, retval, out_fd);
|
||||
}
|
||||
while (vsf_sysutil_retval_is_error(retval) &&
|
||||
vsf_sysutil_get_error() == kVSFSysUtilErrINTR);
|
||||
if (!s_sendfile_checked)
|
||||
{
|
||||
s_sendfile_checked = 1;
|
||||
if (!vsf_sysutil_retval_is_error(retval) ||
|
||||
vsf_sysutil_get_error() != kVSFSysUtilErrNOSYS)
|
||||
{
|
||||
s_runtime_sendfile_works = 1;
|
||||
}
|
||||
}
|
||||
if (s_runtime_sendfile_works)
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
/* Fall thru to normal implementation. We won't check again. */
|
||||
}
|
||||
}
|
||||
#endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE || VSF_SYSDEP_HAVE_FREEBSD_SENDFILE */
|
||||
if (p_recvbuf == 0)
|
||||
{
|
||||
vsf_secbuf_alloc(&p_recvbuf, VSFTP_DATA_BUFSIZE);
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
unsigned int num_read;
|
||||
unsigned int num_written;
|
||||
unsigned int num_read_this_time = VSFTP_DATA_BUFSIZE;
|
||||
if (num_read_this_time > num_send)
|
||||
{
|
||||
num_read_this_time = num_send;
|
||||
}
|
||||
retval = vsf_sysutil_read(in_fd, p_recvbuf, num_read_this_time);
|
||||
if (retval < 0)
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
else if (retval == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
num_read = (unsigned int) retval;
|
||||
*p_offset += num_read;
|
||||
retval = vsf_sysutil_write_loop(out_fd, p_recvbuf, num_read);
|
||||
if (retval < 0)
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
num_written = (unsigned int) retval;
|
||||
total_written += num_written;
|
||||
if (num_written != num_read)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (num_written > num_send)
|
||||
{
|
||||
bug("num_written bigger than num_send in do_sendfile");
|
||||
}
|
||||
num_send -= num_written;
|
||||
if (num_send == 0)
|
||||
{
|
||||
/* Bingo! */
|
||||
return total_written;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vsf_sysutil_set_proctitle_prefix(const struct mystr* p_str)
|
||||
{
|
||||
str_copy(&s_proctitle_prefix_str, p_str);
|
||||
}
|
||||
|
||||
/* This delegation is common to all setproctitle() implementations */
|
||||
void
|
||||
vsf_sysutil_setproctitle_str(const struct mystr* p_str)
|
||||
{
|
||||
vsf_sysutil_setproctitle(str_getbuf(p_str));
|
||||
}
|
||||
|
||||
void
|
||||
vsf_sysutil_setproctitle(const char* p_text)
|
||||
{
|
||||
struct mystr proctitle_str = INIT_MYSTR;
|
||||
str_copy(&proctitle_str, &s_proctitle_prefix_str);
|
||||
if (!str_isempty(&proctitle_str))
|
||||
{
|
||||
str_append_text(&proctitle_str, ": ");
|
||||
}
|
||||
str_append_text(&proctitle_str, p_text);
|
||||
vsf_sysutil_setproctitle_internal(str_getbuf(&proctitle_str));
|
||||
str_free(&proctitle_str);
|
||||
}
|
||||
|
||||
#ifdef VSF_SYSDEP_HAVE_SETPROCTITLE
|
||||
void
|
||||
vsf_sysutil_setproctitle_init(int argc, const char* argv[])
|
||||
{
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
}
|
||||
|
||||
void
|
||||
vsf_sysutil_setproctitle_internal(const char* p_buf)
|
||||
{
|
||||
setproctitle("%s", p_buf);
|
||||
}
|
||||
#elif defined(VSF_SYSDEP_HAVE_HPUX_SETPROCTITLE)
|
||||
void
|
||||
vsf_sysutil_setproctitle_init(int argc, const char* argv[])
|
||||
{
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
}
|
||||
|
||||
void
|
||||
vsf_sysutil_setproctitle_internal(const char* p_buf)
|
||||
{
|
||||
struct mystr proctitle_str = INIT_MYSTR;
|
||||
union pstun p;
|
||||
str_alloc_text(&proctitle_str, "vsftpd: ");
|
||||
str_append_text(&proctitle_str, p_buf);
|
||||
p.pst_command = str_getbuf(&proctitle_str);
|
||||
pstat(PSTAT_SETCMD, p, 0, 0, 0);
|
||||
str_free(&proctitle_str);
|
||||
}
|
||||
#elif defined(VSF_SYSDEP_TRY_LINUX_SETPROCTITLE_HACK)
|
||||
void
|
||||
vsf_sysutil_setproctitle_init(int argc, const char* argv[])
|
||||
{
|
||||
int i;
|
||||
char** p_env = environ;
|
||||
if (s_proctitle_inited)
|
||||
{
|
||||
bug("vsf_sysutil_setproctitle_init called twice");
|
||||
}
|
||||
s_proctitle_inited = 1;
|
||||
if (argv[0] == 0)
|
||||
{
|
||||
die("no argv[0] in vsf_sysutil_setproctitle_init");
|
||||
}
|
||||
for (i=0; i<argc; i++)
|
||||
{
|
||||
s_proctitle_space += vsf_sysutil_strlen(argv[i]) + 1;
|
||||
if (i > 0)
|
||||
{
|
||||
argv[i] = 0;
|
||||
}
|
||||
}
|
||||
while (*p_env != 0)
|
||||
{
|
||||
s_proctitle_space += vsf_sysutil_strlen(*p_env) + 1;
|
||||
p_env++;
|
||||
}
|
||||
/* Oops :-) */
|
||||
environ = 0;
|
||||
s_p_proctitle = (char*) argv[0];
|
||||
vsf_sysutil_memclr(s_p_proctitle, s_proctitle_space);
|
||||
}
|
||||
|
||||
void
|
||||
vsf_sysutil_setproctitle_internal(const char* p_buf)
|
||||
{
|
||||
struct mystr proctitle_str = INIT_MYSTR;
|
||||
unsigned int to_copy;
|
||||
if (!s_proctitle_inited)
|
||||
{
|
||||
bug("vsf_sysutil_setproctitle: not initialized");
|
||||
}
|
||||
vsf_sysutil_memclr(s_p_proctitle, s_proctitle_space);
|
||||
if (s_proctitle_space < 32)
|
||||
{
|
||||
return;
|
||||
}
|
||||
str_alloc_text(&proctitle_str, "vsftpd: ");
|
||||
str_append_text(&proctitle_str, p_buf);
|
||||
to_copy = str_getlen(&proctitle_str);
|
||||
if (to_copy > s_proctitle_space - 1)
|
||||
{
|
||||
to_copy = s_proctitle_space - 1;
|
||||
}
|
||||
vsf_sysutil_memcpy(s_p_proctitle, str_getbuf(&proctitle_str), to_copy);
|
||||
str_free(&proctitle_str);
|
||||
s_p_proctitle[to_copy] = '\0';
|
||||
}
|
||||
#else /* VSF_SYSDEP_HAVE_SETPROCTITLE */
|
||||
void
|
||||
vsf_sysutil_setproctitle_init(int argc, const char* argv[])
|
||||
{
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
}
|
||||
|
||||
void
|
||||
vsf_sysutil_setproctitle_internal(const char* p_buf)
|
||||
{
|
||||
(void) p_buf;
|
||||
}
|
||||
#endif /* VSF_SYSDEP_HAVE_SETPROCTITLE */
|
||||
|
||||
#ifdef VSF_SYSDEP_HAVE_MAP_ANON
|
||||
void
|
||||
vsf_sysutil_map_anon_pages_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void*
|
||||
vsf_sysutil_map_anon_pages(unsigned int length)
|
||||
{
|
||||
char* retval = mmap(0, length, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
if (retval == MAP_FAILED)
|
||||
{
|
||||
die("mmap");
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
#else /* VSF_SYSDEP_HAVE_MAP_ANON */
|
||||
void
|
||||
vsf_sysutil_map_anon_pages_init(void)
|
||||
{
|
||||
if (s_zero_fd != -1)
|
||||
{
|
||||
bug("vsf_sysutil_map_anon_pages_init called twice");
|
||||
}
|
||||
s_zero_fd = open("/dev/zero", O_RDWR);
|
||||
if (s_zero_fd < 0)
|
||||
{
|
||||
die("could not open /dev/zero");
|
||||
}
|
||||
}
|
||||
|
||||
void*
|
||||
vsf_sysutil_map_anon_pages(unsigned int length)
|
||||
{
|
||||
char* retval = mmap(0, length, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE, s_zero_fd, 0);
|
||||
if (retval == MAP_FAILED)
|
||||
{
|
||||
die("mmap");
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
#endif /* VSF_SYSDEP_HAVE_MAP_ANON */
|
||||
|
||||
#ifndef VSF_SYSDEP_NEED_OLD_FD_PASSING
|
||||
|
||||
void
|
||||
vsf_sysutil_send_fd(int sock_fd, int send_fd)
|
||||
{
|
||||
int retval;
|
||||
struct msghdr msg;
|
||||
struct cmsghdr* p_cmsg;
|
||||
struct iovec vec;
|
||||
char cmsgbuf[CMSG_SPACE(sizeof(send_fd))];
|
||||
int* p_fds;
|
||||
char sendchar = 0;
|
||||
msg.msg_control = cmsgbuf;
|
||||
msg.msg_controllen = sizeof(cmsgbuf);
|
||||
p_cmsg = CMSG_FIRSTHDR(&msg);
|
||||
p_cmsg->cmsg_level = SOL_SOCKET;
|
||||
p_cmsg->cmsg_type = SCM_RIGHTS;
|
||||
p_cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd));
|
||||
p_fds = (int*)CMSG_DATA(p_cmsg);
|
||||
*p_fds = send_fd;
|
||||
msg.msg_controllen = p_cmsg->cmsg_len;
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &vec;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_flags = 0;
|
||||
/* "To pass file descriptors or credentials you need to send/read at
|
||||
* least on byte" (man 7 unix)
|
||||
*/
|
||||
vec.iov_base = &sendchar;
|
||||
vec.iov_len = sizeof(sendchar);
|
||||
retval = sendmsg(sock_fd, &msg, 0);
|
||||
if (retval != 1)
|
||||
{
|
||||
die("sendmsg");
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
vsf_sysutil_recv_fd(const int sock_fd)
|
||||
{
|
||||
int retval;
|
||||
struct msghdr msg;
|
||||
char recvchar;
|
||||
struct iovec vec;
|
||||
int recv_fd;
|
||||
char cmsgbuf[CMSG_SPACE(sizeof(recv_fd))];
|
||||
struct cmsghdr* p_cmsg;
|
||||
int* p_fd;
|
||||
vec.iov_base = &recvchar;
|
||||
vec.iov_len = sizeof(recvchar);
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &vec;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = cmsgbuf;
|
||||
msg.msg_controllen = sizeof(cmsgbuf);
|
||||
msg.msg_flags = 0;
|
||||
/* In case something goes wrong, set the fd to -1 before the syscall */
|
||||
p_fd = (int*)CMSG_DATA(CMSG_FIRSTHDR(&msg));
|
||||
*p_fd = -1;
|
||||
retval = recvmsg(sock_fd, &msg, 0);
|
||||
if (retval != 1)
|
||||
{
|
||||
die("recvmsg");
|
||||
}
|
||||
p_cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (p_cmsg == NULL)
|
||||
{
|
||||
die("no passed fd");
|
||||
}
|
||||
/* We used to verify the returned cmsg_level, cmsg_type and cmsg_len here,
|
||||
* but Linux 2.0 totally uselessly fails to fill these in.
|
||||
*/
|
||||
p_fd = (int*)CMSG_DATA(p_cmsg);
|
||||
recv_fd = *p_fd;
|
||||
if (recv_fd == -1)
|
||||
{
|
||||
die("no passed fd");
|
||||
}
|
||||
return recv_fd;
|
||||
}
|
||||
|
||||
#else /* !VSF_SYSDEP_NEED_OLD_FD_PASSING */
|
||||
|
||||
void
|
||||
vsf_sysutil_send_fd(int sock_fd, int send_fd)
|
||||
{
|
||||
int retval;
|
||||
char send_char = 0;
|
||||
struct msghdr msg;
|
||||
struct iovec vec;
|
||||
vec.iov_base = &send_char;
|
||||
vec.iov_len = 1;
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &vec;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_accrights = (caddr_t) &send_fd;
|
||||
msg.msg_accrightslen = sizeof(send_fd);
|
||||
retval = sendmsg(sock_fd, &msg, 0);
|
||||
if (retval != 1)
|
||||
{
|
||||
die("sendmsg");
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
vsf_sysutil_recv_fd(int sock_fd)
|
||||
{
|
||||
int retval;
|
||||
struct msghdr msg;
|
||||
struct iovec vec;
|
||||
char recv_char;
|
||||
int recv_fd = -1;
|
||||
vec.iov_base = &recv_char;
|
||||
vec.iov_len = 1;
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &vec;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_accrights = (caddr_t) &recv_fd;
|
||||
msg.msg_accrightslen = sizeof(recv_fd);
|
||||
retval = recvmsg(sock_fd, &msg, 0);
|
||||
if (retval != 1)
|
||||
{
|
||||
die("recvmsg");
|
||||
}
|
||||
if (recv_fd == -1)
|
||||
{
|
||||
die("no passed fd");
|
||||
}
|
||||
return recv_fd;
|
||||
}
|
||||
|
||||
#endif /* !VSF_SYSDEP_NEED_OLD_FD_PASSING */
|
||||
|
55
sysdeputil.h
Normal file
55
sysdeputil.h
Normal file
@ -0,0 +1,55 @@
|
||||
#ifndef VSF_SYSDEPUTIL_H
|
||||
#define VSF_SYSDEPUTIL_H
|
||||
|
||||
/* VSF_SYSDEPUTIL_H:
|
||||
* Support for highly system dependent features, and querying for support
|
||||
* or lack thereof
|
||||
* TODO: document functions!
|
||||
*/
|
||||
|
||||
struct mystr;
|
||||
|
||||
/* Authentication of local users */
|
||||
/* Return 0 for fail, 1 for success */
|
||||
int vsf_sysdep_check_auth(const struct mystr* p_user,
|
||||
const struct mystr* p_pass,
|
||||
const struct mystr* p_remote_host);
|
||||
|
||||
/* Support for fine grained privilege (capabilities) */
|
||||
int vsf_sysdep_has_capabilities(void);
|
||||
int vsf_sysdep_has_capabilities_as_non_root(void);
|
||||
void vsf_sysdep_keep_capabilities(void);
|
||||
enum ESysdepCapabilities
|
||||
{
|
||||
kCapabilityCAP_CHOWN = 1,
|
||||
kCapabilityCAP_NET_BIND_SERVICE = 2
|
||||
/* NOTE - next one will be 4, this is a bitfield */
|
||||
};
|
||||
void vsf_sysdep_adopt_capabilities(unsigned int caps);
|
||||
|
||||
/* Support for sendfile(), Linux-like interface. Collapses to a read/write
|
||||
* loop under the covers if the target system lacks support.
|
||||
*/
|
||||
int vsf_sysutil_sendfile(const int out_fd, const int in_fd,
|
||||
unsigned long* p_offset, unsigned long num_send,
|
||||
unsigned int max_chunk);
|
||||
|
||||
/* Support for changing the process name as reported by the operating system.
|
||||
* A useful status monitor. NOTE - we don't guarantee that this call will
|
||||
* have any effect.
|
||||
*/
|
||||
void vsf_sysutil_setproctitle_init(int argc, const char* argv[]);
|
||||
void vsf_sysutil_setproctitle(const char* p_text);
|
||||
void vsf_sysutil_setproctitle_str(const struct mystr* p_str);
|
||||
void vsf_sysutil_set_proctitle_prefix(const struct mystr* p_str);
|
||||
|
||||
/* For now, maps read/write private pages. API to be extended.. */
|
||||
void vsf_sysutil_map_anon_pages_init(void);
|
||||
void* vsf_sysutil_map_anon_pages(unsigned int length);
|
||||
|
||||
/* File descriptor passing/receiving */
|
||||
void vsf_sysutil_send_fd(int sock_fd, int send_fd);
|
||||
int vsf_sysutil_recv_fd(int sock_fd);
|
||||
|
||||
#endif /* VSF_SYSDEPUTIL_H */
|
||||
|
168
sysstr.c
Normal file
168
sysstr.c
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* sysstr.c
|
||||
*
|
||||
* This file basically wraps system functions so that we can deal in our
|
||||
* nice abstracted string buffer objects.
|
||||
*/
|
||||
|
||||
#include "sysstr.h"
|
||||
#include "str.h"
|
||||
#include "secbuf.h"
|
||||
#include "sysutil.h"
|
||||
#include "defs.h"
|
||||
#include "utility.h"
|
||||
|
||||
void
|
||||
str_getcwd(struct mystr* p_str)
|
||||
{
|
||||
static char* p_getcwd_buf;
|
||||
char* p_ret;
|
||||
if (p_getcwd_buf == 0)
|
||||
{
|
||||
vsf_secbuf_alloc(&p_getcwd_buf, VSFTP_PATH_MAX);
|
||||
}
|
||||
/* In case getcwd() fails */
|
||||
str_empty(p_str);
|
||||
p_ret = vsf_sysutil_getcwd(p_getcwd_buf, VSFTP_PATH_MAX);
|
||||
if (p_ret != 0)
|
||||
{
|
||||
str_alloc_text(p_str, p_getcwd_buf);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
str_write_loop(const struct mystr* p_str, const int fd)
|
||||
{
|
||||
return vsf_sysutil_write_loop(fd, str_getbuf(p_str), str_getlen(p_str));
|
||||
}
|
||||
|
||||
int
|
||||
str_mkdir(const struct mystr* p_str, const unsigned int mode)
|
||||
{
|
||||
return vsf_sysutil_mkdir(str_getbuf(p_str), mode);
|
||||
}
|
||||
|
||||
int
|
||||
str_rmdir(const struct mystr* p_str)
|
||||
{
|
||||
return vsf_sysutil_rmdir(str_getbuf(p_str));
|
||||
}
|
||||
|
||||
int
|
||||
str_unlink(const struct mystr* p_str)
|
||||
{
|
||||
return vsf_sysutil_unlink(str_getbuf(p_str));
|
||||
}
|
||||
|
||||
int
|
||||
str_chdir(const struct mystr* p_str)
|
||||
{
|
||||
return vsf_sysutil_chdir(str_getbuf(p_str));
|
||||
}
|
||||
|
||||
int
|
||||
str_open(const struct mystr* p_str, const enum EVSFSysStrOpenMode mode)
|
||||
{
|
||||
enum EVSFSysUtilOpenMode open_mode = kVSFSysStrOpenUnknown;
|
||||
switch (mode)
|
||||
{
|
||||
case kVSFSysStrOpenReadOnly:
|
||||
open_mode = kVSFSysUtilOpenReadOnly;
|
||||
break;
|
||||
default:
|
||||
bug("unknown mode value in str_open");
|
||||
break;
|
||||
}
|
||||
return vsf_sysutil_open_file(str_getbuf(p_str), open_mode);
|
||||
}
|
||||
|
||||
int
|
||||
str_stat(const struct mystr* p_str, struct vsf_sysutil_statbuf** p_ptr)
|
||||
{
|
||||
return vsf_sysutil_stat(str_getbuf(p_str), p_ptr);
|
||||
}
|
||||
|
||||
int
|
||||
str_lstat(const struct mystr* p_str, struct vsf_sysutil_statbuf** p_ptr)
|
||||
{
|
||||
return vsf_sysutil_lstat(str_getbuf(p_str), p_ptr);
|
||||
}
|
||||
|
||||
int
|
||||
str_create(const struct mystr* p_str)
|
||||
{
|
||||
return vsf_sysutil_create_file(str_getbuf(p_str));
|
||||
}
|
||||
|
||||
int
|
||||
str_create_overwrite(const struct mystr* p_str)
|
||||
{
|
||||
return vsf_sysutil_create_overwrite_file(str_getbuf(p_str));
|
||||
}
|
||||
|
||||
int
|
||||
str_create_append(const struct mystr* p_str)
|
||||
{
|
||||
return vsf_sysutil_create_or_open_file(str_getbuf(p_str), 0666);
|
||||
}
|
||||
|
||||
int
|
||||
str_chmod(const struct mystr* p_str, unsigned int mode)
|
||||
{
|
||||
return vsf_sysutil_chmod(str_getbuf(p_str), mode);
|
||||
}
|
||||
|
||||
int
|
||||
str_rename(const struct mystr* p_from_str, const struct mystr* p_to_str)
|
||||
{
|
||||
return vsf_sysutil_rename(str_getbuf(p_from_str), str_getbuf(p_to_str));
|
||||
}
|
||||
|
||||
struct vsf_sysutil_dir*
|
||||
str_opendir(const struct mystr* p_str)
|
||||
{
|
||||
return vsf_sysutil_opendir(str_getbuf(p_str));
|
||||
}
|
||||
|
||||
void
|
||||
str_next_dirent(struct mystr* p_filename_str, struct vsf_sysutil_dir* p_dir)
|
||||
{
|
||||
const char* p_filename = vsf_sysutil_next_dirent(p_dir);
|
||||
str_empty(p_filename_str);
|
||||
if (p_filename != 0)
|
||||
{
|
||||
str_alloc_text(p_filename_str, p_filename);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
str_readlink(struct mystr* p_str, const struct mystr* p_filename_str)
|
||||
{
|
||||
static char* p_readlink_buf;
|
||||
int retval;
|
||||
if (p_readlink_buf == 0)
|
||||
{
|
||||
vsf_secbuf_alloc(&p_readlink_buf, VSFTP_PATH_MAX);
|
||||
}
|
||||
/* In case readlink() fails */
|
||||
str_empty(p_str);
|
||||
/* Note: readlink(2) does not NULL terminate, but our wrapper does */
|
||||
retval = vsf_sysutil_readlink(str_getbuf(p_filename_str), p_readlink_buf,
|
||||
VSFTP_PATH_MAX);
|
||||
if (vsf_sysutil_retval_is_error(retval))
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
str_alloc_text(p_str, p_readlink_buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct vsf_sysutil_user*
|
||||
str_getpwnam(const struct mystr* p_user_str)
|
||||
{
|
||||
return vsf_sysutil_getpwnam(str_getbuf(p_user_str));
|
||||
}
|
||||
|
37
sysstr.h
Normal file
37
sysstr.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef VSF_SYSSTR_H
|
||||
#define VSF_SYSSTR_H
|
||||
|
||||
/* Forward declarations */
|
||||
struct mystr;
|
||||
struct vsf_sysutil_statbuf;
|
||||
struct vsf_sysutil_dir;
|
||||
struct vsf_sysutil_user;
|
||||
|
||||
void str_getcwd(struct mystr* p_str);
|
||||
int str_readlink(struct mystr* p_str, const struct mystr* p_filename_str);
|
||||
int str_write_loop(const struct mystr* p_str, const int fd);
|
||||
int str_mkdir(const struct mystr* p_str, const unsigned int mode);
|
||||
int str_rmdir(const struct mystr* p_str);
|
||||
int str_unlink(const struct mystr* p_str);
|
||||
int str_chdir(const struct mystr* p_str);
|
||||
enum EVSFSysStrOpenMode
|
||||
{
|
||||
kVSFSysStrOpenUnknown = 0,
|
||||
kVSFSysStrOpenReadOnly = 1
|
||||
};
|
||||
int str_open(const struct mystr* p_str, const enum EVSFSysStrOpenMode mode);
|
||||
int str_create_append(const struct mystr* p_str);
|
||||
int str_create(const struct mystr* p_str);
|
||||
int str_create_overwrite(const struct mystr* p_str);
|
||||
int str_chmod(const struct mystr* p_str, unsigned int mode);
|
||||
int str_stat(const struct mystr* p_str, struct vsf_sysutil_statbuf** p_ptr);
|
||||
int str_lstat(const struct mystr* p_str, struct vsf_sysutil_statbuf** p_ptr);
|
||||
int str_rename(const struct mystr* p_from_str, const struct mystr* p_to_str);
|
||||
struct vsf_sysutil_dir* str_opendir(const struct mystr* p_str);
|
||||
void str_next_dirent(struct mystr* p_filename_str,
|
||||
struct vsf_sysutil_dir* p_dir);
|
||||
|
||||
struct vsf_sysutil_user* str_getpwnam(const struct mystr* p_user_str);
|
||||
|
||||
#endif /* VSF_SYSSTR_H */
|
||||
|
285
sysutil.h
Normal file
285
sysutil.h
Normal file
@ -0,0 +1,285 @@
|
||||
#ifndef VSF_SYSUTIL_H
|
||||
#define VSF_SYSUTIL_H
|
||||
|
||||
/* TODO: these functions need proper documenting! */
|
||||
|
||||
/* Return value queries */
|
||||
int vsf_sysutil_retval_is_error(int retval);
|
||||
enum EVSFSysUtilError
|
||||
{
|
||||
kVSFSysUtilErrUnknown = 1,
|
||||
kVSFSysUtilErrADDRINUSE,
|
||||
kVSFSysUtilErrNOSYS,
|
||||
kVSFSysUtilErrINTR
|
||||
};
|
||||
enum EVSFSysUtilError vsf_sysutil_get_error(void);
|
||||
|
||||
/* Signal handling utility functions */
|
||||
enum EVSFSysUtilSignal
|
||||
{
|
||||
kVSFSysUtilSigALRM = 1,
|
||||
kVSFSysUtilSigTERM,
|
||||
kVSFSysUtilSigCHLD,
|
||||
kVSFSysUtilSigPIPE,
|
||||
kVSFSysUtilSigURG
|
||||
};
|
||||
enum EVSFSysUtilInterruptContext
|
||||
{
|
||||
kVSFSysUtilUnknown,
|
||||
kVSFSysUtilIO
|
||||
};
|
||||
typedef void (*vsf_sighandle_t)(void*);
|
||||
typedef void (*vsf_async_sighandle_t)(int);
|
||||
typedef void (*vsf_context_io_t)(int, int, void*);
|
||||
|
||||
void vsf_sysutil_install_null_sighandler(const enum EVSFSysUtilSignal sig);
|
||||
void vsf_sysutil_install_sighandler(const enum EVSFSysUtilSignal,
|
||||
vsf_sighandle_t handler, void* p_private);
|
||||
void vsf_sysutil_install_async_sighandler(const enum EVSFSysUtilSignal sig,
|
||||
vsf_async_sighandle_t handler);
|
||||
void vsf_sysutil_default_sig(const enum EVSFSysUtilSignal sig);
|
||||
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);
|
||||
|
||||
/* Alarm setting/clearing utility functions */
|
||||
void vsf_sysutil_set_alarm(const unsigned int trigger_seconds);
|
||||
void vsf_sysutil_clear_alarm(void);
|
||||
|
||||
/* Directory related things */
|
||||
char* vsf_sysutil_getcwd(char* p_dest, const unsigned int buf_size);
|
||||
int vsf_sysutil_mkdir(const char* p_dirname, const unsigned int mode);
|
||||
int vsf_sysutil_rmdir(const char* p_dirname);
|
||||
int vsf_sysutil_chdir(const char* p_dirname);
|
||||
int vsf_sysutil_rename(const char* p_from, const char* p_to);
|
||||
|
||||
struct vsf_sysutil_dir;
|
||||
struct vsf_sysutil_dir* vsf_sysutil_opendir(const char* p_dirname);
|
||||
void vsf_sysutil_closedir(struct vsf_sysutil_dir* p_dir);
|
||||
const char* vsf_sysutil_next_dirent(struct vsf_sysutil_dir* p_dir);
|
||||
|
||||
/* File create/open/close etc. */
|
||||
enum EVSFSysUtilOpenMode
|
||||
{
|
||||
kVSFSysUtilOpenReadOnly = 1,
|
||||
kVSFSysUtilOpenWriteOnly,
|
||||
kVSFSysUtilOpenReadWrite
|
||||
};
|
||||
int vsf_sysutil_open_file(const char* p_filename,
|
||||
const enum EVSFSysUtilOpenMode);
|
||||
/* Fails if file already exists */
|
||||
int vsf_sysutil_create_file(const char* p_filename);
|
||||
/* Overwrites if file already exists */
|
||||
int vsf_sysutil_create_overwrite_file(const char* p_filename);
|
||||
/* Creates file or appends if already exists */
|
||||
int vsf_sysutil_create_append_file(const char* p_filename);
|
||||
/* Creates or appends */
|
||||
int vsf_sysutil_create_or_open_file(const char* p_filename, unsigned int mode);
|
||||
void vsf_sysutil_close(int fd);
|
||||
int vsf_sysutil_close_failok(int fd);
|
||||
int vsf_sysutil_unlink(const char* p_dead);
|
||||
int vsf_sysutil_write_access(const char* p_filename);
|
||||
|
||||
/* Reading and writing */
|
||||
unsigned long vsf_sysutil_lseek_to(const int fd, unsigned long seek_pos);
|
||||
unsigned long vsf_sysutil_get_file_offset(const int file_fd);
|
||||
int vsf_sysutil_read(const int fd, void* p_buf, const unsigned int size);
|
||||
int vsf_sysutil_write(const int fd, const void* p_buf,
|
||||
const unsigned int size);
|
||||
/* Reading and writing, with handling of interrupted system calls and partial
|
||||
* reads/writes. Slightly more usable than the standard UNIX API!
|
||||
*/
|
||||
int vsf_sysutil_read_loop(const int fd, void* p_buf, unsigned int size);
|
||||
int vsf_sysutil_write_loop(const int fd, const void* p_buf, unsigned int size);
|
||||
|
||||
struct vsf_sysutil_statbuf;
|
||||
int vsf_sysutil_stat(const char* p_name, struct vsf_sysutil_statbuf** p_ptr);
|
||||
int vsf_sysutil_lstat(const char* p_name, struct vsf_sysutil_statbuf** p_ptr);
|
||||
void vsf_sysutil_fstat(int fd, struct vsf_sysutil_statbuf** p_ptr);
|
||||
void vsf_sysutil_dir_stat(const struct vsf_sysutil_dir* p_dir,
|
||||
struct vsf_sysutil_statbuf** p_ptr);
|
||||
int vsf_sysutil_statbuf_is_regfile(const struct vsf_sysutil_statbuf* p_stat);
|
||||
int vsf_sysutil_statbuf_is_symlink(const struct vsf_sysutil_statbuf* p_stat);
|
||||
int vsf_sysutil_statbuf_is_socket(const struct vsf_sysutil_statbuf* p_stat);
|
||||
int vsf_sysutil_statbuf_is_dir(const struct vsf_sysutil_statbuf* p_stat);
|
||||
unsigned long vsf_sysutil_statbuf_get_size(
|
||||
const struct vsf_sysutil_statbuf* p_stat);
|
||||
const char* vsf_sysutil_statbuf_get_perms(
|
||||
const struct vsf_sysutil_statbuf* p_stat);
|
||||
const char* vsf_sysutil_statbuf_get_date(
|
||||
const struct vsf_sysutil_statbuf* p_stat);
|
||||
const char* vsf_sysutil_statbuf_get_numeric_date(
|
||||
const struct vsf_sysutil_statbuf* p_stat);
|
||||
unsigned int vsf_sysutil_statbuf_get_links(
|
||||
const struct vsf_sysutil_statbuf* p_stat);
|
||||
int vsf_sysutil_statbuf_get_uid(const struct vsf_sysutil_statbuf* p_stat);
|
||||
int vsf_sysutil_statbuf_get_gid(const struct vsf_sysutil_statbuf* p_stat);
|
||||
int vsf_sysutil_statbuf_is_readable_other(
|
||||
const struct vsf_sysutil_statbuf* p_stat);
|
||||
const char* vsf_sysutil_statbuf_get_sortkey_mtime(
|
||||
const struct vsf_sysutil_statbuf* p_stat);
|
||||
|
||||
int vsf_sysutil_chmod(const char* p_filename, unsigned int mode);
|
||||
void vsf_sysutil_fchown(const int fd, const int uid, const int gid);
|
||||
int vsf_sysutil_readlink(const char* p_filename, char* p_dest,
|
||||
unsigned int bufsiz);
|
||||
|
||||
/* Get an exclusive lock, and blocks */
|
||||
int vsf_sysutil_lock_file(int fd);
|
||||
void vsf_sysutil_unlock_file(int fd);
|
||||
|
||||
/* Mapping/unmapping */
|
||||
enum EVSFSysUtilMapPermission
|
||||
{
|
||||
kVSFSysUtilMapProtReadOnly = 1,
|
||||
kVSFSysUtilMapProtNone
|
||||
};
|
||||
void vsf_sysutil_memprotect(void* p_addr, unsigned int len,
|
||||
const enum EVSFSysUtilMapPermission perm);
|
||||
void vsf_sysutil_memunmap(void* p_start, unsigned int length);
|
||||
|
||||
/* Memory allocating/freeing */
|
||||
void* vsf_sysutil_malloc(unsigned int size);
|
||||
void* vsf_sysutil_realloc(void* p_ptr, unsigned int size);
|
||||
void vsf_sysutil_free(void* p_ptr);
|
||||
|
||||
/* Process creation/exit/process handling */
|
||||
unsigned int vsf_sysutil_getpid(void);
|
||||
int vsf_sysutil_fork(void);
|
||||
void vsf_sysutil_exit(int exit_code);
|
||||
struct vsf_sysutil_wait_retval
|
||||
{
|
||||
int PRIVATE_HANDS_OFF_syscall_retval;
|
||||
int PRIVATE_HANDS_OFF_exit_status;
|
||||
};
|
||||
struct vsf_sysutil_wait_retval vsf_sysutil_wait(void);
|
||||
int vsf_sysutil_wait_get_retval(
|
||||
const struct vsf_sysutil_wait_retval* p_waitret);
|
||||
int vsf_sysutil_wait_exited_normally(
|
||||
const struct vsf_sysutil_wait_retval* p_waitret);
|
||||
int vsf_sysutil_wait_get_exitcode(
|
||||
const struct vsf_sysutil_wait_retval* p_waitret);
|
||||
|
||||
/* Various string functions */
|
||||
unsigned int vsf_sysutil_strlen(const char* p_text);
|
||||
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);
|
||||
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);
|
||||
int vsf_sysutil_atoi(const char* p_str);
|
||||
long vsf_sysutil_atol(const char* p_str);
|
||||
const char* vsf_sysutil_ulong_to_str(unsigned long the_ulong);
|
||||
const char* vsf_sysutil_double_to_str(double the_double);
|
||||
const char* vsf_sysutil_uint_to_octal(unsigned int the_uint);
|
||||
unsigned int vsf_sysutil_octal_to_uint(const char* p_str);
|
||||
int vsf_sysutil_toupper(int the_char);
|
||||
int vsf_sysutil_isspace(int the_char);
|
||||
int vsf_sysutil_isprint(int the_char);
|
||||
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;
|
||||
int socket_two;
|
||||
};
|
||||
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_ipv4port vsf_sysutil_ipv4port_from_int(unsigned int port);
|
||||
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);
|
||||
int vsf_sysutil_get_ipv4_sock(void);
|
||||
const struct vsf_sysutil_socketpair_retval
|
||||
vsf_sysutil_unix_dgram_socketpair(void);
|
||||
int vsf_sysutil_bind(int fd, const struct vsf_sysutil_sockaddr* p_sockptr);
|
||||
void vsf_sysutil_listen(int fd, const unsigned int backlog);
|
||||
void vsf_sysutil_getsockname(int fd, struct vsf_sysutil_sockaddr** p_sockptr);
|
||||
void vsf_sysutil_getpeername(int fd, struct vsf_sysutil_sockaddr** p_sockptr);
|
||||
int vsf_sysutil_accept_timeout(int fd, struct vsf_sysutil_sockaddr** p_sockptr,
|
||||
unsigned int wait_seconds);
|
||||
int vsf_sysutil_connect_timeout(int fd,
|
||||
const struct vsf_sysutil_sockaddr* p_sockaddr,
|
||||
unsigned int wait_seconds);
|
||||
/* Option setting on sockets */
|
||||
void vsf_sysutil_activate_keepalive(int fd);
|
||||
void vsf_sysutil_set_iptos_throughput(int fd);
|
||||
void vsf_sysutil_activate_reuseaddr(int fd);
|
||||
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_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 struct vsf_sysutil_sockaddr* p_sockptr);
|
||||
|
||||
/* User database queries etc. */
|
||||
struct vsf_sysutil_user;
|
||||
struct vsf_sysutil_group;
|
||||
|
||||
struct vsf_sysutil_user* vsf_sysutil_getpwuid(const int uid);
|
||||
struct vsf_sysutil_user* vsf_sysutil_getpwnam(const char* p_user);
|
||||
const char* vsf_sysutil_user_getname(const struct vsf_sysutil_user* p_user);
|
||||
const char* vsf_sysutil_user_get_homedir(
|
||||
const struct vsf_sysutil_user* p_user);
|
||||
int vsf_sysutil_user_getuid(const struct vsf_sysutil_user* p_user);
|
||||
int vsf_sysutil_user_getgid(const struct vsf_sysutil_user* p_user);
|
||||
|
||||
struct vsf_sysutil_group* vsf_sysutil_getgrgid(const int gid);
|
||||
const char* vsf_sysutil_group_getname(const struct vsf_sysutil_group* p_group);
|
||||
|
||||
/* More random things */
|
||||
unsigned int vsf_sysutil_getpagesize(void);
|
||||
unsigned char vsf_sysutil_get_random_byte(void);
|
||||
unsigned int vsf_sysutil_get_umask(void);
|
||||
void vsf_sysutil_set_umask(unsigned int umask);
|
||||
void vsf_sysutil_make_session_leader(void);
|
||||
void vsf_sysutil_tzset(void);
|
||||
const char* vsf_sysutil_get_current_date(void);
|
||||
void vsf_sysutil_qsort(void* p_base, unsigned int num_elem,
|
||||
unsigned int elem_size,
|
||||
int (*p_compar)(const void *, const void *));
|
||||
|
||||
/* Credentials handling */
|
||||
int vsf_sysutil_running_as_root(void);
|
||||
void vsf_sysutil_setuid(const struct vsf_sysutil_user* p_user);
|
||||
void vsf_sysutil_setuid_numeric(int uid);
|
||||
void vsf_sysutil_setgid(const struct vsf_sysutil_user* p_user);
|
||||
void vsf_sysutil_setgid_numeric(int gid);
|
||||
void vsf_sysutil_clear_supp_groups(void);
|
||||
void vsf_sysutil_initgroups(const struct vsf_sysutil_user* p_user);
|
||||
void vsf_sysutil_chroot(const char* p_root_path);
|
||||
|
||||
/* Time handling */
|
||||
void vsf_sysutil_update_cached_time(void);
|
||||
long vsf_sysutil_get_cached_time_sec(void);
|
||||
long vsf_sysutil_get_cached_time_usec(void);
|
||||
void vsf_sysutil_sleep(double seconds);
|
||||
|
||||
#endif /* VSF_SYSUTIL_H */
|
||||
|
66
tunables.c
Normal file
66
tunables.c
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* tunables.c
|
||||
*/
|
||||
|
||||
#include "tunables.h"
|
||||
|
||||
int tunable_anonymous_enable = 1;
|
||||
int tunable_local_enable = 0;
|
||||
int tunable_pasv_enable = 1;
|
||||
int tunable_port_enable = 1;
|
||||
int tunable_chroot_local_user = 0;
|
||||
int tunable_write_enable = 0;
|
||||
int tunable_anon_upload_enable = 0;
|
||||
int tunable_anon_mkdir_write_enable = 0;
|
||||
int tunable_anon_other_write_enable = 0;
|
||||
int tunable_chown_uploads = 0;
|
||||
int tunable_connect_from_port_20 = 0;
|
||||
int tunable_xferlog_enable = 0;
|
||||
int tunable_dirmessage_enable = 0;
|
||||
int tunable_anon_world_readable_only = 1;
|
||||
int tunable_async_abor_enable = 0;
|
||||
int tunable_ascii_upload_enable = 0;
|
||||
int tunable_ascii_download_enable = 0;
|
||||
int tunable_one_process_model = 0;
|
||||
int tunable_xferlog_std_format = 0;
|
||||
int tunable_pasv_promiscuous = 0;
|
||||
int tunable_deny_email_enable = 0;
|
||||
int tunable_chroot_list_enable = 0;
|
||||
int tunable_setproctitle_enable = 0;
|
||||
int tunable_text_userdb_names = 0;
|
||||
int tunable_ls_recurse_enable = 0;
|
||||
int tunable_log_ftp_protocol = 0;
|
||||
int tunable_guest_enable = 0;
|
||||
int tunable_userlist_enable = 0;
|
||||
int tunable_userlist_deny = 1;
|
||||
|
||||
unsigned int tunable_accept_timeout = 60;
|
||||
unsigned int tunable_connect_timeout = 60;
|
||||
unsigned int tunable_local_umask = 077;
|
||||
unsigned int tunable_anon_umask = 077;
|
||||
unsigned int tunable_ftp_data_port = 20;
|
||||
unsigned int tunable_idle_session_timeout = 300;
|
||||
unsigned int tunable_data_connection_timeout = 300;
|
||||
/* IPPORT_USERRESERVED + 1 */
|
||||
unsigned int tunable_pasv_min_port = 5001;
|
||||
unsigned int tunable_pasv_max_port = 0;
|
||||
unsigned int tunable_anon_max_rate = 0;
|
||||
unsigned int tunable_local_max_rate = 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_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";
|
||||
const char* tunable_chroot_list_file = "/etc/vsftpd.chroot_list";
|
||||
const char* tunable_pam_service_name = "ftp";
|
||||
const char* tunable_guest_username = "ftp";
|
||||
const char* tunable_userlist_file = "/etc/vsftpd.user_list";
|
||||
|
64
tunables.h
Normal file
64
tunables.h
Normal file
@ -0,0 +1,64 @@
|
||||
#ifndef VSF_TUNABLES_H
|
||||
#define VSF_TUNABLES_H
|
||||
|
||||
/* Configurable preferences */
|
||||
/* Booleans */
|
||||
extern int tunable_anonymous_enable; /* Allow anon logins */
|
||||
extern int tunable_local_enable; /* Allow local logins */
|
||||
extern int tunable_pasv_enable; /* Allow PASV */
|
||||
extern int tunable_port_enable; /* Allow PORT */
|
||||
extern int tunable_chroot_local_user; /* Restrict local to home dir */
|
||||
extern int tunable_write_enable; /* Global enable writes */
|
||||
extern int tunable_anon_upload_enable; /* Enable STOR for anon users */
|
||||
extern int tunable_anon_mkdir_write_enable; /* MKD for anon */
|
||||
extern int tunable_anon_other_write_enable; /* DELE RMD RNFR RNTO for anon */
|
||||
extern int tunable_chown_uploads; /* chown() anon uploaded files */
|
||||
extern int tunable_connect_from_port_20; /* PORT connects from port 20 */
|
||||
extern int tunable_xferlog_enable; /* Log transfers to a file */
|
||||
extern int tunable_dirmessage_enable; /* Look for + output .message */
|
||||
extern int tunable_anon_world_readable_only; /* Only serve world readable */
|
||||
extern int tunable_async_abor_enable; /* Enable async ABOR requests */
|
||||
extern int tunable_ascii_upload_enable; /* Permit ASCII upload */
|
||||
extern int tunable_ascii_download_enable; /* Permit ASCII download */
|
||||
extern int tunable_one_process_model; /* Go faster stripes ;-) */
|
||||
extern int tunable_xferlog_std_format; /* Log details like wu-ftpd */
|
||||
extern int tunable_pasv_promiscuous; /* Allow any PASV connect IP */
|
||||
extern int tunable_deny_email_enable; /* Ban a list of anon e-mails */
|
||||
extern int tunable_chroot_list_enable; /* chroot() based on list file */
|
||||
extern int tunable_setproctitle_enable; /* Try to use setproctitle() */
|
||||
extern int tunable_text_userdb_names; /* For "ls", lookup text names */
|
||||
extern int tunable_ls_recurse_enable; /* Allow ls -R */
|
||||
extern int tunable_log_ftp_protocol; /* Log FTP requests/responses */
|
||||
extern int tunable_guest_enable; /* Remap guest users */
|
||||
extern int tunable_userlist_enable; /* Explicit user allow or deny */
|
||||
extern int tunable_userlist_deny; /* Is user list allow or deny? */
|
||||
|
||||
/* Integer/numeric defines */
|
||||
extern unsigned int tunable_accept_timeout;
|
||||
extern unsigned int tunable_connect_timeout;
|
||||
extern unsigned int tunable_local_umask;
|
||||
extern unsigned int tunable_anon_umask;
|
||||
extern unsigned int tunable_ftp_data_port;
|
||||
extern unsigned int tunable_idle_session_timeout;
|
||||
extern unsigned int tunable_data_connection_timeout;
|
||||
extern unsigned int tunable_pasv_min_port;
|
||||
extern unsigned int tunable_pasv_max_port;
|
||||
extern unsigned int tunable_anon_max_rate;
|
||||
extern unsigned int tunable_local_max_rate;
|
||||
|
||||
/* 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_message_file;
|
||||
extern const char* tunable_nopriv_user;
|
||||
extern const char* tunable_ftpd_banner;
|
||||
extern const char* tunable_banned_email_file;
|
||||
extern const char* tunable_chroot_list_file;
|
||||
extern const char* tunable_pam_service_name;
|
||||
extern const char* tunable_guest_username;
|
||||
extern const char* tunable_userlist_file;
|
||||
|
||||
#endif /* VSF_TUNABLES_H */
|
||||
|
276
twoprocess.c
Normal file
276
twoprocess.c
Normal file
@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* License: GPL
|
||||
* Author: Chris Evans
|
||||
* twoprocess.c
|
||||
*
|
||||
* Code implementing the standard, secure two process security model.
|
||||
*/
|
||||
|
||||
#include "twoprocess.h"
|
||||
#include "privops.h"
|
||||
#include "prelogin.h"
|
||||
#include "postlogin.h"
|
||||
#include "postprivparent.h"
|
||||
#include "session.h"
|
||||
#include "privsock.h"
|
||||
#include "secutil.h"
|
||||
#include "sysutil.h"
|
||||
#include "filestr.h"
|
||||
#include "str.h"
|
||||
#include "sysstr.h"
|
||||
#include "utility.h"
|
||||
#include "tunables.h"
|
||||
#include "defs.h"
|
||||
|
||||
static void drop_all_privs(void);
|
||||
static void handle_sigchld(int duff);
|
||||
static void process_login_req(struct vsf_session* p_sess);
|
||||
static void common_do_login(struct vsf_session* p_sess,
|
||||
const struct mystr* p_user_str, int do_chroot,
|
||||
int anon);
|
||||
|
||||
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
|
||||
* exited normally with zero exit code
|
||||
*/
|
||||
if (vsf_sysutil_retval_is_error(vsf_sysutil_wait_get_retval(&wait_retval)) ||
|
||||
!vsf_sysutil_wait_exited_normally(&wait_retval) ||
|
||||
vsf_sysutil_wait_get_exitcode(&wait_retval) != 0)
|
||||
{
|
||||
die("child died");
|
||||
}
|
||||
else
|
||||
{
|
||||
vsf_sysutil_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vsf_two_process_start(struct vsf_session* p_sess)
|
||||
{
|
||||
/* Create the comms channel between privileged parent and no-priv child */
|
||||
priv_sock_init(p_sess);
|
||||
vsf_sysutil_install_async_sighandler(kVSFSysUtilSigCHLD, handle_sigchld);
|
||||
{
|
||||
int newpid = vsf_sysutil_fork();
|
||||
if (newpid != 0)
|
||||
{
|
||||
/* Parent - go into pre-login parent process mode */
|
||||
while (1)
|
||||
{
|
||||
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
|
||||
* login processing
|
||||
*/
|
||||
if (tunable_local_enable && tunable_userlist_enable)
|
||||
{
|
||||
int retval = str_fileread(&p_sess->userlist_str, tunable_userlist_file,
|
||||
VSFTP_CONF_FILE_MAX);
|
||||
if (vsf_sysutil_retval_is_error(retval))
|
||||
{
|
||||
die("cannot open user list file");
|
||||
}
|
||||
}
|
||||
drop_all_privs();
|
||||
init_connection(p_sess);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
static void
|
||||
drop_all_privs(void)
|
||||
{
|
||||
struct mystr user_str = INIT_MYSTR;
|
||||
struct mystr dir_str = INIT_MYSTR;
|
||||
str_alloc_text(&user_str, tunable_nopriv_user);
|
||||
str_alloc_text(&dir_str, tunable_secure_chroot_dir);
|
||||
/* Be kind: give good error message if the secure dir is missing */
|
||||
{
|
||||
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 'tunable_secure_chroot_dir'");
|
||||
}
|
||||
vsf_sysutil_free(p_statbuf);
|
||||
}
|
||||
vsf_secutil_change_credentials(&user_str, &dir_str, 1, 0, 0);
|
||||
str_free(&user_str);
|
||||
str_free(&dir_str);
|
||||
}
|
||||
|
||||
void
|
||||
vsf_two_process_login(struct vsf_session* p_sess,
|
||||
const struct mystr* p_pass_str)
|
||||
{
|
||||
char result;
|
||||
priv_sock_send_cmd(p_sess, PRIV_SOCK_LOGIN);
|
||||
priv_sock_send_str(p_sess, &p_sess->user_str);
|
||||
priv_sock_send_str(p_sess, p_pass_str);
|
||||
result = priv_sock_get_result(p_sess);
|
||||
if (result == PRIV_SOCK_RESULT_OK)
|
||||
{
|
||||
/* Miracle. We don't emit the success message here. That is left to
|
||||
* process_post_login().
|
||||
* Exit normally, parent will wait for this and launch new child
|
||||
*/
|
||||
vsf_sysutil_exit(0);
|
||||
}
|
||||
else if (result == PRIV_SOCK_RESULT_BAD)
|
||||
{
|
||||
/* Continue the processing loop.. */
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
die("priv_sock_get_result");
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
vsf_two_process_get_priv_data_sock(struct vsf_session* p_sess)
|
||||
{
|
||||
char res;
|
||||
priv_sock_send_cmd(p_sess, PRIV_SOCK_GET_DATA_SOCK);
|
||||
res = priv_sock_get_result(p_sess);
|
||||
if (res != PRIV_SOCK_RESULT_OK)
|
||||
{
|
||||
die("could not get privileged socket");
|
||||
}
|
||||
return priv_sock_child_recv_fd(p_sess);
|
||||
}
|
||||
|
||||
void
|
||||
vsf_two_process_chown_upload(struct vsf_session* p_sess, int fd)
|
||||
{
|
||||
char res;
|
||||
priv_sock_send_cmd(p_sess, PRIV_SOCK_CHOWN);
|
||||
priv_sock_child_send_fd(p_sess, fd);
|
||||
res = priv_sock_get_result(p_sess);
|
||||
if (res != PRIV_SOCK_RESULT_OK)
|
||||
{
|
||||
die("unexpected failure in vsf_two_process_chown_upload");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
process_login_req(struct vsf_session* p_sess)
|
||||
{
|
||||
enum EVSFPrivopLoginResult e_login_result = kVSFLoginNull;
|
||||
/* Blocks */
|
||||
if (priv_sock_get_cmd(p_sess) != PRIV_SOCK_LOGIN)
|
||||
{
|
||||
die("bad request");
|
||||
}
|
||||
/* Get username and password - we must distrust these */
|
||||
{
|
||||
struct mystr password_str = INIT_MYSTR;
|
||||
priv_sock_get_str(p_sess, &p_sess->user_str);
|
||||
priv_sock_get_str(p_sess, &password_str);
|
||||
e_login_result = vsf_privop_do_login(p_sess, &password_str);
|
||||
str_free(&password_str);
|
||||
}
|
||||
switch (e_login_result)
|
||||
{
|
||||
case kVSFLoginFail:
|
||||
priv_sock_send_result(p_sess, PRIV_SOCK_RESULT_BAD);
|
||||
return;
|
||||
break;
|
||||
case kVSFLoginAnon:
|
||||
str_alloc_text(&p_sess->user_str, tunable_ftp_username);
|
||||
common_do_login(p_sess, &p_sess->user_str, 1, 1);
|
||||
break;
|
||||
case kVSFLoginReal:
|
||||
{
|
||||
int do_chroot = 0;
|
||||
if (tunable_chroot_local_user)
|
||||
{
|
||||
do_chroot = 1;
|
||||
}
|
||||
if (tunable_chroot_list_enable)
|
||||
{
|
||||
struct mystr chroot_list_file = INIT_MYSTR;
|
||||
int retval = str_fileread(&chroot_list_file,
|
||||
tunable_chroot_list_file,
|
||||
VSFTP_CONF_FILE_MAX);
|
||||
if (vsf_sysutil_retval_is_error(retval))
|
||||
{
|
||||
die("cannot open chroot() user list file");
|
||||
}
|
||||
if (str_contains_line(&chroot_list_file, &p_sess->user_str))
|
||||
{
|
||||
if (do_chroot)
|
||||
{
|
||||
do_chroot = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
do_chroot = 1;
|
||||
}
|
||||
}
|
||||
str_free(&chroot_list_file);
|
||||
}
|
||||
common_do_login(p_sess, &p_sess->user_str, do_chroot, 0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
bug("weird state in process_login_request");
|
||||
break;
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
static void
|
||||
common_do_login(struct vsf_session* p_sess, const struct mystr* p_user_str,
|
||||
int do_chroot, int anon)
|
||||
{
|
||||
int newpid;
|
||||
vsf_sysutil_default_sig(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();
|
||||
vsf_sysutil_install_async_sighandler(kVSFSysUtilSigCHLD, handle_sigchld);
|
||||
newpid = vsf_sysutil_fork();
|
||||
if (newpid == 0)
|
||||
{
|
||||
struct mystr guest_user_str = INIT_MYSTR;
|
||||
/* Child - drop privs and start proper FTP! */
|
||||
if (tunable_guest_enable && !anon)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
vsf_secutil_change_credentials(p_user_str, 0, do_chroot, 1, 0);
|
||||
str_free(&guest_user_str);
|
||||
/* Guard against the config error of having the anonymous ftp tree owned
|
||||
* by the user we are running as
|
||||
*/
|
||||
if (anon && vsf_sysutil_write_access("/"))
|
||||
{
|
||||
die("vsftpd: refusing to run with writable anonymous root");
|
||||
}
|
||||
p_sess->is_anonymous = anon;
|
||||
process_post_login(p_sess);
|
||||
bug("should not get here: common_do_login");
|
||||
}
|
||||
/* Parent */
|
||||
vsf_priv_parent_postlogin(p_sess);
|
||||
bug("should not get here in common_do_login");
|
||||
}
|
||||
|
46
twoprocess.h
Normal file
46
twoprocess.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef VSF_TWOPROCESS_H
|
||||
#define VSF_TWOPROCESS_H
|
||||
|
||||
struct mystr;
|
||||
struct vsf_session;
|
||||
|
||||
/* vsf_two_process_start()
|
||||
* PURPOSE
|
||||
* Called to start FTP login processing using the two process model. This
|
||||
* launches the unprivileged child to process the FTP login.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
*/
|
||||
void vsf_two_process_start(struct vsf_session* p_sess);
|
||||
|
||||
/* vsf_two_process_login()
|
||||
* PURPOSE
|
||||
* Called to propose a login using the two process model.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* p_pass_str - the proposed password
|
||||
*/
|
||||
void vsf_two_process_login(struct vsf_session* p_sess,
|
||||
const struct mystr* p_pass_str);
|
||||
|
||||
/* vsf_two_process_get_priv_data_sock()
|
||||
* PURPOSE
|
||||
* Get a privileged port 20 bound data socket using the two process model.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* RETURNS
|
||||
* The file descriptor of the privileged socket
|
||||
*/
|
||||
int vsf_two_process_get_priv_data_sock(struct vsf_session* p_sess);
|
||||
|
||||
/* vsf_two_process_chown_upload()
|
||||
* PURPOSE
|
||||
* Change ownership of an uploaded file using the two process model.
|
||||
* PARAMETERS
|
||||
* p_sess - the current session object
|
||||
* fd - the file descriptor to change ownership on
|
||||
*/
|
||||
void vsf_two_process_chown_upload(struct vsf_session* p_sess, int fd);
|
||||
|
||||
#endif /* VSF_TWOPROCESS_H */
|
||||
|
34
utility.c
Normal file
34
utility.c
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Part of Very Secure FTPd
|
||||
* Licence: GPL
|
||||
* Author: Chris Evans
|
||||
* utility.c
|
||||
*/
|
||||
|
||||
#include "utility.h"
|
||||
#include "sysutil.h"
|
||||
#include "defs.h"
|
||||
|
||||
#define DIE_DEBUG
|
||||
|
||||
void
|
||||
die(const char* p_text)
|
||||
{
|
||||
#ifdef DIE_DEBUG
|
||||
bug(p_text);
|
||||
#endif
|
||||
vsf_sysutil_exit(1);
|
||||
}
|
||||
|
||||
void
|
||||
bug(const char* p_text)
|
||||
{
|
||||
/* Rats. Try and write the reason to the network for diagnostics */
|
||||
vsf_sysutil_activate_noblock(VSFTP_COMMAND_FD);
|
||||
(void) vsf_sysutil_write_loop(VSFTP_COMMAND_FD, "500 OOPS: ", 10);
|
||||
(void) vsf_sysutil_write_loop(VSFTP_COMMAND_FD, p_text,
|
||||
vsf_sysutil_strlen(p_text));
|
||||
(void) vsf_sysutil_write_loop(VSFTP_COMMAND_FD, "\r\n", 2);
|
||||
vsf_sysutil_exit(1);
|
||||
}
|
||||
|
25
utility.h
Normal file
25
utility.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef VSF_UTILITY_H
|
||||
#define VSF_UTILITY_H
|
||||
|
||||
struct mystr;
|
||||
|
||||
/* die()
|
||||
* PURPOSE
|
||||
* Terminate execution of the process, due to an abnormal (but non-bug)
|
||||
* situation.
|
||||
* PARAMETERS
|
||||
* p_text - text string describing why the process is exiting
|
||||
*/
|
||||
void die(const char* p_text);
|
||||
|
||||
/* bug()
|
||||
* PURPOSE
|
||||
* Terminate execution of the process, due to a suspected bug, trying to emit
|
||||
* the reason this happened down the network in FTP response format.
|
||||
* PARAMETERS
|
||||
* p_text - text string describing what bug trap has triggered
|
||||
* */
|
||||
void bug(const char* p_text);
|
||||
|
||||
#endif
|
||||
|
43
vsf_findlibs.sh
Executable file
43
vsf_findlibs.sh
Executable file
@ -0,0 +1,43 @@
|
||||
#!/bin/sh
|
||||
# Cheesy hacky location of additional link libraries.
|
||||
|
||||
# Optimizations for specific platforms, to avoid unneccessary libraries
|
||||
if [ -r /etc/redhat-release ]; then
|
||||
grep '7\.' /etc/redhat-release >/dev/null && echo "-lpam" && exit
|
||||
grep '6\.' /etc/redhat-release >/dev/null && echo "-lpam -ldl" && exit
|
||||
fi
|
||||
|
||||
locate_library() { [ ! "$1*" = "`echo $1*`" ]; }
|
||||
|
||||
# Look for PAM
|
||||
locate_library /lib/libpam.so && echo "-lpam";
|
||||
|
||||
# Look for PAM in alternate location (FreeBSD)
|
||||
locate_library /usr/lib/libpam.so && echo "-lpam";
|
||||
|
||||
# Look for the crypt library
|
||||
# XXX - adds a link library even if it's not needed
|
||||
locate_library /lib/libcrypt.so && echo "-lcrypt"
|
||||
|
||||
# Look for the crypt library (FreeBSD)
|
||||
locate_library /usr/lib/libcrypt.so && echo "-lcrypt"
|
||||
|
||||
# Look for the dynamic linker library. Needed by older RedHat when
|
||||
# you link in PAM
|
||||
locate_library /lib/libdl.so && echo "-ldl";
|
||||
|
||||
# Look for libsocket. Solaris needs this.
|
||||
locate_library /lib/libsocket.so && echo "-lsocket";
|
||||
|
||||
# Look for libnsl. Solaris needs this.
|
||||
locate_library /lib/libnsl.so && echo "-lnsl";
|
||||
|
||||
# Look for libutil. Older FreeBSD need this for setproctitle().
|
||||
locate_library /usr/lib/libutil.so && echo "-lutil";
|
||||
|
||||
# HP-UX ends shared libraries with .sl
|
||||
locate_library /usr/lib/libpam.sl && echo "-lpam";
|
||||
|
||||
# For older HP-UX...
|
||||
locate_library /usr/lib/libsec.sl && echo "-lsec";
|
||||
|
26
vsftpd.8
Normal file
26
vsftpd.8
Normal file
@ -0,0 +1,26 @@
|
||||
.\" Copyright (c) 2001 Daniel Jacobowitz <dan@debian.org>
|
||||
.Dd March 8, 2001
|
||||
.Dt VSFTPD 8
|
||||
.Sh NAME
|
||||
.Nm vsftpd
|
||||
.Nd Very Secure FTP Daemon
|
||||
.Sh SYNOPSIS
|
||||
.Nm vsftpd
|
||||
.Op Ar configuration file
|
||||
.Sh DESCRIPTION
|
||||
.Nm vsftpd
|
||||
is the Very Secure File Transfer Protocol Daemon. The server should be
|
||||
invoked from a
|
||||
.Dq super-server
|
||||
such as
|
||||
.Xr inetd 8
|
||||
or
|
||||
.Xr xinetd 8 .
|
||||
.Sh OPTIONS
|
||||
An optional
|
||||
.Op configuration file
|
||||
may be given on the command line. The default configuration file is
|
||||
.Pa /etc/vsftpd.conf .
|
||||
.Sh SEE ALSO
|
||||
.Xr vsftpd.conf 5
|
||||
|
100
vsftpd.conf
Normal file
100
vsftpd.conf
Normal file
@ -0,0 +1,100 @@
|
||||
# Example config file /etc/vsftpd.conf
|
||||
#
|
||||
# The default compiled in settings are very paranoid. This sample file
|
||||
# loosens things up a bit, to make the ftp daemon more usable.
|
||||
#
|
||||
# Allow anonymous FTP?
|
||||
anonymous_enable=YES
|
||||
#
|
||||
# Uncomment this to allow local users to log in.
|
||||
#local_enable=YES
|
||||
#
|
||||
# Uncomment this to enable any form of FTP write command.
|
||||
#write_enable=YES
|
||||
#
|
||||
# Default umask for local users is 077. You may wish to change this to 022,
|
||||
# if your users expect that (022 is used by most other ftpd's)
|
||||
#local_umask=022
|
||||
#
|
||||
# Uncomment this to allow the anonymous FTP user to upload files. This only
|
||||
# has an effect if the above global write enable is activated. Also, you will
|
||||
# obviously need to create a directory writable by the FTP user.
|
||||
#anon_upload_enable=YES
|
||||
#
|
||||
# Uncomment this if you want the anonymous FTP user to be able to create
|
||||
# new directories.
|
||||
#anon_mkdir_write_enable=YES
|
||||
#
|
||||
# Activate directory messages - messages given to remote users when they
|
||||
# go into a certain directory.
|
||||
dirmessage_enable=YES
|
||||
#
|
||||
# Activate logging of uploads/downloads.
|
||||
xferlog_enable=YES
|
||||
#
|
||||
# Make sure PORT transfer connections originate from port 20 (ftp-data).
|
||||
connect_from_port_20=YES
|
||||
#
|
||||
# If you want, you can arrange for uploaded anonymous files to be owned by
|
||||
# a different user. Note! Using "root" for uploaded files is not
|
||||
# recommended!
|
||||
#chown_uploads=YES
|
||||
#chown_username=whoever
|
||||
#
|
||||
# You may override where the log file goes if you like. The default is shown
|
||||
# below.
|
||||
#xferlog_file=/var/log/vsftpd.log
|
||||
#
|
||||
# If you want, you can have your log file in standard ftpd xferlog format
|
||||
#xferlog_std_format=YES
|
||||
#
|
||||
# You may change the default value for timing out an idle session.
|
||||
#idle_session_timeout=600
|
||||
#
|
||||
# You may change the default value for timing out a data connection.
|
||||
#data_connection_timeout=120
|
||||
#
|
||||
# It is recommended that you define on your system a unique user which the
|
||||
# ftp server can use as a totally isolated and unprivileged user.
|
||||
#nopriv_user=ftpsecure
|
||||
#
|
||||
# Enable this and the server will recognise asynchronous ABOR requests. Not
|
||||
# recommended for security (the code is non-trivial). Not enabling it,
|
||||
# however, may confuse older FTP clients.
|
||||
#async_abor_enable=YES
|
||||
#
|
||||
# By default the server will pretend to allow ASCII mode but in fact ignore
|
||||
# the request. Turn on the below options to have the server actually do ASCII
|
||||
# mangling on files when in ASCII mode.
|
||||
# Beware that turning on ascii_download_enable enables malicious remote parties
|
||||
# to consume your I/O resources, by issuing the command "SIZE /big/file" in
|
||||
# ASCII mode.
|
||||
# These ASCII options are split into upload and download because you may wish
|
||||
# to enable ASCII uploads (to prevent uploaded scripts etc. from breaking),
|
||||
# without the DoS risk of SIZE and ASCII downloads. ASCII mangling should be
|
||||
# on the client anyway..
|
||||
#ascii_upload_enable=YES
|
||||
#ascii_download_enable=YES
|
||||
#
|
||||
# You may fully customise the login banner string:
|
||||
#ftpd_banner=Welcome to blah FTP service.
|
||||
#
|
||||
# You may specify a file of disallowed anonymous e-mail addresses. Apparently
|
||||
# useful for combatting certain DoS attacks.
|
||||
#deny_email_enable=YES
|
||||
# (default follows)
|
||||
#banned_email_file=/etc/vsftpd.banned_emails
|
||||
#
|
||||
# You may specify an explicit list of local users to chroot() to their home
|
||||
# directory. If chroot_local_user is YES, then this list becomes a list of
|
||||
# users to NOT chroot().
|
||||
#chroot_list_enable=YES
|
||||
# (default follows)
|
||||
#chroot_list_file=/etc/vsftpd.chroot_list
|
||||
#
|
||||
# You may activate the "-R" option to the builtin ls. This is disabled by
|
||||
# default to avoid remote users being able to cause excessive I/O on large
|
||||
# sites. However, some broken FTP clients such as "ncftp" and "mirror" assume
|
||||
# the presence of the "-R" option, so there is a strong case for enabling it.
|
||||
#ls_recurse_enable=YES
|
||||
|
439
vsftpd.conf.5
Normal file
439
vsftpd.conf.5
Normal file
@ -0,0 +1,439 @@
|
||||
.TH VSFTPD.CONF 5
|
||||
.SH NAME
|
||||
vsftpd.conf, the config file for vsftpd
|
||||
.SH DESCRIPTION
|
||||
vsftpd.conf may be used to control various aspects of vsftpd's behaviour. By
|
||||
default, vsftpd looks for this file at the location
|
||||
.BR /etc/vsftpd.conf .
|
||||
However, you may override this by specifying a command line argument to
|
||||
vsftpd. The command line argument is the pathname of the configuration file
|
||||
for vsftpd. This behaviour is useful because you may wish to use an advanced
|
||||
inetd such as
|
||||
.BR xinetd
|
||||
to launch vsftpd with different configuration files on a per virtual host
|
||||
basis.
|
||||
|
||||
.SH FORMAT
|
||||
The format of vsftpd.conf is very simple. Each line is either a comment or
|
||||
a directive. Comment lines start with a # and are ignored. A directive line
|
||||
has the format:
|
||||
|
||||
option=value
|
||||
|
||||
It is important to note that it is an error to put any space between the
|
||||
option, = and value.
|
||||
|
||||
Each setting has a compiled in default which may be modified in the
|
||||
configuration file.
|
||||
|
||||
.SH BOOLEAN OPTIONS
|
||||
Below is a list of boolean options. The value for a boolean option may be set
|
||||
to
|
||||
.BR YES
|
||||
or
|
||||
.BR NO .
|
||||
|
||||
.TP
|
||||
.B anon_mkdir_write_enable
|
||||
If set to YES, anonymous users will be permitted to create new directories
|
||||
under certain conditions. For this to work, the option
|
||||
.BR write_enable
|
||||
must be activated, and the anonymous ftp user must have write permission on
|
||||
the parent directory.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
.B anon_other_write_enable
|
||||
If set to YES, anonymous users will be permitted to perform write operations
|
||||
other than upload and create directory, such as deletion and renaming. This
|
||||
is generally not recommended but included for completeness.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
.B anon_upload_enable
|
||||
If set to YES, anonymous users will be permitted to upload files under certain
|
||||
conditions. For this to work, the option
|
||||
.BR write_enable
|
||||
must be activated, and the anonymous ftp user must have write permission on
|
||||
desired upload locations.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
.B anon_world_readable_only
|
||||
When enabled, anonymous users will only be allowed to download files which
|
||||
are world readable. This is recognising that the ftp user may own files,
|
||||
especially in the presence of uploads.
|
||||
|
||||
Default: YES
|
||||
.TP
|
||||
.B anonymous_enable
|
||||
Controls whether anonymous logins are permitted or not. If enabled,
|
||||
both the usernames
|
||||
.BR ftp
|
||||
and
|
||||
.BR anonymous
|
||||
are recognised as anonymous logins.
|
||||
|
||||
Default: YES
|
||||
.TP
|
||||
.B ascii_download_enable
|
||||
When enabled, ASCII mode data transfers will be honoured on downloads.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
.B ascii_upload_enable
|
||||
When enabled, ASCII mode data transfers will be honoured on uploads.
|
||||
|
||||
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
|
||||
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 chown_uploads
|
||||
If enabled, all anonymously uploaded files will have the ownership changed
|
||||
to the user specified in the setting
|
||||
.BR chown_username .
|
||||
This is useful from an administrative, and perhaps security, standpoint.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
.B chroot_list_enable
|
||||
If activated, you may provide a list of local users who are placed in a
|
||||
chroot() jail in their home directory upon login. The meaning is slightly
|
||||
different if chroot_local_user is set to YES. In this case, the list becomes
|
||||
a list of users which are NOT to be placed in a chroot() jail.
|
||||
By default, the file containing this list is
|
||||
/etc/vsftpd.chroot_list, but you may override this with the
|
||||
.BR chroot_list_file
|
||||
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.
|
||||
.BR Warning:
|
||||
This option has security implications, especially if the users also have
|
||||
shell access. Only enable if you know what you are doing.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
.B connect_from_port_20
|
||||
This controls whether PORT style data connections use port 20 (ftp-data) on
|
||||
the server machine. For security reasons, some clients may insist that this
|
||||
is the case. Conversely, disabling this option enables vsftpd to run with
|
||||
slightly less privilege.
|
||||
|
||||
Default: NO (but the sample config file enables it)
|
||||
.TP
|
||||
.B deny_email_enable
|
||||
If activated, you may provide a list of anonymous password e-mail responses
|
||||
which cause login to be denied. By default, the file containing this list is
|
||||
/etc/vsftpd.banned_emails, but you may override this with the
|
||||
.BR banned_email_file
|
||||
setting.
|
||||
|
||||
Default: NO
|
||||
.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
|
||||
file .message, but that may be overridden with the configuration setting
|
||||
.BR message_file .
|
||||
|
||||
Default: NO (but the sample config file enables it)
|
||||
.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
|
||||
.BR guest_username
|
||||
setting.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
.B local_enable
|
||||
Controls whether local logins are permitted or not. If enabled, normal
|
||||
user accounts in /etc/passwd may be used to log in.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
.B log_ftp_protocol
|
||||
When enabled, all FTP requests and responses are logged, providing the option
|
||||
xferlog_std_format is not enabled. Useful for debugging.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
.B ls_recurse_enable
|
||||
When enabled, this setting will allow the use of "ls -R". This is a minor
|
||||
security risk, because a ls -R at the top level of a large site may consume
|
||||
a lot of resources.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
.B one_process_model
|
||||
If you have a Linux 2.4 kernel, it is possible to use a different security
|
||||
model which only uses one process per connection. It is a less pure security
|
||||
model, but gains you performance. You really don't want to enable this unless
|
||||
you know what you are doing, and your site supports huge numbers of
|
||||
simultaneously connected users.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
.B pasv_enable
|
||||
Set to NO if you want to disallow the PASV method of obtaining a data
|
||||
connection.
|
||||
|
||||
Default: YES
|
||||
.TP
|
||||
.B pasv_promiscuous
|
||||
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.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
.B port_enable
|
||||
Set to NO if you want to disallow the PORT method of obtaining a data
|
||||
connection.
|
||||
|
||||
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 text_userdb_names
|
||||
By default, numeric IDs are shown in the user and group fields of directory
|
||||
listings. You can get textual names by enabling this parameter. It is off
|
||||
by default for performance reasons.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
.B userlist_deny
|
||||
This option is examined if
|
||||
.B userlist_enable
|
||||
is activated. If you set this setting to NO, then users will be denied login
|
||||
unless they are explicitly listed in the file specified by
|
||||
.BR userlist_file .
|
||||
When login is denied, the denial is issued before the user is asked for a
|
||||
password.
|
||||
|
||||
Default: YES
|
||||
.TP
|
||||
.B userlist_enable
|
||||
If enabled, vsftpd will load a list of usernames, from the filename given by
|
||||
.BR userlist_file .
|
||||
If a user tries to log in using a name in this file, they will be denied
|
||||
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 write_enable
|
||||
This controls whether any FTP commands which change the filesystem are allowed
|
||||
or not. These commands are: STOR, DELE, RNFR, RNTO, MKD, RMD, APPE and SITE.
|
||||
|
||||
Default: NO
|
||||
.TP
|
||||
.B xferlog_enable
|
||||
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 .
|
||||
|
||||
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.
|
||||
|
||||
Default: NO
|
||||
|
||||
.SH NUMERIC OPTIONS
|
||||
Below is a list of numeric options. A numeric option must be set to a non
|
||||
negative integer. Octal numbers are supported, for convenience of the umask
|
||||
options. To specify an octal number, use 0 as the first digit of the number.
|
||||
|
||||
.TP
|
||||
.B accept_timeout
|
||||
The timeout, in seconds, for a remote client to establish connection with
|
||||
a PASV style data connection.
|
||||
|
||||
Default: 60
|
||||
.TP
|
||||
.B anon_max_rate
|
||||
The maximum data transfer rate permitted, in bytes per second, for anonymous
|
||||
clients.
|
||||
|
||||
Default: 0 (unlimited)
|
||||
.TP
|
||||
.B anon_umask
|
||||
The value that the umask for file creation is set to for anonymous users. NOTE! If you want to specify octal values, remember the "0" prefix otherwise the
|
||||
value will be treated as a base 10 integer!
|
||||
|
||||
Default: 077
|
||||
.TP
|
||||
.B connect_timeout
|
||||
The timeout, in seconds, for a remote client to respond to our PORT style
|
||||
data connection.
|
||||
|
||||
Default: 60
|
||||
.TP
|
||||
.B data_connection_timeout
|
||||
The timeout, in seconds, which is roughly the maximum time we permit data
|
||||
transfers to stall for with no progress. If the timeout triggers, the remote
|
||||
client is kicked off.
|
||||
|
||||
Default: 300
|
||||
.TP
|
||||
.B ftp_data_port
|
||||
The port from which PORT style connections originate (as long as the poorly
|
||||
named
|
||||
.BR connect_from_port_20
|
||||
is enabled).
|
||||
|
||||
Default: 20
|
||||
.TP
|
||||
.B idle_session_timeout
|
||||
The timeout, in seconds, which is the maximum time a remote client may spend
|
||||
between FTP commands. If the timeout triggers, the remote client is kicked
|
||||
off.
|
||||
|
||||
Default: 300
|
||||
.TP
|
||||
.B local_max_rate
|
||||
The maximum data transfer rate permitted, in bytes per second, for local
|
||||
authenticated users.
|
||||
|
||||
Default: 0 (unlimited)
|
||||
.TP
|
||||
.B local_umask
|
||||
The value that the umask for file creation is set to for local users. NOTE! If
|
||||
you want to specify octal values, remember the "0" prefix otherwise the value
|
||||
will be treated as a base 10 integer!
|
||||
|
||||
Default: 077
|
||||
.TP
|
||||
.B pasv_max_port
|
||||
The maximum 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 pasv_min_port
|
||||
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)
|
||||
|
||||
.SH STRING OPTIONS
|
||||
Below is a list of string options.
|
||||
|
||||
.TP
|
||||
.B banned_email_file
|
||||
This option is the name of a file containing a list of anonymous e-mail
|
||||
passwords which are not permitted. This file is consulted if the option
|
||||
.BR deny_email_enable
|
||||
is enabled.
|
||||
|
||||
Default: /etc/vsftpd.banned_emails
|
||||
.TP
|
||||
.B chown_username
|
||||
This is the name of the user who is given ownership of anonymously uploaded
|
||||
files. This option is only relevant if another option,
|
||||
.BR chown_uploads ,
|
||||
is set.
|
||||
|
||||
Default: root
|
||||
.TP
|
||||
.B chroot_list_file
|
||||
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
|
||||
.BR chroot_local_user
|
||||
is disabled.
|
||||
|
||||
Default: /etc/vsftpd.chroot_list
|
||||
.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 ftp_username
|
||||
This is the name of the user we use for handling anonymous FTP. The home
|
||||
directory of this user is the root of the anonymous FTP area.
|
||||
|
||||
Default: ftp
|
||||
.TP
|
||||
.B ftpd_banner
|
||||
This string option allows you to override the greeting banner displayed
|
||||
by vsftpd when a connection first comes in.
|
||||
|
||||
Default: (none - default vsftpd banner is displayed)
|
||||
.TP
|
||||
.B message_file
|
||||
This option is the name of the file we look for when a new directory is
|
||||
entered. The contents are displayed to the remote user. This option is
|
||||
only relevant if the option
|
||||
.BR dirmessage_enable
|
||||
is enabled.
|
||||
|
||||
Default: .message
|
||||
.TP
|
||||
.B nopriv_user
|
||||
This is the name of the user that is used by vsftpd when it want to be
|
||||
totally unprivileged. Note that this should be a dedicated user, rather
|
||||
than nobody. The user nobody tends to be used for rather a lot of important
|
||||
things on most machines.
|
||||
|
||||
Default: nobody
|
||||
.TP
|
||||
.B pam_service_name
|
||||
This string is the name of the PAM service vsftpd will use.
|
||||
|
||||
Default: ftp
|
||||
.TP
|
||||
.B secure_chroot_dir
|
||||
This option should be the name of a directory which is empty. Also, the
|
||||
directory should not be writable by the ftp user. This directory is used
|
||||
as a secure chroot() jail at times vsftpd does not require filesystem access.
|
||||
|
||||
Default: /usr/share/empty
|
||||
.TP
|
||||
.B userlist_file
|
||||
This option is the name of the file loaded when the
|
||||
.BR userlist_enable
|
||||
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 xferlog_enable
|
||||
is set.
|
||||
|
||||
Default: /var/log/vsftpd.log
|
||||
|
||||
.SH AUTHOR
|
||||
chris@scary.beasts.org
|
||||
|
7
vsftpver.h
Normal file
7
vsftpver.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef VSF_VERSION_H
|
||||
#define VSF_VERSION_H
|
||||
|
||||
#define VSF_VERSION "0.9.2"
|
||||
|
||||
#endif /* VSF_VERSION_H */
|
||||
|
18
xinetd.d/vsftpd
Normal file
18
xinetd.d/vsftpd
Normal file
@ -0,0 +1,18 @@
|
||||
# default: on
|
||||
# description:
|
||||
# The vsftpd FTP server serves FTP connections. It uses
|
||||
# normal, unencrypted usernames and passwords for authentication.
|
||||
# vsftpd is designed to be secure.
|
||||
service ftp
|
||||
{
|
||||
socket_type = stream
|
||||
wait = no
|
||||
user = root
|
||||
server = /usr/sbin/vsftpd
|
||||
# server_args =
|
||||
# log_on_success += DURATION USERID
|
||||
# log_on_failure += USERID
|
||||
nice = 10
|
||||
disable = no
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user