Source: vsftpd
Severity: wishlist
Tags: patch

Dear Maintainer,

vsftpd provides very basic name-based access control via the deny_file option.
Files with names matching a provided pattern cannot be targeted by any
operation other than a directory listing.

The provided patch allows to restrict file uploads and downloads using the
same simple pattern specification as deny_file and hide_file introducing the
new options upload_file and download_file. If these options are specified, a
file is only permitted to be up- or downloaded if its name matches the
corresponding pattern - in addition to not matching deny_file.

The provision of distinct filename patterns for up- and download is useful
in many use cases where the served files (e.g. configurations) are different
from the collected ones (e.g. status reports). Especially in the context of
legacy ftp without SSL-secured access, this avoids risking the server to be
misused as a data relay for third parties.

The provided patch:
 - introduces the new options upload_file and download_file,
   -> tunables.h, tunables.c, parseconf.c
 - provides corresonding access checkers,
   -> access.h, access.c
 - utilizes these access checkers in the corresponding operations, and
   -> postlogin.c
 - documents the new options in the manual page.
   -> vsftpd.conf.5

The patch has been generated on the git repo:
 - cloned on 2015-12-21 and
 - patched with all patches included under debian/patches/.

Thus, the patch should be applied after all other patches in the root of the
repo using:

  patch -p1 < upload_download_filename_pattern.patch


Description: Restrict upload and download of files to certain name patterns.
Author: Thomas B. Preußer <thomas.preus...@utexas.edu>
Last-Update: 2015-12-21
---
This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
===================================================================
Index:  access.c
        access.h
        parseconf.c
        postlogin.c
        tunables.c
        tunables.h
        vsftpd.conf.5

--- vsftpd.orig/access.c
+++ vsftpd/access.c
@@ -12,11 +12,27 @@
 #include "tunables.h"
 #include "str.h"
 
+static int
+vsf_match_filter(struct mystr const *const p_filename_str,
+                struct mystr const *const p_access_str) {
+
+  unsigned  iters = 0;
+  if (vsf_filename_passes_filter(p_filename_str, p_access_str, &iters))
+  {
+    return 1;
+  }
+  else
+  {
+    struct str_locate_result const loc_res =
+      str_locate_str(p_filename_str, p_access_str);
+    return  loc_res.found;
+  }
+}
+
 int
 vsf_access_check_file(const struct mystr* p_filename_str)
 {
   static struct mystr s_access_str;
-  unsigned int iters = 0;
 
   if (!tunable_deny_file)
   {
@@ -26,27 +42,21 @@
   {
     str_alloc_text(&s_access_str, tunable_deny_file);
   }
-  if (vsf_filename_passes_filter(p_filename_str, &s_access_str, &iters))
+
+  if (vsf_match_filter(p_filename_str, &s_access_str))
   {
     return 0;
   }
   else
   {
-    struct str_locate_result loc_res =
-      str_locate_str(p_filename_str, &s_access_str);
-    if (loc_res.found)
-    {
-      return 0;
-    }
+    return 1;
   }
-  return 1;
 }
 
 int
 vsf_access_check_file_visible(const struct mystr* p_filename_str)
 {
   static struct mystr s_access_str;
-  unsigned int iters = 0;
 
   if (!tunable_hide_file)
   {
@@ -56,19 +66,47 @@
   {
     str_alloc_text(&s_access_str, tunable_hide_file);
   }
-  if (vsf_filename_passes_filter(p_filename_str, &s_access_str, &iters))
+
+  if (vsf_match_filter(p_filename_str, &s_access_str))
   {
     return 0;
   }
   else
   {
-    struct str_locate_result loc_res =
-      str_locate_str(p_filename_str, &s_access_str);
-    if (loc_res.found)
-    {
-      return 0;
-    }
+    return 1;
+  }
+}
+
+int
+vsf_access_check_file_upload(const struct mystr* p_filename_str)
+{
+  static struct mystr s_access_str;
+
+  if (!tunable_upload_file)
+  {
+    return 1;
+  }
+  if (str_isempty(&s_access_str))
+  {
+    str_alloc_text(&s_access_str, tunable_upload_file);
   }
-  return 1;
+
+  return  vsf_match_filter(p_filename_str, &s_access_str);
 }
 
+int
+vsf_access_check_file_download(const struct mystr* p_filename_str)
+{
+  static struct mystr s_access_str;
+
+  if (!tunable_download_file)
+  {
+    return 1;
+  }
+  if (str_isempty(&s_access_str))
+  {
+    str_alloc_text(&s_access_str, tunable_download_file);
+  }
+
+  return  vsf_match_filter(p_filename_str, &s_access_str);
+}
--- vsftpd.orig/access.h
+++ vsftpd/access.h
@@ -25,5 +25,27 @@
  */
 int vsf_access_check_file_visible(const struct mystr* p_filename_str);
 
+/* vsf_access_check_file_upload()
+ * PURPOSE
+ * Check whether the current session has permission to upload a file
+ * using the given filename.
+ * PARAMETERS
+ * p_filename_str  - the filename to check upload permission for
+ * RETURNS
+ * Returns 1 if the file may be uploaded, otherwise 0.
+ */
+int vsf_access_check_file_upload(const struct mystr* p_filename_str);
+
+/* vsf_access_check_file_download()
+ * PURPOSE
+ * Check whether the current session has permission to download a file
+ * with the given filename.
+ * PARAMETERS
+ * p_filename_str  - the filename to check download permission for
+ * RETURNS
+ * Returns 1 if the file may be downloaded, otherwise 0.
+ */
+int vsf_access_check_file_download(const struct mystr* p_filename_str);
+
 #endif /* VSF_ACCESS_H */
 
--- vsftpd.orig/parseconf.c
+++ vsftpd/parseconf.c
@@ -171,6 +171,8 @@
   { "cmds_allowed", &tunable_cmds_allowed },
   { "hide_file", &tunable_hide_file },
   { "deny_file", &tunable_deny_file },
+  { "upload_file", &tunable_upload_file },
+  { "download_file", &tunable_download_file },
   { "user_sub_token", &tunable_user_sub_token },
   { "email_password_file", &tunable_email_password_file },
   { "rsa_cert_file", &tunable_rsa_cert_file },
--- vsftpd.orig/postlogin.c
+++ vsftpd/postlogin.c
@@ -671,7 +671,8 @@
   vsf_log_start_entry(p_sess, kVSFLogEntryDownload);
   str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
   prepend_path_to_filename(&p_sess->log_str);
-  if (!vsf_access_check_file(&p_sess->ftp_arg_str))
+  if (!vsf_access_check_file(&p_sess->ftp_arg_str) ||
+      !vsf_access_check_file_download(&p_sess->ftp_arg_str))
   {
     vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
     return;
@@ -1040,7 +1041,8 @@
   vsf_log_start_entry(p_sess, kVSFLogEntryUpload);
   str_copy(&p_sess->log_str, &p_sess->ftp_arg_str);
   prepend_path_to_filename(&p_sess->log_str);
-  if (!vsf_access_check_file(p_filename))
+  if (!vsf_access_check_file(p_filename) ||
+      !vsf_access_check_file_upload(p_filename))
   {
     vsf_cmdio_write(p_sess, FTP_NOPERM, "Permission denied.");
     return;
--- vsftpd.orig/tunables.c
+++ vsftpd/tunables.c
@@ -135,6 +135,8 @@
 const char* tunable_cmds_denied;
 const char* tunable_hide_file;
 const char* tunable_deny_file;
+const char* tunable_upload_file;
+const char* tunable_download_file;
 const char* tunable_user_sub_token;
 const char* tunable_email_password_file;
 const char* tunable_rsa_cert_file;
@@ -280,6 +282,8 @@
   install_str_setting(0, &tunable_cmds_denied);
   install_str_setting(0, &tunable_hide_file);
   install_str_setting(0, &tunable_deny_file);
+  install_str_setting(0, &tunable_upload_file);
+  install_str_setting(0, &tunable_download_file);
   install_str_setting(0, &tunable_user_sub_token);
   install_str_setting("/etc/vsftpd.email_passwords",
                       &tunable_email_password_file);
--- vsftpd.orig/tunables.h
+++ vsftpd/tunables.h
@@ -137,6 +137,8 @@
 extern const char* tunable_cmds_allowed;
 extern const char* tunable_hide_file;
 extern const char* tunable_deny_file;
+extern const char* tunable_upload_file;
+extern const char* tunable_download_file;
 extern const char* tunable_user_sub_token;
 extern const char* tunable_email_password_file;
 extern const char* tunable_rsa_cert_file;
--- vsftpd.orig/vsftpd.conf.5
+++ vsftpd/vsftpd.conf.5
@@ -847,6 +847,16 @@
 
 Default: (none)
 .TP
+.B download_file
+This option may be set to restrict downloads to files with names matching the
+specified pattern. If a filename also matches the
+.BR deny_file
+pattern, the denial takes precedence. For usage and pattern details, see the
+.BR deny_file
+option.
+
+Default: (none)
+.TP
 .B dsa_cert_file
 This option specifies the location of the DSA certificate to use for SSL
 encrypted connections.
@@ -982,6 +992,16 @@
 
 Default: DES-CBC3-SHA
 .TP
+.B upload_file
+This option may be set to restrict uploads to files with names matching the
+specified pattern. If a filename also matches the
+.BR deny_file
+pattern, the denial takes precedence. For usage and pattern details, see the
+.BR deny_file
+option.
+
+Default: (none)
+.TP
 .B user_config_dir
 This powerful option allows the override of any config option specified in
 the manual page, on a per-user basis. Usage is simple, and is best illustrated

Reply via email to