Hello community,

here is the log from the commit of package uftpd for openSUSE:Leap:15.2 checked 
in at 2020-01-17 12:00:35
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Leap:15.2/uftpd (Old)
 and      /work/SRC/openSUSE:Leap:15.2/.uftpd.new.26092 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "uftpd"

Fri Jan 17 12:00:35 2020 rev:4 rq:763576 version:2.11

Changes:
--------
--- /work/SRC/openSUSE:Leap:15.2/uftpd/uftpd.changes    2020-01-15 
16:26:47.152698100 +0100
+++ /work/SRC/openSUSE:Leap:15.2/.uftpd.new.26092/uftpd.changes 2020-01-17 
12:00:35.600481678 +0100
@@ -1,0 +2,46 @@
+Sun Jan  5 09:44:30 UTC 2020 - Martin Hauke <[email protected]>
+
+- Update to version 2.11
+  * Increased logging at default log level. Now all relevant
+    interaction is logged. See the man page for how to adjust.
+  * Fix buffer overflow in FTP PORT parser
+  * Fix TFTP/FTP directory traversal regression
+  * Fix potential DOS through non-busy loop and segfault
+  * Fix potential segfault through empty FTP password
+  * Fix potential segfault through FTP PORT command
+
+-------------------------------------------------------------------
+Mon Aug 26 20:56:41 UTC 2019 - Martin Hauke <[email protected]>
+
+- Update to version 2.10
+  * Add support for TFTP WRQ, i.e. for clients sending files to
+    server
+  * Fix invalid TFTP error codes, now uses custom error string to
+    code 0
+  * Slightly improved debug messages
+
+-------------------------------------------------------------------
+Mon Jul 29 20:02:51 UTC 2019 - Martin Hauke <[email protected]>
+
+- Update to version 2.9
+  * Check FTP root security after dropping privileges
+  * Revert insecure default: "writable FTP root", introduced in v2.8
+  * Revert part of issue #18 to fix issue #23; "CWD /" doesn't work
+  * Fix spelling errors found by Lintian
+  * Fix package description, more formal and less personal
+
+-------------------------------------------------------------------
+Tue May 28 17:44:15 UTC 2019 - Martin Hauke <[email protected]>
+
+- Update to version 2.8
+  Changes:
+  * The FTP command processor now always converts all inbound
+    commands to uppercase to handle clients sending commands in
+    lowercase
+  * Any arguments to the FTP LIST command are now ignored
+  * Improved user feedback on bad FTP root error message
+  Fixes
+  * Fix #18: KDE Dolphin, FTP client interop problems.
+  * Fix off-by-one regression introduced in v2.5
+
+-------------------------------------------------------------------

Old:
----
  uftpd-2.7.tar.gz

New:
----
  uftpd-2.11.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ uftpd.spec ++++++
--- /var/tmp/diff_new_pack.osGmRO/_old  2020-01-17 12:00:36.048481875 +0100
+++ /var/tmp/diff_new_pack.osGmRO/_new  2020-01-17 12:00:36.060481881 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package uftpd
 #
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2020 SUSE LINUX GmbH, Nuernberg, Germany.
 # Copyright (c) 2018, Martin Hauke <[email protected]>
 #
 # All modifications and additions to the file contributed by third parties
@@ -18,7 +18,7 @@
 
 
 Name:           uftpd
-Version:        2.7
+Version:        2.11
 Release:        0
 Summary:        A combined TFTP/FTP server
 License:        ISC

++++++ uftpd-2.7.tar.gz -> uftpd-2.11.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/.gitignore new/uftpd-2.11/.gitignore
--- old/uftpd-2.7/.gitignore    2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/.gitignore   2020-01-05 08:51:54.000000000 +0100
@@ -10,7 +10,7 @@
 Makefile.in
 aclocal.m4
 ar-lib
-/autom4te.cache/*
+autom4te.cache/*
 compile
 config.*
 configure
@@ -18,19 +18,10 @@
 install-sh
 libtool
 ltmain.sh
-misc/
 missing
 stamp-h1
-uftpd
-debian/files
-debian/uftpd.*
 TAGS
-/GPATH
-/GRTAGS
-/GSYMS
-/GTAGS
-/CHANGELOG.html
-/README.html
-/uftpd.html
-/tok
-/uftp
+GPATH
+GRTAGS
+GSYMS
+GTAGS
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/ChangeLog.md new/uftpd-2.11/ChangeLog.md
--- old/uftpd-2.7/ChangeLog.md  2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/ChangeLog.md 2020-01-05 08:51:54.000000000 +0100
@@ -4,6 +4,66 @@
 All notable changes to the project are documented in this file.
 
 
+[v2.11][] - 2020-01-05
+----------------------
+
+### Changes
+- Increased logging at default log level.  Now users logging in,
+  downloading, uploading, directory creation/removal is logged by
+  default.  Start with `-l error` to silence uftpd again
+
+### Fixes
+- Fix buffer overflow in FTP PORT parser, reported by Aaron Esau
+- Fix TFTP/FTP directory traversal regression , reported by Aaron Esau
+- Fix potential DOS through non-busy loop and segfault, by Aaron Esau
+- Fix potential segfault through empty FTP password, by Aaron Esau
+- Fix potential segfault through FTP PORT command, by Aaron Esau
+
+
+[v2.10][] - 2019-08-15
+----------------------
+
+### Changes
+- Issue #25: Add support for TFTP write support (WRQ)
+- Slightly improved debug messages.
+
+### Fixes
+- Minor fix to TFTP error codes, only use standardized codes, and
+  code 0 + custom error message for everything else
+
+
+[v2.9][] - 2019-07-29
+---------------------
+
+### Changes
+- Reduced log level for "Invalid path" and "Failed realpath()" syslog
+  messages.  Only relevant when debugging.  For use on the Internet it
+  will otherwise cause an excessive amount of logs due to GXHLGSL.txt
+- Debian packaging fixes and updates:
+  - Reverts `-o writable`, due to fixing issue #22
+  - Fixes failing `dpkg -P uftpd` due to bug in postrm script
+
+### Fixes
+- Issue #21: Check for `pkg-config` before looking for deps.
+- Issue #22: Check FTP root security *after* having dropped privs.
+  This means no longer having to run with `-o writable` by default
+- Issue #23: FTP command `CWD /` does not work, affects all clients.
+  This is a regression introduced in v2.8 while fixing #18
+
+
+[v2.8][] - 2019-05-28
+---------------------
+
+### Changes
+- The FTP command processor now always converts all inbound commands
+  to uppercase to handle clients sending commands in lowercase
+- Any arguments to the FTP `LIST` command are now ignored
+- Improved user feedback on bad FTP root error message
+
+### Fixes
+- Fix #18: KDE Dolphin, FTP client interop problems.
+
+
 [v2.7][] - 2019-03-03
 ---------------------
 
@@ -12,7 +72,7 @@
 - Require libuEv v2.2, or later
 
 ### Fixes
-- Issue #17: Issues with relative FTP root when running unpriviliged
+- Issue #17: Issues with relative FTP root when running unprivileged
 
 
 [v2.6][] - 2018-07-03
@@ -58,7 +118,7 @@
 
 ### Changes
 - Handle non-chrooted use-cases better, ensure CWD starts with /
-- Increased default inactivty timer: 20 sec --> 180 sec
+- Increased default inactivity timer: 20 sec --> 180 sec
 - Ensure FTP `PASV` and `PORT` sockets are set non-blocking to prevent
   blocking the event loop
 - [README.md][] updates, add usage section and improve build + install
@@ -143,7 +203,7 @@
 [v2.0][] - 2016-01-22
 ---------------------
 
-Sleak, smart, simple ... UNIX
+Sleek, smart, simple ... UNIX
 
 ### Changes
 - Greatly simplified command line syntax
@@ -188,7 +248,7 @@
 - Major refactor of both FTP and TFTP servers to use libuEv better.
 - Move to use [libite][] v1.0.0 for `strlcpy()`, `strlcat()`, `pidfile()`
   and more.
-- Add proper session timout to TFTP, like what FTP already has.
+- Add proper session timeout to TFTP, like what FTP already has.
 - Add support for `NLST` FTP command, needed for multiple get operations.
   This fixes issue #2, thanks to @oz123 on GitHub for pointing this out!
 - Add support for `FEAT` and `HELP` FTP commands used by some clients.
@@ -269,7 +329,7 @@
 ### Fixes
 - Fix nasty invalid `sizeof()` argument to `recv()` causing uftpd to
   only read 4/8 bytes (32/64 bit arch) at a time from the FTP socket.
-  This should greatly reduce CPU utilisation and improve xfer speeds.
+  This should greatly reduce CPU utilization and improve xfer speeds.
   Found by [Coverity Scan][].
 - Fix minor resource leak in `ftp_session()` when `getsockname()` or
   `getpeername()` fail.  Minor fix because the session exits and the OS
@@ -317,7 +377,7 @@
 - Incompatible changes to the command line arguments, compared to v1.2!
 - Add libuEv as a GIT submodule, handles signals, timers, and all I/O.
 - Refactor all signal handling, timers, and socket `poll()` calls to
-  use libuEv instead.  Much cleaner and maintaiable code as a result.
+  use libuEv instead.  Much cleaner and maintainable code as a result.
 - Clarify copyright claims, not much remains of the original [FtpServer][]
   code, by [Xu Wang][].
 
@@ -392,7 +452,11 @@
   Lines must end in the old `\r\n` format, rather than UNIX `\n`.
 
 
-[UNRELEASED]:    https://github.com/troglobit/uftpd/compare/v2.7...HEAD
+[UNRELEASED]:    https://github.com/troglobit/uftpd/compare/v2.11...HEAD
+[v2.11]:         https://github.com/troglobit/uftpd/compare/v2.10...v2.11
+[v2.10]:         https://github.com/troglobit/uftpd/compare/v2.9...v2.10
+[v2.9]:          https://github.com/troglobit/uftpd/compare/v2.8...v2.9
+[v2.8]:          https://github.com/troglobit/uftpd/compare/v2.7...v2.8
 [v2.7]:          https://github.com/troglobit/uftpd/compare/v2.6...v2.7
 [v2.6]:          https://github.com/troglobit/uftpd/compare/v2.5...v2.6
 [v2.5]:          https://github.com/troglobit/uftpd/compare/v2.4...v2.5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/Makefile.am new/uftpd-2.11/Makefile.am
--- old/uftpd-2.7/Makefile.am   2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/Makefile.am  2020-01-05 08:51:54.000000000 +0100
@@ -1,5 +1,5 @@
 SUBDIRS            = src man
-doc_DATA           = README.md LICENSE
+doc_DATA           = README.md LICENSE ChangeLog.md
 EXTRA_DIST         = README.md LICENSE ChangeLog.md
 
 ## Generate .deb package
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/README.md new/uftpd-2.11/README.md
--- old/uftpd-2.7/README.md     2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/README.md    2020-01-05 08:51:54.000000000 +0100
@@ -25,7 +25,7 @@
 uftpd [-hnsv] [-l LEVEL] [-o ftp=PORT,tftp=PORT,writable] [PATH]
 
   -h         Show this help text
-  -l LEVEL   Set log level: none, err, info, notice (default), debug
+  -l LEVEL   Set log level: none, err, notice (default), info, debug
   -n         Run in foreground, do not detach from controlling terminal
   -o OPT     Options:
                       ftp=PORT
@@ -58,11 +58,14 @@
 
 Set `PORT` to zero (0) to disable either service.
 
-By default, uftpd will exit if it detects the FTP root is writable.  To
-allow writable FTP root:
+New sessions are droppbed by default if uftpd detects the FTP root is
+writable.  To allow writable FTP root:
 
     uftpd -o writable PATH
 
+> **Note:** since v2.11 uftpd logs a lot more events by default.  Set up
+> your syslogd to redirect `LOG_FTP` to a separate log file, or reduce
+> the log level of uftpd using `-l error` to only log errors and higher.
 
 Running from inetd
 ------------------
@@ -88,7 +91,7 @@
 Caveat
 ------
 
-uftpd is primarily not targetted at secure installations, it is targeted
+uftpd is primarily not targeted at secure installations, it is targeted
 at users in need of a *simple* FTP/TFTP server.
 
 uftpd allows symlinks outside the FTP root, as well as a group writable
@@ -105,14 +108,25 @@
 and [lite][].  See their respective README for details, there should be
 no real surprises, both use the familiar configure, make, make install.
 
+To find the two libraries uftpd depends on `pkg-config`.  The package
+name for your Linux distribution varies, on Debian/Ubuntu systems:
+
+```shell
+user@example:~/> sudo apt install pkg-config
+```
+
 uftpd, as well as its dependencies, can be built as `.deb` packages on
-Debian or Ubuntu based distributions.  Simply download each source
-component and run
+Debian or Ubuntu based distributions.  Download and install each of the
+dependencies, and then run
 
     ./autogen.sh      <--- Only needed if using GIT sources
     ./configure
     make package
 
+The `.deb` package takes care of setting up `/etc/inetd.conf`, create an
+`ftp` user and an `/srv/ftp` home directory with write permissions for
+all members of the `users` group.
+
 If you are using a different Linux or UNIX distribution, check the
 output from `./configure --help`, followed by `make all install`.
 For instance, building on [Alpine Linux](https://alpinelinux.org/):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/configure.ac new/uftpd-2.11/configure.ac
--- old/uftpd-2.7/configure.ac  2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/configure.ac 2020-01-05 08:51:54.000000000 +0100
@@ -1,4 +1,5 @@
-AC_INIT([uftpd], [2.7], [https://github.com/troglobit/uftpd/issues],, 
[http://troglobit.com/uftpd.html])
+AC_INIT([uftpd], [2.11], [https://github.com/troglobit/uftpd/issues],,
+       [https://troglobit.com/projects/uftpd/])
 AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
 AM_SILENT_RULES([yes])
 
@@ -11,7 +12,6 @@
 AC_PROG_INSTALL
 
 # Configuration.
-AC_HEADER_STDC
 AC_CHECK_HEADERS(sys/time.h)
 AC_CHECK_FUNCS(strstr getopt getsubopt gettimeofday)
 
@@ -20,6 +20,9 @@
 AC_TYPE_UINT16_T
 AC_TYPE_UINT32_T
 
+# Check for pkg-config first, warn if it's not installed
+PKG_PROG_PKG_CONFIG
+
 # Check for required libraries
 PKG_CHECK_MODULES([uev],  [libuev >= 2.2.0])
 PKG_CHECK_MODULES([lite], [libite >= 1.5.0])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/debian/.gitignore 
new/uftpd-2.11/debian/.gitignore
--- old/uftpd-2.7/debian/.gitignore     1970-01-01 01:00:00.000000000 +0100
+++ new/uftpd-2.11/debian/.gitignore    2020-01-05 08:51:54.000000000 +0100
@@ -0,0 +1,8 @@
+autoreconf.*
+debhelper-build-stamp
+files
+uftpd.debhelper.log
+uftpd.post*
+uftpd.pre*
+uftpd.substvars
+uftpd/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/debian/changelog 
new/uftpd-2.11/debian/changelog
--- old/uftpd-2.7/debian/changelog      2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/debian/changelog     2020-01-05 08:51:54.000000000 +0100
@@ -1,7 +1,48 @@
+uftpd (2.11) unstable; urgency=medium
+
+  * Increased logging at default log level.  Now all relevant interaction
+    is logged.  See the man page for how to adjust.
+  * Fix buffer overflow in FTP PORT parser
+  * Fix TFTP/FTP directory traversal regression
+  * Fix potential DOS through non-busy loop and segfault
+  * Fix potential segfault through empty FTP password
+  * Fix potential segfault through FTP PORT command
+
+ -- Joachim Nilsson <[email protected]>  Sun, 05 Jan 2020 08:49:56 +0100
+
+uftpd (2.10) unstable; urgency=medium
+
+  * Add support for TFTP WRQ, i.e. for clients sending files to server
+  * Fix invalid TFTP error codes, now uses custom error string to code 0
+  * Slightly improved debug messages
+
+ -- Joachim Nilsson <[email protected]>  Thu, 15 Aug 2019 08:59:35 +0200
+
+uftpd (2.9) unstable; urgency=medium
+
+  * Check FTP root security after dropping privileges, issue #22
+  * Revert insecure default: "writable FTP root", introduced in v2.8
+  * Revert part of issue #18 to fix issue #23; "CWD /" doesn't work
+  * Update debian packaging to policy 4.3.0
+  * Fix failing postrm script, causing dpkg -P uftpd to fail hard
+  * Fix spelling errors found by Lintian
+  * Fix package description, more formal and less personal, thanks Lintian
+
+ -- Joachim Nilsson <[email protected]>  Mon, 29 Jul 2019 10:52:49 +0200
+
+uftpd (2.8) unstable; urgency=medium
+
+  * Fix off-by-one regression introduced in v2.5
+  * Convert all commands from user to uppercase for processing
+  * Skip any and *all* FTP LIST options
+  * Enable users group writable FTP root in /etc/inetd.conf
+
+ -- Joachim Nilsson <[email protected]>  Tue, 28 May 2019 06:22:18 +0200
+
 uftpd (2.7) unstable; urgency=medium
 
   * Bug fix release
-  * Fix running uftpd as unpriviliged user using a relative FTP root
+  * Fix running uftpd as unprivileged user using a relative FTP root
 
  -- Joachim Nilsson <[email protected]>  Sun, 03 Mar 2019 11:39:03 +0100
 
@@ -52,7 +93,7 @@
 
     A user must now be member of the users group to share files over
     TFTP/FTP.  Simply add a user to 'users' and they can upload their
-    files to /srv/ftp.  The TFT/FTP server itself has no rights to
+    files to /srv/ftp.  The TFTP/FTP server itself has no rights to
     write there.  Add an uploads/ sub-directory with write perms for
     the 'ftp' user if you want to enable anonymous uploads via FTP.
 
@@ -113,7 +154,7 @@
 
   * New upstream release:
     - Fix insecure chroot(), reported by Coverity Scan, CID #54523
-    - Minor updates to README and a new CHANGLOG file
+    - Minor updates to README and a new CHANGELOG file
 
  -- Joachim Nilsson <[email protected]>  Sun,  2 Feb 2015 06:45:06 +0100
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/debian/compat new/uftpd-2.11/debian/compat
--- old/uftpd-2.7/debian/compat 2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/debian/compat        2020-01-05 08:51:54.000000000 +0100
@@ -1 +1 @@
-7
+10
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/debian/config new/uftpd-2.11/debian/config
--- old/uftpd-2.7/debian/config 2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/debian/config        2020-01-05 08:51:54.000000000 +0100
@@ -1,4 +1,6 @@
-#!/bin/sh -e
+#!/bin/sh
+
+set -e
 . /usr/share/debconf/confmodule
 
 db_title uftpd
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/debian/control 
new/uftpd-2.11/debian/control
--- old/uftpd-2.7/debian/control        2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/debian/control       2020-01-05 08:51:54.000000000 +0100
@@ -1,10 +1,10 @@
 Source: uftpd
 Section: net
-Priority: extra
+Priority: optional
 Maintainer: Joachim Nilsson <[email protected]>
-Build-Depends: debhelper (>= 7.0.0)
-Standards-Version: 3.9.3
-Homepage: http://troglobit.com/uftpd.html
+Build-Depends: debhelper (>= 10)
+Standards-Version: 4.3.0
+Homepage: https://troglobit.com/projects/uftpd/
 
 Package: uftpd
 Architecture: any
@@ -12,12 +12,15 @@
 Depends: openbsd-inetd | inet-superserver, debconf (>= 0.2.17), 
${shlibs:Depends}, ${misc:Depends}
 Provides: ftp-server
 Conflicts: ftp-server, tftpd, tftpd-hpa
-Description: The no nonsense TFTP/FTP server.
- An excellent choice for those of us who never wanted to learn every
- config file format on this planet.  uftpd has no configuration, and
- starts automatically from the traditional UNIX inetd super server,
- neatly tcpwrapped for your safety.
+Description: No nonsense TFTP/FTP server
+ uftpd is a small and simple TFTP and FTP server intended for LANs.  Its
+ author runs it on the Internet, although this is not recommended.
+ .
+ uftpd is set up in a read-only configuration by default.  It has no
+ users, except for anonymous, no configuration file, and is started
+ on-demand by the UNIX inetd super server, neatly tcpwrapped for your
+ safety.
  .
  Hardcore Internet users and anyone concerned about security should
- probably consider a seperate TFTP server and for FTP look at one of:
- vsftpd, proftpd or pure-ftpd.
+ probably consider a separate TFTP server and for FTP look at one of:
+ vsftpd, proftpd, or pure-ftpd.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/debian/postinst 
new/uftpd-2.11/debian/postinst
--- old/uftpd-2.7/debian/postinst       2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/debian/postinst      2020-01-05 08:51:54.000000000 +0100
@@ -1,4 +1,5 @@
 #!/bin/sh
+
 set -e
 
 [ "$1" = "configure" ] || exit 0
@@ -6,8 +7,8 @@
 # Source debconf library.
 . /usr/share/debconf/confmodule
 
-FTPENTRY="ftp          stream  tcp     nowait  root    /usr/sbin/tcpd  
/usr/sbin/in.ftpd"
-TFTPENTRY="tftp                dgram   udp     wait    root    /usr/sbin/tcpd  
/usr/sbin/in.tftpd"
+FTPENTRY="ftp          stream  tcp     nowait  root    /usr/sbin/tcpd  in.ftpd"
+TFTPENTRY="tftp                dgram   udp     wait    root    /usr/sbin/tcpd  
in.tftpd"
 
 if [ ! -f /etc/inetd.conf -a -d /etc/xinetd.d -a -x /usr/sbin/xinetd ]; then
        cat <<-TEXT
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/debian/postrm new/uftpd-2.11/debian/postrm
--- old/uftpd-2.7/debian/postrm 2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/debian/postrm        2020-01-05 08:51:54.000000000 +0100
@@ -1,24 +1,20 @@
 #!/bin/sh
-
 set -e
 
 if [ "$1" = "purge" ]; then
        if command -v update-inetd >/dev/null 2>&1; then
-               update-inetd --pattern '/usr/sbin/uftpd' --remove ".*ftp"
-               update-inetd --pattern '/usr/sbin/in.ftpd' --remove ftp
-               update-inetd --pattern '/usr/sbin/in.tftpd' --remove tftp
+               update-inetd --pattern 'uftpd' --remove ".*ftp"
+               update-inetd --pattern 'in.ftpd' --remove ftp
+               update-inetd --pattern 'in.tftpd' --remove tftp
        fi
 
        # Remove uftpd entries from db
        if [ -f /usr/share/debconf/confmodule ]; then
                . /usr/share/debconf/confmodule
-               db_purge uftpd/ftp
-               db_purge uftpd/tftp
+               db_purge
        fi
 fi
 
-deluser ftp || true
-
-#DEBHELPER#
+deluser --quiet --system ftp
 
 exit 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/debian/preinst 
new/uftpd-2.11/debian/preinst
--- old/uftpd-2.7/debian/preinst        2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/debian/preinst       1970-01-01 01:00:00.000000000 +0100
@@ -1,5 +0,0 @@
-#!/bin/sh
-
-set -e
-
-#DEBHELPER#
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/debian/prerm new/uftpd-2.11/debian/prerm
--- old/uftpd-2.7/debian/prerm  2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/debian/prerm 2020-01-05 08:51:54.000000000 +0100
@@ -2,7 +2,5 @@
 
 set -e
 
-update-inetd --pattern '/usr/sbin/in.ftpd' --multi --disable ftp
-update-inetd --pattern '/usr/sbin/in.tftpd' --multi --disable tftp
-
-#DEBHELPER#
+update-inetd --pattern 'in.ftpd' --multi --disable ftp
+update-inetd --pattern 'in.tftpd' --multi --disable tftp
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/debian/rules new/uftpd-2.11/debian/rules
--- old/uftpd-2.7/debian/rules  2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/debian/rules 2020-01-05 08:51:54.000000000 +0100
@@ -1,45 +1,15 @@
 #!/usr/bin/make -f
-# Simple debian/rules that uses debhelper(7).
-# GNU copyright 1997 by Joey Hess.
-.PHONY: build clean binary-indep binary-arch binary install
+# export DH_VERBOSE=1
+export DEB_BUILD_MAINT_OPTIONS = hardening=+all
 
-build:
-       dh_testdir
-       dh_auto_configure
-       dh_auto_build
+%:
+       dh $@ --with autoreconf,systemd
 
-clean:
-       dh_testdir
-       dh_testroot
-       dh_clean
+override_dh_installchangelogs:
+       dh_installchangelogs ChangeLog.md
 
-install: build
-       dh_testdir
-       dh_testroot
-       dh_prep
-       dh_installdirs
+# Remove LICENSE and ChangeLog.md per Debian Policy
+override_dh_auto_install:
        dh_auto_install
-
-binary-indep: build install
-       dh_installdocs
-       dh_installdebconf
-       dh_installman
-       dh_installchangelogs
-
-binary-arch: build install
-       dh_strip
-       rm -f debian/uftpd/usr/share/doc/uftpd/LICENSE
-       rm -f debian/uftpd/usr/share/doc/uftpd/ChangeLog.md
-       dh_compress
-       dh_fixperms
-       dh_installdebconf
-       dh_installdeb
-       dh_shlibdeps
-       dh_gencontrol
-       dh_md5sums
-       dh_builddeb
-
-source diff:                                                                  
-       @echo >&2 'source and diff are obsolete - use dpkg-source -b'; false
-
-binary: binary-indep binary-arch
+       rm -v debian/uftpd/usr/share/doc/uftpd/LICENSE
+       rm -v debian/uftpd/usr/share/doc/uftpd/ChangeLog.md
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/debian/templates 
new/uftpd-2.11/debian/templates
--- old/uftpd-2.7/debian/templates      2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/debian/templates     2020-01-05 08:51:54.000000000 +0100
@@ -1,10 +1,10 @@
 Template: uftpd/ftp
 Type: boolean
 Default: true
-Description: Enable FTP service
+Description: Enable FTP service?
 
 Template: uftpd/tftp
 Type: boolean
 Default: true
-Description: Enable TFTP service
+Description: Enable TFTP service?
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/man/uftpd.8 new/uftpd-2.11/man/uftpd.8
--- old/uftpd-2.7/man/uftpd.8   2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/man/uftpd.8  2020-01-05 08:51:54.000000000 +0100
@@ -13,9 +13,9 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd Mar 03, 2019
+.Dd Aug 12, 2019
 .Dt UFTPD 8
-.Os "uftpd (2.7)"
+.Os "uftpd (2.10)"
 .Sh NAME
 .Nm uftpd
 .Nd
@@ -23,7 +23,7 @@
 .Sh SYNOPSIS
 .Nm
 .Op Fl hnsv
-.Op Fl l Ar LVL
+.Op Fl l Ar LOG
 .Op Fl o Ar ftp=PORT,tftp=PORT,writable
 .Op Ar PATH
 .Sh DESCRIPTION
@@ -41,8 +41,7 @@
 .Nm
 this means listen to port 21 (FTP) and port 69 (TFTP), serve files from
 .Pa /srv/ftp ,
-and log anything out of the ordinary to syslog.  Messages are written to
-the syslog using the
+and log to syslog.  Messages are written to the syslog using the
 .Nm LOG_FTP
 facility.
 .Pp
@@ -50,16 +49,25 @@
 .Bl -tag -width Ds
 .It Fl h
 Show built-in help text
-.It Fl l Ar LVL
-Set log level: none, err, info,
+.It Fl l Ar LOG
+Set log level: none, err,
 .Ar notice ,
-debug
+info, debug.  By default the log level is
+.Ar notice ,
+which is less verbose than
+.Ar info ,
+but still logs all relevant events: users logging in, uploading,
+downloading, creating and removing directories, etc.  To reduce
+the log level, start
+.Nm
+with
+.Cm Fl l Ar error .
 .It Fl n
 Run in foreground, do not detach from controlling terminal
 .It Fl o
 Set
 .Nm
-option, seprate multiple options with comma:
+option, separate multiple options with comma:
 .Bl -tag
 .It Ar ftp=PORT
 .It Ar tftp=PORT
@@ -130,7 +138,7 @@
 The FTP server currently supports the following requests.
 The case of the requests is ignored.
 .Bl -column "Request" -offset indent
-.It Request Ta "Description"
+.It Sy Request Ta Sy "Description"
 .It ABOR Ta "abort current transfer"
 .It CDUP Ta "shorthand for CD .. command"
 .It CWD Ta "change working directory"
@@ -174,15 +182,18 @@
 .Pp
 The TFTP server currently supports the following requests.
 .Bl -column "Request" -offset indent
-.It RRQ
-.It ERROR
-.It ACK
+.It Sy Request Ta Sy Description
+.It RRQ     Ta Read Request for file, may have options
+.It WRQ     Ta Write Request for file, may have options
+.It DATA    Ta File data, preceded by block n:o
+.It ERROR   Ta Error, end of session
+.It ACK     Ta ACKnowledge DATA or WRQ without options
+.It OACK    Ta Option acknowledged, sent as response to RRQ/WRQ
 .El
 .Pp
 .Nm
 supports TFTP blocksize negotiation, according to RFC2348, so full sized
-Ethernet frames can be used, which greatly speeds up transfers.  Support
-for WRQ is not yet implemented, patches welcome!
+Ethernet frames can be used, which greatly speeds up transfers.
 .Pp
 .Sh FILES
 .Bl -tag -width /etc/ftpwelcome -compact
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/src/.gitignore 
new/uftpd-2.11/src/.gitignore
--- old/uftpd-2.7/src/.gitignore        1970-01-01 01:00:00.000000000 +0100
+++ new/uftpd-2.11/src/.gitignore       2020-01-05 08:51:54.000000000 +0100
@@ -0,0 +1 @@
+uftpd
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/src/common.c new/uftpd-2.11/src/common.c
--- old/uftpd-2.7/src/common.c  2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/src/common.c 2020-01-05 08:51:54.000000000 +0100
@@ -83,20 +83,24 @@
 
                memset(rpath, 0, sizeof(rpath));
                if (!realpath(ptr, rpath)) {
-                       ERR(errno, "Failed realpath(%s)", ptr);
+                       INFO("Failed realpath(%s): %m", ptr);
                        return NULL;
                }
 
+               DBG("realpath(%s) => %s", ptr, rpath);
+
                if (rpath[1] != 0)
                        strlcat(rpath, "/", sizeof(rpath));
                strlcat(rpath, name, sizeof(rpath));
        }
 
-       if (!chrooted && strncmp(dir, home, strlen(home))) {
+       if (!chrooted && strncmp(rpath, home, strlen(home))) {
                DBG("Failed non-chroot dir:%s vs home:%s", dir, home);
                return NULL;
        }
 
+       DBG("Final path to file: %s", rpath);
+
        return rpath;
 }
 
@@ -260,6 +264,15 @@
                if (!fail1 && !fail2)
                        INFO("Successfully dropped privilges to %d:%d 
(uid:gid)", pw->pw_uid, pw->pw_gid);
 
+               /*
+                * Check we don't have write access to the FTP root,
+                * unless explicitly allowed
+                */
+               if (!do_insecure && !access(home, W_OK)) {
+                       ERR(0, "FTP root %s writable, possible security 
violation, aborting session!", home);
+                       goto fail;
+               }
+
                /* On failure, we tried at least.  Only warn once. */
                privs_dropped = 1;
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/src/ftpcmd.c new/uftpd-2.11/src/ftpcmd.c
--- old/uftpd-2.7/src/ftpcmd.c  2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/src/ftpcmd.c 2020-01-05 08:51:54.000000000 +0100
@@ -16,6 +16,7 @@
  */
 
 #include "uftpd.h"
+#include <ctype.h>
 #include <arpa/ftp.h>
 #ifdef HAVE_SYS_TIME_H
 # include <sys/time.h>
@@ -126,7 +127,7 @@
                *cmd      = msg;
                *argument = NULL;
 
-               DBG("Recv: [%s], %d bytes", msg, bytes);
+               DBG("Recv: [%s], %zd bytes", msg, bytes);
 
                return 0;
        }
@@ -149,6 +150,9 @@
        if (ptr)
                *ptr = 0;
 
+       /* Convert command to std ftp upper case, issue #18 */
+       for (ptr = msg; *ptr; ++ptr) *ptr = toupper(*ptr);
+
        DBG("Recv: %s %s", *cmd, *argument ?: "");
 
        return 0;
@@ -329,6 +333,11 @@
                return;
        }
 
+        if (!pass) {
+                send_msg(ctrl->sd, "503 No password given.\r\n");
+                return;
+        }
+
        strlcpy(ctrl->pass, pass, sizeof(ctrl->pass));
        if (check_user_pass(ctrl) < 0) {
                LOG("User %s from %s, invalid password!", ctrl->name, 
ctrl->clientaddr);
@@ -395,6 +404,8 @@
         */
        dir = compose_abspath(ctrl, path);
        if (!dir || stat(dir, &st) || !S_ISDIR(st.st_mode)) {
+               DBG("chrooted:%d, ctrl->cwd: %s, home:%s, dir:%s, len:%zd, 
dirlen:%zd",
+                   chrooted, ctrl->cwd, home, dir, strlen(home), strlen(dir));
                send_msg(ctrl->sd, "550 No such directory.\r\n");
                return;
        }
@@ -404,10 +415,13 @@
 
                DBG("non-chrooted CWD, home:%s, dir:%s, len:%zd, dirlen:%zd",
                    home, dir, len, strlen(dir));
-               if (len <= strlen(dir))
-                       dir += len;
+               dir += len;
        }
+
        snprintf(ctrl->cwd, sizeof(ctrl->cwd), "%s", dir);
+       if (ctrl->cwd[0] == 0)
+               snprintf(ctrl->cwd, sizeof(ctrl->cwd), "/");
+
 done:
        DBG("New CWD: '%s'", ctrl->cwd);
        send_msg(ctrl->sd, "250 OK\r\n");
@@ -430,9 +444,14 @@
                ctrl->data_sd = -1;
        }
 
+        if (!str) {
+                send_msg(ctrl->sd, "500 No PORT specified.\r\n");
+                return;
+        }
+
        /* Convert PORT command's argument to IP address + port */
        sscanf(str, "%d,%d,%d,%d,%d,%d", &a, &b, &c, &d, &e, &f);
-       sprintf(addr, "%d.%d.%d.%d", a, b, c, d);
+       snprintf(addr, sizeof(addr), "%d.%d.%d.%d", a, b, c, d);
 
        /* Check IPv4 address using inet_aton(), throw away converted result */
        if (!inet_aton(addr, &(sin.sin_addr))) {
@@ -748,12 +767,15 @@
                /* Check if client sends ls arguments ... */
                ptr = arg;
                while (*ptr) {
-                       if (*ptr == ' ')
+                       if (isspace(*ptr))
                                ptr++;
-                       if (string_match(ptr, "-l"))
-                               ptr += 2;
-                       else
-                               break;
+
+                       if (*ptr == '-') {
+                               while (*ptr && !isspace(*ptr))
+                                       ptr++;
+                       }
+
+                       break;
                }
 
                /* Strip any "" from "<arg>" */
@@ -761,10 +783,11 @@
                        char *ptr2;
 
                        ptr2 = strchr(&quot[1], '"');
-                       if (ptr2) {
-                               memmove(ptr2, &ptr2[1], strlen(ptr2));
-                               memmove(quot, &quot[1], strlen(quot));
-                       }
+                       if (!ptr2)
+                               break;
+
+                       memmove(ptr2, &ptr2[1], strlen(ptr2));
+                       memmove(quot, &quot[1], strlen(quot));
                }
                arg = ptr;
        }
@@ -782,8 +805,13 @@
        ctrl->file = strdup(arg ? arg : "");
        ctrl->i = 0;
        ctrl->d_num = scandir(path, &ctrl->d, NULL, alphasort);
-       DBG("Reading directory %s ... %d number of entries", path, ctrl->d_num);
+       if (ctrl->d_num == -1) {
+               send_msg(ctrl->sd, "550 No such file or directory.\r\n");
+               DBG("Failed reading directory '%s': %s", path, strerror(errno));
+               return;
+       }
 
+       DBG("Reading directory %s ... %d number of entries", path, ctrl->d_num);
        if (ctrl->data_sd > -1) {
                send_msg(ctrl->sd, "125 Data connection already open; transfer 
starting.\r\n");
                uev_io_init(ctrl->ctx, &ctrl->data_watcher, do_LIST, ctrl, 
ctrl->data_sd, UEV_WRITE);
@@ -903,7 +931,7 @@
                return 1;
        }
 
-       INFO("Data server port estabished.  Waiting for client connnect ...");
+       INFO("Data server port established.  Waiting for client to connect 
...");
        if (listen(ctrl->data_listen_sd, 1) < 0) {
                ERR(errno, "Client data connection failure");
                send_msg(ctrl->sd, "426 Internal server error.\r\n");
@@ -993,7 +1021,7 @@
        num = fread(buf, sizeof(char), sizeof(buf), ctrl->fp);
        if (!num) {
                if (feof(ctrl->fp))
-                       INFO("User %s from %s downloaded %s", ctrl->name, 
ctrl->clientaddr, ctrl->file);
+                       LOG("User %s from %s downloaded '%s'", ctrl->name, 
ctrl->clientaddr, ctrl->file);
                else if (ferror(ctrl->fp))
                        ERR(0, "Error while reading %s", ctrl->file);
                do_abort(ctrl);
@@ -1006,7 +1034,7 @@
 
        gettimeofday(&tv, NULL);
        if (tv.tv_sec - ctrl->tv.tv_sec > 3) {
-               DBG("Sending %d bytes of %s to %s ...", num, ctrl->file, 
ctrl->clientaddr);
+               DBG("Sending %zd bytes of %s to %s ...", num, ctrl->file, 
ctrl->clientaddr);
                ctrl->tv.tv_sec = tv.tv_sec;
        }
 
@@ -1073,7 +1101,13 @@
        struct stat st;
 
        path = compose_abspath(ctrl, file);
-       if (!path || stat(path, &st) || !S_ISREG(st.st_mode)) {
+       if (!path || stat(path, &st)) {
+               LOG("%s: Failed opening '%s'. No such file or directory", 
ctrl->clientaddr, path);
+               send_msg(ctrl->sd, "550 No such file or directory.\r\n");
+               return;
+       }
+       if (!S_ISREG(st.st_mode)) {
+               LOG("%s: Failed opening '%s'. Not a regular file", 
ctrl->clientaddr, path);
                send_msg(ctrl->sd, "550 Not a regular file.\r\n");
                return;
        }
@@ -1107,6 +1141,7 @@
        do_PORT(ctrl, 2);
 }
 
+/* Request to set mtime, ncftp does this */
 static void handle_MDTM(ctrl_t *ctrl, char *file)
 {
        struct stat st;
@@ -1115,16 +1150,19 @@
        char *mtime = NULL;
        char buf[80];
 
-       /* Request to set mtime, ncftp does this */
+        if (!file)
+               goto missing;
+
        ptr = strchr(file, ' ');
        if (ptr) {
                *ptr++ = 0;
                mtime = file;
                file  = ptr;
-       }
+        }
 
        path = compose_abspath(ctrl, file);
        if (!path || stat(path, &st) || !S_ISREG(st.st_mode)) {
+       missing:
                send_msg(ctrl->sd, "550 Not a regular file.\r\n");
                return;
        }
@@ -1149,6 +1187,8 @@
                        ERR(errno, "Failed setting MTIME %s of %s", mtime, 
file);
                        goto fail;
                }
+
+               LOG("User %s from %s changed mtime of %s", ctrl->name, 
ctrl->clientaddr, file);
                (void)stat(path, &st);
        }
 
@@ -1191,7 +1231,7 @@
                return;
        }
        if (bytes == 0) {
-               INFO("User %s at %s uploaded file %s", ctrl->name, 
ctrl->clientaddr, ctrl->file);
+               LOG("User %s from %s uploaded file %s", ctrl->name, 
ctrl->clientaddr, ctrl->file);
                do_abort(ctrl);
                send_msg(ctrl->sd, "226 Transfer complete.\r\n");
                return;
@@ -1216,13 +1256,14 @@
 
        path = compose_abspath(ctrl, file);
        if (!path) {
-               ERR(errno, "Invalid path for %s", file);
+               INFO("Invalid path for %s: %m", file);
                goto fail;
        }
 
        DBG("Trying to write to %s ...", path);
        fp = fopen(path, "wb");
        if (!fp) {
+               /* If EACCESS client is trying to do something disallowed */
                ERR(errno, "Failed writing %s", path);
        fail:
                send_msg(ctrl->sd, "451 Trouble storing file.\r\n");
@@ -1265,11 +1306,14 @@
                fail:   send_msg(ctrl->sd, "550 No such file or 
directory.\r\n");
                else if (EPERM == errno)
                        send_msg(ctrl->sd, "550 Not allowed to remove file or 
directory.\r\n");
+               else if (ENOTEMPTY == errno)
+                       send_msg(ctrl->sd, "550 Not allowed to remove 
directory, not empty.\r\n");
                else
                        send_msg(ctrl->sd, "550 Unknown error.\r\n");
                return;
        }
 
+       LOG("User %s from %s deleted %s", ctrl->name, ctrl->clientaddr, file);
        send_msg(ctrl->sd, "200 Command OK\r\n");
 }
 
@@ -1279,7 +1323,7 @@
 
        path = compose_abspath(ctrl, arg);
        if (!path) {
-               ERR(errno, "Invalid path for %s", arg);
+               INFO("Invalid path for %s: %m", arg);
                goto fail;
        }
 
@@ -1291,6 +1335,7 @@
                return;
        }
 
+       LOG("User %s from %s created directory %s", ctrl->name, 
ctrl->clientaddr, arg);
        send_msg(ctrl->sd, "200 Command OK\r\n");
 }
 
@@ -1393,7 +1438,7 @@
 static void handle_OPTS(ctrl_t *ctrl, char *arg)
 {
        /* OPTS MLST type;size;modify;perm; */
-       if (strstr(arg, "MLST")) {
+       if (arg && strstr(arg, "MLST")) {
                size_t i = 0;
                char *ptr;
                char buf[42] = "200 MLST OPTS ";
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/src/tftpcmd.c new/uftpd-2.11/src/tftpcmd.c
--- old/uftpd-2.7/src/tftpcmd.c 2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/src/tftpcmd.c        2020-01-05 08:51:54.000000000 +0100
@@ -19,6 +19,28 @@
 #include <poll.h>
 #include <arpa/tftp.h>
 
+/*
+ * Theory of operation, from RFC1350:
+ *
+ * Client gets file, RRQ:
+ *
+ *      client                     server
+ *        |----------- RRQ ---------->|
+ *        |<--------- DATA -----------|
+ *        |----------- ACK ---------->|
+ *        |                           |
+ *
+ * Client puts file, WRQ:
+ *
+ *      client                     server
+ *        |----------- WRQ ---------->|
+ *        |<---------- ACK -----------|
+ *        |---------- DATA ---------->|
+ *        |<---------- ACK -----------|
+ *        |                           |
+ *
+ */
+
 /* Send @len bytes data in @ctrl->buf */
 static int do_send(ctrl_t *ctrl, size_t len)
 {
@@ -32,7 +54,7 @@
        if (ctrl->th->th_opcode == OACK)
                hdrsz = ctrl->th->th_stuff - ctrl->buf;
 
-       DBG("tftp sending %zd + %zd bytes ...", hdrsz, len);
+       DBG("SND %c: header size: %zd, data len: %zd ...", ctrl->th->th_code, 
hdrsz, len);
        result = sendto(ctrl->sd, ctrl->buf, hdrsz + len, 0, (struct sockaddr 
*)&ctrl->client_sa, salen);
        if (-1 == result)
                return 1;
@@ -67,21 +89,22 @@
        return do_send(ctrl, len);
 }
 
-#if 0 /* TODO, for client op */
-static int send_ACK(ctrl_t *ctrl)
+static int send_ACK(ctrl_t *ctrl, int block)
 {
-       return 0;
+       memset(ctrl->buf, 0, ctrl->bufsz);
+
+       ctrl->th->th_opcode = htons(ACK);
+       ctrl->th->th_block  = htons(block);
+       DBG("ACK block %d", block);
+
+       return do_send(ctrl, 4);
 }
-#endif
 
 /* Acknowledge options sent by client */
 static int send_OACK(ctrl_t *ctrl)
 {
        char *ptr;
 
-       if (!ctrl->tftp_options)
-               return 0;
-
        memset(ctrl->buf, 0, ctrl->bufsz);
 
        /* Create message */
@@ -99,10 +122,13 @@
        return do_send(ctrl, ptr - ctrl->buf);
 }
 
-static int send_ERROR(ctrl_t *ctrl, int code)
+static int send_ERROR(ctrl_t *ctrl, int code, char *str)
 {
-       char   *str = strerror(code);
-       size_t  len = strlen(str);
+       size_t len;
+
+       if (!str)
+               str = strerror(code);
+       len = strlen(str);
 
        memset(ctrl->buf, 0, ctrl->segsize);
 
@@ -110,6 +136,7 @@
        ctrl->th->th_opcode = htons(ERROR);
        ctrl->th->th_code   = htons(code);
        strlcpy(ctrl->th->th_msg, str, len);
+       DBG("ERR %d: %s", code, str);
 
        /* Error is ASCIIZ string, hence +1 */
        return do_send(ctrl, len + 1);
@@ -138,15 +165,15 @@
        return 0;
 }
 
-/* Parse TFTP payload in RRQ to get filename and optional blksize & timeout */
-static int parse_RRQ(ctrl_t *ctrl, char *buf, size_t len)
+/* Parse TFTP payload in WRQ/RRQ for filename and optional blksize+timeout */
+static int parse_RWRQ(ctrl_t *ctrl, char *buf, size_t len)
 {
        size_t opt_len = strlen(buf) + 1;
 
        /* First opt is always filename */
        ctrl->file = strdup(buf);
        if (!ctrl->file)
-               return send_ERROR(ctrl, ENOMEM);
+               return send_ERROR(ctrl, EUNDEF, NULL);
 
        do {
                /* Prepare to read options */
@@ -167,13 +194,17 @@
 
                        if (alloc_buf(ctrl, sz)) {
                                ERR(errno, "Failed reallocating TFTP buffer 
memory");
-                               return send_ERROR(ctrl, ENOMEM);
+                               return send_ERROR(ctrl, EUNDEF, NULL);
                        }
 
+                       DBG("Negotiated blksize %zd", sz);
                        setbit(&ctrl->tftp_options, 1);
                }
        } while (len);
 
+       if (!ctrl->tftp_options)
+               return 0;
+
        return send_OACK(ctrl);
 }
 
@@ -184,18 +215,67 @@
        path = compose_path(ctrl, ctrl->file);
        if (!path) {
                ERR(errno, "%s: Invalid path to file %s", ctrl->clientaddr, 
ctrl->file);
-               return send_ERROR(ctrl, ENOTFOUND);
+               return send_ERROR(ctrl, ENOTFOUND, NULL);
        }
 
        ctrl->fp = fopen(path, "r");
        if (!ctrl->fp) {
-               ERR(errno, "%s: Failed opening %s", ctrl->clientaddr, path);
-               return send_ERROR(ctrl, ENOTFOUND);
+               ERR(errno, "%s: Failed opening '%s'", ctrl->clientaddr, path);
+               return send_ERROR(ctrl, ENOTFOUND, NULL);
        }
 
        return !send_DATA(ctrl, 0);
 }
 
+static int handle_WRQ(ctrl_t *ctrl)
+{
+       char *path;
+
+       path = compose_path(ctrl, ctrl->file);
+       if (!path) {
+               ERR(errno, "%s: Invalid path to file %s", ctrl->clientaddr, 
ctrl->file);
+               return send_ERROR(ctrl, ENOTFOUND, NULL);
+       }
+
+       ctrl->offset = 1;       /* First expected block */
+       ctrl->fp = fopen(path, "w");
+       if (!ctrl->fp) {
+               ERR(errno, "%s: Failed opening '%s'", ctrl->clientaddr, path);
+               return send_ERROR(ctrl, ENOTFOUND, NULL);
+       }
+
+       if (ctrl->tftp_options)
+               return 0;
+
+       return send_ACK(ctrl, 0);
+}
+
+static int handle_DATA(ctrl_t *ctrl, size_t len)
+{
+       char errmsg[80];
+       int block;
+
+       block = ntohs(ctrl->th->th_block);
+       if (block != ctrl->offset) {
+               snprintf(errmsg, sizeof(errmsg), "Expected block %ld, "
+                        "got DATA for block %d", ctrl->offset, block);
+               return !send_ERROR(ctrl, EUNDEF, errmsg);
+       }
+
+       DBG("tftp block %d writing %zd bytes ...", ctrl->th->th_block, len);
+       if (len != fwrite(ctrl->th->th_data, sizeof(char), len, ctrl->fp)) {
+               snprintf(errmsg, sizeof(errmsg), "Failed writing file: %s",
+                        strerror(errno));
+               return !send_ERROR(ctrl, ENOSPACE, errmsg);
+       }
+
+       ctrl->offset++;
+       if (send_ACK(ctrl, block) || len < ctrl->segsize)
+               return 0;
+
+       return 1;
+}
+
 /* TODO: Add support for ACK timeout and resend */
 static int handle_ACK(ctrl_t *ctrl, int block)
 {
@@ -243,22 +323,40 @@
        switch (op) {
        case RRQ:
                len -= ctrl->th->th_stuff - ctrl->buf;
-               if (parse_RRQ(ctrl, ctrl->th->th_stuff, len)) {
+               if (parse_RWRQ(ctrl, ctrl->th->th_stuff, len)) {
                        ERR(errno, "Failed parsing TFTP RRQ");
                        active = 0;
                        break;
                }
-               DBG("tftp RRQ %s from %s:%d", ctrl->file, ctrl->clientaddr, 
port);
+               LOG("tftp RRQ '%s' from %s:%d", ctrl->file, ctrl->clientaddr, 
port);
                active = handle_RRQ(ctrl);
                free(ctrl->file);
                break;
 
+       case WRQ:
+               len -= ctrl->th->th_stuff - ctrl->buf;
+               if (parse_RWRQ(ctrl, ctrl->th->th_stuff, len)) {
+                       ERR(errno, "Failed parsing TFTP WRQ");
+                       active = 0;
+                       break;
+               }
+               LOG("tftp WRQ '%s' from %s:%d", ctrl->file, ctrl->clientaddr, 
port);
+               handle_WRQ(ctrl);
+               free(ctrl->file);
+               break;
+
+       case DATA:              /* Received data after WRQ */
+               INFO("tftp DATA '%s' from %s:%d", ctrl->file, ctrl->clientaddr, 
port);
+               len -= ctrl->th->th_data - ctrl->buf;
+               active = handle_DATA(ctrl, len);
+               break;
+
        case ERROR:
                DBG("tftp ERROR: %hd", ntohs(ctrl->th->th_code));
                active = 0;
                break;
 
-       case ACK:
+       case ACK:               /* Sent for each DATA we send in a RRQ */
                DBG("tftp ACK, block # %hu", block);
                active = handle_ACK(ctrl, block);
                break;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/src/uftpd.c new/uftpd-2.11/src/uftpd.c
--- old/uftpd-2.7/src/uftpd.c   2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/src/uftpd.c  2020-01-05 08:51:54.000000000 +0100
@@ -55,7 +55,7 @@
                printf("\nUsage: %s [-hnsv] [-l LEVEL] [-o 
ftp=PORT,tftp=PORT,writable] [PATH]\n\n", prognm);
 
        printf("  -h         Show this help text\n"
-              "  -l LEVEL   Set log level: none, err, info, notice (default), 
debug\n");
+              "  -l LEVEL   Set log level: none, err, notice (default), info, 
debug\n");
        if (!is_inetd)
                printf("  -n         Run in foreground, do not detach from 
controlling terminal\n"
                       "  -o OPT     Options:\n"
@@ -100,7 +100,7 @@
  */
 static void sigquit_cb(uev_t *w, void *arg, int events)
 {
-       INFO("Recieved signal %d, exiting ...", w->signo);
+       INFO("Received signal %d, exiting ...", w->signo);
 
        /* Forward signal to any children in this process group. */
        if (killpg(getpgrp(), SIGTERM))
@@ -139,25 +139,6 @@
        return port;
 }
 
-/*
- * Check that we don't have write access to the FTP root,
- * unless explicitly allowed
- */
-static int security_check(char *home)
-{
-       if (access(home, F_OK)) {
-               ERR(errno, "Cannot access FTP root %s", home);
-               return 1;
-       }
-
-       if (!do_insecure && !access(home, W_OK)) {
-               ERR(0, "FTP root %s writable, possible security violation!", 
home);
-               return 1;
-       }
-
-       return 0;
-}
-
 static int init(uev_ctx_t *ctx)
 {
        /* Figure out FTP/TFTP ports */
@@ -178,8 +159,10 @@
                }
        }
 
-       if (!home || security_check(home))
+       if (!home || access(home, F_OK)) {
+               ERR(errno, "Cannot access FTP root %s", home ? home : "NIL");
                return 1;
+       }
 
        return uev_init(ctx);
 }
@@ -359,7 +342,7 @@
        if (optind < argc) {
                home = realpath(argv[optind], NULL);
                if (!home) {
-                       ERR(errno, "Invalid FTP root");
+                       ERR(errno, "Invalid FTP root %s", argv[optind]);
                        return 1;
                }
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/uftpd-2.7/src/uftpd.h new/uftpd-2.11/src/uftpd.h
--- old/uftpd-2.7/src/uftpd.h   2019-03-03 15:52:00.000000000 +0100
+++ new/uftpd-2.11/src/uftpd.h  2020-01-05 08:51:54.000000000 +0100
@@ -97,6 +97,7 @@
 extern int   do_syslog;         /* Bool: False at daemon start      */
 extern int   do_ftp;            /* Port: FTP port, or disabled      */
 extern int   do_tftp;           /* Port: TFTP port, or disabled     */
+extern int   do_insecure;      /* Bool: Allow writable root or not */
 extern struct passwd *pw;       /* FTP user's passwd entry          */
 
 typedef struct tftphdr tftp_t;
@@ -125,7 +126,7 @@
        char     pending;       /* Pending op: LIST, RETR, STOR */
        char     list_mode;     /* Current LIST mode */
        char    *file;          /* Current file name to fetch */
-       off_t    offset;        /* Offset in current file, for REST */
+       off_t    offset;        /* Offset/block in current file, for REST/WRQ */
        FILE    *fp;            /* Current file in operation */
        int      i;             /* i of d_num in 'd' */
        int      d_num;         /* Number of entries in 'd' */


Reply via email to