]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
path-util: introduce path_is_safe()
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 30 Apr 2021 17:37:31 +0000 (02:37 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 28 May 2021 04:41:23 +0000 (13:41 +0900)
The function is similar to path_is_valid(), but it refuses paths which
contain ".." component.

src/basic/path-util.c
src/basic/path-util.h
src/test/test-path-util.c

index 752590bf70ad68371d8a7b82a9f48b06a21d87ff..48535da7256724a2d84d8c95eb04e0decd1c0b86 100644 (file)
@@ -1037,14 +1037,14 @@ bool filename_is_valid(const char *p) {
         return true;
 }
 
-bool path_is_valid(const char *p) {
+bool path_is_valid_full(const char *p, bool accept_dot_dot) {
         if (isempty(p))
                 return false;
 
         for (const char *e = p;;) {
                 int r;
 
-                r = path_find_first_component(&e, /* accept_dot_dot= */ true, NULL);
+                r = path_find_first_component(&e, accept_dot_dot, NULL);
                 if (r < 0)
                         return false;
 
index d83a1effdae980d90759485136aaebc9ee6ddad5..52ceb9b5a955696924ecc0c45dd401d7bbf90339 100644 (file)
@@ -155,7 +155,13 @@ int path_extract_filename(const char *p, char **ret);
 int path_extract_directory(const char *p, char **ret);
 
 bool filename_is_valid(const char *p) _pure_;
-bool path_is_valid(const char *p) _pure_;
+bool path_is_valid_full(const char *p, bool accept_dot_dot) _pure_;
+static inline bool path_is_valid(const char *p) {
+        return path_is_valid_full(p, true);
+}
+static inline bool path_is_safe(const char *p) {
+        return path_is_valid_full(p, false);
+}
 bool path_is_normalized(const char *p) _pure_;
 
 char *file_in_same_dir(const char *path, const char *filename);
index bb175145746c0eaab5c3bd6e1efa7ba750b5e191..c68603fc9bab34589bd9bcba67ff3d44308f71fd 100644 (file)
@@ -806,36 +806,48 @@ static void test_filename_is_valid(void) {
         assert_se(filename_is_valid("o.o"));
 }
 
-static void test_path_is_valid(void) {
+static void test_path_is_valid_and_safe_one(const char *p, bool ret) {
+        log_debug("/* %s(\"%s\")*/", __func__, strnull(p));
+
+        assert_se(path_is_valid(p) == ret);
+        if (ret)
+                ret = !streq(p, "..") &&
+                        !startswith(p, "../") &&
+                        !endswith(p, "/..") &&
+                        !strstr(p, "/../");
+        assert_se(path_is_safe(p) == ret);
+}
+
+static void test_path_is_valid_and_safe(void) {
         char foo[PATH_MAX+2];
         const char *c;
 
         log_info("/* %s */", __func__);
 
-        assert_se(!path_is_valid(""));
-        assert_se(path_is_valid("/bar/foo"));
-        assert_se(path_is_valid("/bar/foo/"));
-        assert_se(path_is_valid("/bar/foo/"));
-        assert_se(path_is_valid("//bar//foo//"));
-        assert_se(path_is_valid("/"));
-        assert_se(path_is_valid("/////"));
-        assert_se(path_is_valid("/////.///.////...///..//."));
-        assert_se(path_is_valid("."));
-        assert_se(path_is_valid(".."));
-        assert_se(path_is_valid("bar/foo"));
-        assert_se(path_is_valid("bar/foo/"));
-        assert_se(path_is_valid("bar//"));
+        test_path_is_valid_and_safe_one("", false);
+        test_path_is_valid_and_safe_one("/bar/foo", true);
+        test_path_is_valid_and_safe_one("/bar/foo/", true);
+        test_path_is_valid_and_safe_one("/bar/foo/", true);
+        test_path_is_valid_and_safe_one("//bar//foo//", true);
+        test_path_is_valid_and_safe_one("/", true);
+        test_path_is_valid_and_safe_one("/////", true);
+        test_path_is_valid_and_safe_one("/////.///.////...///..//.", true);
+        test_path_is_valid_and_safe_one(".", true);
+        test_path_is_valid_and_safe_one("..", true);
+        test_path_is_valid_and_safe_one("bar/foo", true);
+        test_path_is_valid_and_safe_one("bar/foo/", true);
+        test_path_is_valid_and_safe_one("bar//", true);
 
         memset(foo, 'a', sizeof(foo) -1);
         char_array_0(foo);
 
-        assert_se(!path_is_valid(foo));
+        test_path_is_valid_and_safe_one(foo, false);
 
         c = strjoina("/xxx/", foo, "/yyy");
-        assert_se(!path_is_valid(c));
+        test_path_is_valid_and_safe_one(c, false);
 
-        assert_se(path_is_valid("foo_bar-333"));
-        assert_se(path_is_valid("o.o"));
+        test_path_is_valid_and_safe_one("foo_bar-333", true);
+        test_path_is_valid_and_safe_one("o.o", true);
 }
 
 static void test_hidden_or_backup_file(void) {
@@ -983,7 +995,7 @@ int main(int argc, char **argv) {
         test_path_extract_filename();
         test_path_extract_directory();
         test_filename_is_valid();
-        test_path_is_valid();
+        test_path_is_valid_and_safe();
         test_hidden_or_backup_file();
         test_skip_dev_prefix();
         test_empty_or_root();