Blame SOURCES/0049-Add-new-filename-generation-algorithm-for-STOU-comma.patch

f22e83
From 1203b943b369651d96d057f8190f14f015e6ff0b Mon Sep 17 00:00:00 2001
f22e83
From: =?UTF-8?q?Ond=C5=99ej=20Lyson=C4=9Bk?= <olysonek@redhat.com>
f22e83
Date: Tue, 6 Feb 2018 13:30:44 +0100
f22e83
Subject: [PATCH 49/59] Add new filename generation algorithm for STOU command
f22e83
f22e83
A new configuration option 'better_stou' can be used to enable
f22e83
a better algorithm for generating unique filenames.
f22e83
f22e83
Resolves: rhbz#1479237
f22e83
---
f22e83
 parseconf.c   |   1 +
f22e83
 postlogin.c   | 176 +++++++++++++++++++++++++++++++++++++++++++++++++---------
f22e83
 sysutil.c     |   3 +
f22e83
 sysutil.h     |   3 +-
f22e83
 tunables.c    |   2 +
f22e83
 tunables.h    |   3 +
f22e83
 vsftpd.conf.5 |   5 ++
f22e83
 7 files changed, 166 insertions(+), 27 deletions(-)
f22e83
f22e83
diff --git a/parseconf.c b/parseconf.c
f22e83
index 33a1349..47b54f1 100644
f22e83
--- a/parseconf.c
f22e83
+++ b/parseconf.c
f22e83
@@ -111,6 +111,7 @@ parseconf_bool_array[] =
f22e83
   { "http_enable", &tunable_http_enable },
f22e83
   { "seccomp_sandbox", &tunable_seccomp_sandbox },
f22e83
   { "allow_writeable_chroot", &tunable_allow_writeable_chroot },
f22e83
+  { "better_stou", &tunable_better_stou },
f22e83
   { 0, 0 }
f22e83
 };
f22e83
 
f22e83
diff --git a/postlogin.c b/postlogin.c
f22e83
index 8363c9c..7c749ef 100644
f22e83
--- a/postlogin.c
f22e83
+++ b/postlogin.c
f22e83
@@ -29,6 +29,7 @@
f22e83
 #include "opts.h"
f22e83
 
f22e83
 #include <errno.h>
f22e83
+#include <stdio.h>
f22e83
 
f22e83
 /* Private local functions */
f22e83
 static void handle_pwd(struct vsf_session* p_sess);
f22e83
@@ -1028,6 +1029,114 @@ handle_stor(struct vsf_session* p_sess)
f22e83
   handle_upload_common(p_sess, 0, 0);
f22e83
 }
f22e83
 
f22e83
+/* Based on __gen_tempname() from glibc - thanks, glibc! Relicensed
f22e83
+ * from LGPL2.1+ to GPL2.
f22e83
+ */
f22e83
+static int
f22e83
+create_unique_file(struct vsf_session* p_sess, struct mystr* p_outstr,
f22e83
+                   const struct mystr* p_base_str,
f22e83
+                   int (*access_checker)(const struct mystr*))
f22e83
+{
f22e83
+  struct mystr s_result = INIT_MYSTR;
f22e83
+  const int suffix_len = 6;
f22e83
+  unsigned int count;
f22e83
+  static unsigned long long int value;
f22e83
+  unsigned long long int random_time_bits;
f22e83
+  int fd = -1;
f22e83
+  /* These are the characters used in temporary file names.  */
f22e83
+  struct mystr s_letters = INIT_MYSTR;
f22e83
+  unsigned int s_letters_len;
f22e83
+  int base_len;
f22e83
+
f22e83
+  str_alloc_text(&s_letters,
f22e83
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
f22e83
+  s_letters_len = str_getlen(&s_letters);
f22e83
+
f22e83
+  /* A lower bound on the number of temporary files to attempt to
f22e83
+     generate.  The maximum total number of temporary file names that
f22e83
+     can exist for a given template is 62**6.  It should never be
f22e83
+     necessary to try all of these combinations.  Instead if a reasonable
f22e83
+     number of names is tried (we define reasonable as 62**3) fail to
f22e83
+     give the system administrator the chance to remove the problems.  */
f22e83
+#define ATTEMPTS_MIN (62 * 62 * 62)
f22e83
+
f22e83
+  /* The number of times to attempt to generate a temporary file. */
f22e83
+#if ATTEMPTS_MIN < TMP_MAX
f22e83
+  unsigned int attempts = TMP_MAX;
f22e83
+#else
f22e83
+  unsigned int attempts = ATTEMPTS_MIN;
f22e83
+#endif
f22e83
+#undef ATTEMPTS_MIN
f22e83
+
f22e83
+  {
f22e83
+    long sec = vsf_sysutil_get_time_sec();
f22e83
+    long usec = vsf_sysutil_get_time_usec();
f22e83
+    random_time_bits = ((unsigned long long int) usec << 16) ^ sec;
f22e83
+    value += random_time_bits ^ vsf_sysutil_getpid();
f22e83
+  }
f22e83
+
f22e83
+  if (str_isempty(p_base_str))
f22e83
+  {
f22e83
+    const char *base = "STOU.";
f22e83
+    base_len = vsf_sysutil_strlen(base);
f22e83
+    str_reserve(&s_result, base_len + suffix_len);
f22e83
+    str_alloc_text(&s_result, base);
f22e83
+  }
f22e83
+  else
f22e83
+  {
f22e83
+    str_reserve(&s_result, str_getlen(p_base_str) + suffix_len + 1);
f22e83
+    str_copy(&s_result, p_base_str);
f22e83
+    str_append_char(&s_result, '.');
f22e83
+    base_len = str_getlen(&s_result);
f22e83
+  }
f22e83
+
f22e83
+  for (count = 0; count < attempts; value += 7777, ++count)
f22e83
+  {
f22e83
+    unsigned long long v = value;
f22e83
+    str_trunc(&s_result, base_len);
f22e83
+    for (int i = 0; i < suffix_len; ++i)
f22e83
+    {
f22e83
+      char c;
f22e83
+      c = str_get_char_at(&s_letters, v % s_letters_len);
f22e83
+      v /= s_letters_len;
f22e83
+      str_append_char(&s_result, c);
f22e83
+    }
f22e83
+    if (!access_checker(&s_result))
f22e83
+    {
f22e83
+      /* If we generate a filename which is not allowed, we fail immediatelly,
f22e83
+       * without trying any other possibilities. This is to prevent attackers
f22e83
+       * from keeping us busy.
f22e83
+       */
f22e83
+      vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
f22e83
+      break;
f22e83
+    }
f22e83
+    fd = str_create_exclusive(&s_result);
f22e83
+    if (vsf_sysutil_retval_is_error(fd))
f22e83
+    {
f22e83
+      if (kVSFSysUtilErrEXIST == vsf_sysutil_get_error())
f22e83
+      {
f22e83
+        continue;
f22e83
+      }
f22e83
+      else
f22e83
+      {
f22e83
+        vsf_cmdio_write(p_sess, FTP_UPLOADFAIL, "Could not create file.");
f22e83
+        break;
f22e83
+      }
f22e83
+    }
f22e83
+    else
f22e83
+    {
f22e83
+      break;
f22e83
+    }
f22e83
+  }
f22e83
+  if (!vsf_sysutil_retval_is_error(fd))
f22e83
+  {
f22e83
+    str_copy(p_outstr, &s_result);
f22e83
+  }
f22e83
+  str_free(&s_letters);
f22e83
+  str_free(&s_result);
f22e83
+  return fd;
f22e83
+}
f22e83
+
f22e83
 static void
f22e83
 handle_upload_common(struct vsf_session* p_sess, int is_append, int is_unique)
f22e83
 {
f22e83
@@ -1049,41 +1158,56 @@ handle_upload_common(struct vsf_session* p_sess, int is_append, int is_unique)
f22e83
     return;
f22e83
   }
f22e83
   resolve_tilde(&p_sess->ftp_arg_str, p_sess);
f22e83
-  p_filename = &p_sess->ftp_arg_str;
f22e83
-  if (is_unique)
f22e83
-  {
f22e83
-    get_unique_filename(&s_filename, p_filename);
f22e83
-    p_filename = &s_filename;
f22e83
-  }
f22e83
   vsf_log_start_entry(p_sess, kVSFLogEntryUpload);
f22e83
   str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
f22e83
   prepend_path_to_filename(&p_sess->log_str);
f22e83
-  if (!vsf_access_check_file(p_filename))
f22e83
-  {
f22e83
-    vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
f22e83
-    return;
f22e83
-  }
f22e83
-  /* NOTE - actual file permissions will be governed by the tunable umask */
f22e83
-  /* XXX - do we care about race between create and chown() of anonymous
f22e83
-   * upload?
f22e83
-   */
f22e83
-  if (is_unique || (p_sess->is_anonymous && !tunable_anon_other_write_enable))
f22e83
+  p_filename = &p_sess->ftp_arg_str;
f22e83
+  if (is_unique && tunable_better_stou)
f22e83
   {
f22e83
-    new_file_fd = str_create_exclusive(p_filename);
f22e83
+    new_file_fd = create_unique_file(p_sess, &s_filename, p_filename,
f22e83
+                                     vsf_access_check_file);
f22e83
+    if (vsf_sysutil_retval_is_error(new_file_fd))
f22e83
+    {
f22e83
+      return;
f22e83
+    }
f22e83
+    p_filename = &s_filename;
f22e83
   }
f22e83
   else
f22e83
   {
f22e83
-    /* For non-anonymous, allow open() to overwrite or append existing files */
f22e83
-    new_file_fd = str_create(p_filename);
f22e83
-    if (!is_append && offset == 0)
f22e83
+    if (is_unique)
f22e83
     {
f22e83
-      do_truncate = 1;
f22e83
+      get_unique_filename(&s_filename, p_filename);
f22e83
+      p_filename = &s_filename;
f22e83
+    }
f22e83
+    if (!vsf_access_check_file(p_filename))
f22e83
+    {
f22e83
+      vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
f22e83
+      return;
f22e83
+    }
f22e83
+    /* NOTE - actual file permissions will be governed by the tunable umask */
f22e83
+    /* XXX - do we care about race between create and chown() of anonymous
f22e83
+     * upload?
f22e83
+     */
f22e83
+    if (is_unique || (p_sess->is_anonymous && !tunable_anon_other_write_enable))
f22e83
+    {
f22e83
+      new_file_fd = str_create_exclusive(p_filename);
f22e83
+    }
f22e83
+    else
f22e83
+    {
f22e83
+      /* For non-anonymous, allow open() to overwrite or append existing
f22e83
+       * files
f22e83
+       */
f22e83
+      new_file_fd = str_create(p_filename);
f22e83
+      if (!is_append && offset == 0)
f22e83
+      {
f22e83
+        do_truncate = 1;
f22e83
+      }
f22e83
+    }
f22e83
+    if (vsf_sysutil_retval_is_error(new_file_fd))
f22e83
+    {
f22e83
+      vsf_cmdio_write(p_sess, FTP_UPLOADFAIL, "Could not create file.");
f22e83
+      return;
f22e83
     }
f22e83
-  }
f22e83
-  if (vsf_sysutil_retval_is_error(new_file_fd))
f22e83
-  {
f22e83
-    vsf_cmdio_write(p_sess, FTP_UPLOADFAIL, "Could not create file.");
f22e83
-    return;
f22e83
   }
f22e83
   created = 1;
f22e83
   vsf_sysutil_fstat(new_file_fd, &s_p_statbuf);
f22e83
diff --git a/sysutil.c b/sysutil.c
f22e83
index 1c0422e..e847650 100644
f22e83
--- a/sysutil.c
f22e83
+++ b/sysutil.c
f22e83
@@ -1666,6 +1666,9 @@ vsf_sysutil_get_error(void)
f22e83
     case EAGAIN:
f22e83
       retval = kVSFSysUtilErrAGAIN;
f22e83
       break;
f22e83
+    case EEXIST:
f22e83
+      retval = kVSFSysUtilErrEXIST;
f22e83
+      break;
f22e83
     default:
f22e83
       break;
f22e83
   }
f22e83
diff --git a/sysutil.h b/sysutil.h
f22e83
index be727f5..7a59f13 100644
f22e83
--- a/sysutil.h
f22e83
+++ b/sysutil.h
f22e83
@@ -19,7 +19,8 @@ enum EVSFSysUtilError
f22e83
   kVSFSysUtilErrOPNOTSUPP,
f22e83
   kVSFSysUtilErrACCES,
f22e83
   kVSFSysUtilErrNOENT,
f22e83
-  kVSFSysUtilErrAGAIN
f22e83
+  kVSFSysUtilErrAGAIN,
f22e83
+  kVSFSysUtilErrEXIST
f22e83
 };
f22e83
 enum EVSFSysUtilError vsf_sysutil_get_error(void);
f22e83
 
f22e83
diff --git a/tunables.c b/tunables.c
f22e83
index 9680528..5ec2bdc 100644
f22e83
--- a/tunables.c
f22e83
+++ b/tunables.c
f22e83
@@ -92,6 +92,7 @@ int tunable_ftp_enable;
f22e83
 int tunable_http_enable;
f22e83
 int tunable_seccomp_sandbox;
f22e83
 int tunable_allow_writeable_chroot;
f22e83
+int tunable_better_stou;
f22e83
 
f22e83
 unsigned int tunable_accept_timeout;
f22e83
 unsigned int tunable_connect_timeout;
f22e83
@@ -239,6 +240,7 @@ tunables_load_defaults()
f22e83
   tunable_http_enable = 0;
f22e83
   tunable_seccomp_sandbox = 0;
f22e83
   tunable_allow_writeable_chroot = 0;
f22e83
+  tunable_better_stou = 0;
f22e83
 
f22e83
   tunable_accept_timeout = 60;
f22e83
   tunable_connect_timeout = 60;
f22e83
diff --git a/tunables.h b/tunables.h
f22e83
index a466427..85ea1a8 100644
f22e83
--- a/tunables.h
f22e83
+++ b/tunables.h
f22e83
@@ -93,6 +93,9 @@ extern int tunable_ftp_enable;                /* Allow FTP protocol */
f22e83
 extern int tunable_http_enable;               /* Allow HTTP protocol */
f22e83
 extern int tunable_seccomp_sandbox;           /* seccomp filter sandbox */
f22e83
 extern int tunable_allow_writeable_chroot;    /* Allow misconfiguration */
f22e83
+extern int tunable_better_stou;               /* Use better file name generation
f22e83
+                                               * algorithm for the STOU command
f22e83
+					       */
f22e83
 
f22e83
 /* Integer/numeric defines */
f22e83
 extern unsigned int tunable_accept_timeout;
f22e83
diff --git a/vsftpd.conf.5 b/vsftpd.conf.5
f22e83
index 43b0435..6911a73 100644
f22e83
--- a/vsftpd.conf.5
f22e83
+++ b/vsftpd.conf.5
f22e83
@@ -65,6 +65,11 @@ creates an 'etc' directory in the new root directory, they could potentially
f22e83
 trick the C library into loading a user-created configuration file from the
f22e83
 /etc/ directory.
f22e83
 
f22e83
+Default: NO
f22e83
+.TP
f22e83
+.B better_stou
f22e83
+Use better file name generation algorithm for the STOU command.
f22e83
+
f22e83
 Default: NO
f22e83
 .TP
f22e83
 .B anon_mkdir_write_enable
f22e83
-- 
f22e83
2.14.4
f22e83