chiark / gitweb /
tmpfiles: use safe_glob()
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Wed, 26 Apr 2017 03:50:35 +0000 (23:50 -0400)
committerSven Eden <yamakuzure@gmx.net>
Tue, 25 Jul 2017 07:46:52 +0000 (09:46 +0200)
This filters out "." and ".." from glob results. Fixes #5655 and #5644.

Any judgements on whether the path is "safe" are removed. We will not remove
"/" under any name (including "/../" and such), but we will remove stuff that
is specified using paths that include "//", "/./" and "/../". Such paths can be
created when joining strings automatically, or for other reasons, and people
generally know what ".." and "." is.

Tests are added to make sure that the helper functions behave as expected.

src/basic/rm-rf.c
src/test/test-path-util.c

index 565f240e120d0e6750eda66ffc91a0706fb616b1..87a7bd845fba3195f813e1ba3d11d49a82874751 100644 (file)
@@ -184,19 +184,12 @@ int rm_rf(const char *path, RemoveFlags flags) {
         /* We refuse to clean the root file system with this
          * call. This is extra paranoia to never cause a really
          * seriously broken system. */
-        if (path_equal(path, "/")) {
+        if (path_equal_or_files_same(path, "/")) {
                 log_error("Attempted to remove entire root file system, and we can't allow that.");
                 return -EPERM;
         }
 
 #if 0 /// elogind does not support BTRFS this directly
-        /* Another safe-check. Removing "/path/.." could easily remove entire root as well.
-         * It's especially easy to do using globs in tmpfiles, like "/path/.*", which the glob()
-         * function expands to both "/path/." and "/path/..".
-         * Return -EINVAL to be consistent with rmdir("/path/."). */
-        if (endswith(path, "/..") || endswith(path, "/../"))
-                return -EINVAL;
-
         if ((flags & (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) == (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) {
                 /* Try to remove as subvolume first */
                 r = btrfs_subvol_remove(path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
index 4d957cd3fb90473fa256bc6a1facc0ba919b125d..72b931568d004fcbc963fed55c0a076ce196d63f 100644 (file)
@@ -104,6 +104,38 @@ static void test_path(void) {
         assert_se(!path_equal_ptr(NULL, "/a"));
 }
 
+static void test_path_equal_root(void) {
+        /* Nail down the details of how path_equal("/", ...) works. */
+
+        assert_se(path_equal("/", "/"));
+        assert_se(path_equal("/", "//"));
+
+        assert_se(!path_equal("/", "/./"));
+        assert_se(!path_equal("/", "/../"));
+
+        assert_se(!path_equal("/", "/.../"));
+
+        /* Make sure that files_same works as expected. */
+
+        assert_se(files_same("/", "/") > 0);
+        assert_se(files_same("/", "//") > 0);
+
+        assert_se(files_same("/", "/./") > 0);
+        assert_se(files_same("/", "/../") > 0);
+
+        assert_se(files_same("/", "/.../") == -ENOENT);
+
+        /* The same for path_equal_or_files_same. */
+
+        assert_se(path_equal_or_files_same("/", "/"));
+        assert_se(path_equal_or_files_same("/", "//"));
+
+        assert_se(path_equal_or_files_same("/", "/./"));
+        assert_se(path_equal_or_files_same("/", "/../"));
+
+        assert_se(!path_equal_or_files_same("/", "/.../"));
+}
+
 static void test_find_binary(const char *self) {
         char *p;
 
@@ -555,6 +587,7 @@ int main(int argc, char **argv) {
         log_open();
 
         test_path();
+        test_path_equal_root();
         test_find_binary(argv[0]);
         test_prefixes();
         test_path_join();