a9339c
From 2f9ee3163c44a71c99fe104daf01d4d9ab51d2c9 Mon Sep 17 00:00:00 2001
a9339c
From: Jan Synacek <jsynacek@redhat.com>
a9339c
Date: Mon, 28 May 2018 10:52:52 +0200
a9339c
Subject: [PATCH] tmpfiles: use safe_glob()
a9339c
a9339c
This filters out "." and ".." from glob results. Fixes #5655 and #5644.
a9339c
a9339c
Any judgements on whether the path is "safe" are removed. We will not remove
a9339c
"/" under any name (including "/../" and such), but we will remove stuff that
a9339c
is specified using paths that include "//", "/./" and "/../". Such paths can be
a9339c
created when joining strings automatically, or for other reasons, and people
a9339c
generally know what ".." and "." is.
a9339c
a9339c
Tests are added to make sure that the helper functions behave as expected.
a9339c
a9339c
Original commit: 84e72b5ef445ffb256bc4add4209c4c9c9855206
a9339c
Resolves: #1436004
a9339c
---
a9339c
 src/shared/util.c       | 63 +++++++++++++++++++++++++++++++++++++++++++++++--
a9339c
 src/shared/util.h       |  2 ++
a9339c
 src/tmpfiles/tmpfiles.c | 11 +++------
a9339c
 3 files changed, 66 insertions(+), 10 deletions(-)
a9339c
a9339c
diff --git a/src/shared/util.c b/src/shared/util.c
a9339c
index 3216f004a..78967103a 100644
a9339c
--- a/src/shared/util.c
a9339c
+++ b/src/shared/util.c
a9339c
@@ -49,7 +49,6 @@
a9339c
 #include <dlfcn.h>
a9339c
 #include <sys/wait.h>
a9339c
 #include <sys/time.h>
a9339c
-#include <glob.h>
a9339c
 #include <grp.h>
a9339c
 #include <sys/mman.h>
a9339c
 #include <sys/vfs.h>
a9339c
@@ -3370,7 +3369,7 @@ static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bo
a9339c
         /* We refuse to clean the root file system with this
a9339c
          * call. This is extra paranoia to never cause a really
a9339c
          * seriously broken system. */
a9339c
-        if (path_equal(path, "/")) {
a9339c
+        if (path_equal_or_files_same(path, "/")) {
a9339c
                 log_error("Attempted to remove entire root file system, and we can't allow that.");
a9339c
                 return -EPERM;
a9339c
         }
a9339c
@@ -5096,6 +5095,66 @@ int in_group(const char *name) {
a9339c
         return in_gid(gid);
a9339c
 }
a9339c
 
a9339c
+static void closedir_wrapper(void* v) {
a9339c
+        (void) closedir(v);
a9339c
+}
a9339c
+
a9339c
+static bool dot_or_dot_dot(const char *path) {
a9339c
+        if (!path)
a9339c
+                return false;
a9339c
+        if (path[0] != '.')
a9339c
+                return false;
a9339c
+        if (path[1] == 0)
a9339c
+                return true;
a9339c
+        if (path[1] != '.')
a9339c
+                return false;
a9339c
+
a9339c
+        return path[2] == 0;
a9339c
+}
a9339c
+
a9339c
+static struct dirent* readdir_no_dot(DIR *dirp) {
a9339c
+        struct dirent* d;
a9339c
+
a9339c
+        for (;;) {
a9339c
+                d = readdir(dirp);
a9339c
+                if (d && dot_or_dot_dot(d->d_name))
a9339c
+                        continue;
a9339c
+                return d;
a9339c
+        }
a9339c
+}
a9339c
+
a9339c
+int safe_glob(const char *path, int flags, glob_t *pglob) {
a9339c
+        int k;
a9339c
+
a9339c
+        /* We want to set GLOB_ALTDIRFUNC ourselves, don't allow it to be set. */
a9339c
+        assert(!(flags & GLOB_ALTDIRFUNC));
a9339c
+
a9339c
+        if (!pglob->gl_closedir)
a9339c
+                pglob->gl_closedir = closedir_wrapper;
a9339c
+        if (!pglob->gl_readdir)
a9339c
+                pglob->gl_readdir = (struct dirent *(*)(void *)) readdir_no_dot;
a9339c
+        if (!pglob->gl_opendir)
a9339c
+                pglob->gl_opendir = (void *(*)(const char *)) opendir;
a9339c
+        if (!pglob->gl_lstat)
a9339c
+                pglob->gl_lstat = lstat;
a9339c
+        if (!pglob->gl_stat)
a9339c
+                pglob->gl_stat = stat;
a9339c
+
a9339c
+        errno = 0;
a9339c
+        k = glob(path, flags | GLOB_ALTDIRFUNC, NULL, pglob);
a9339c
+
a9339c
+        if (k == GLOB_NOMATCH)
a9339c
+                return -ENOENT;
a9339c
+        if (k == GLOB_NOSPACE)
a9339c
+                return -ENOMEM;
a9339c
+        if (k != 0)
a9339c
+                return errno > 0 ? -errno : -EIO;
a9339c
+        if (strv_isempty(pglob->gl_pathv))
a9339c
+                return -ENOENT;
a9339c
+
a9339c
+        return 0;
a9339c
+}
a9339c
+
a9339c
 int glob_exists(const char *path) {
a9339c
         _cleanup_globfree_ glob_t g = {};
a9339c
         int k;
a9339c
diff --git a/src/shared/util.h b/src/shared/util.h
a9339c
index 998f882bb..cf096aa07 100644
a9339c
--- a/src/shared/util.h
a9339c
+++ b/src/shared/util.h
a9339c
@@ -44,6 +44,7 @@
a9339c
 #include <mntent.h>
a9339c
 #include <sys/socket.h>
a9339c
 #include <sys/inotify.h>
a9339c
+#include <glob.h>
a9339c
 
a9339c
 #if SIZEOF_PID_T == 4
a9339c
 #  define PID_PRI PRIi32
a9339c
@@ -595,6 +596,7 @@ char* gid_to_name(gid_t gid);
a9339c
 
a9339c
 int glob_exists(const char *path);
a9339c
 int glob_extend(char ***strv, const char *path);
a9339c
+int safe_glob(const char *path, int flags, glob_t *pglob);
a9339c
 
a9339c
 int dirent_ensure_type(DIR *d, struct dirent *de);
a9339c
 
a9339c
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
a9339c
index 5212d72f5..8a75efb22 100644
a9339c
--- a/src/tmpfiles/tmpfiles.c
a9339c
+++ b/src/tmpfiles/tmpfiles.c
a9339c
@@ -1095,19 +1095,14 @@ static int item_do_children(Item *i, const char *path, action_t action) {
a9339c
 
a9339c
 static int glob_item(Item *i, action_t action, bool recursive) {
a9339c
         _cleanup_globfree_ glob_t g = {
a9339c
-                .gl_closedir = (void (*)(void *)) closedir,
a9339c
-                .gl_readdir = (struct dirent *(*)(void *)) readdir,
a9339c
                 .gl_opendir = (void *(*)(const char *)) opendir_nomod,
a9339c
-                .gl_lstat = lstat,
a9339c
-                .gl_stat = stat,
a9339c
         };
a9339c
         int r = 0, k;
a9339c
         char **fn;
a9339c
 
a9339c
-        errno = 0;
a9339c
-        k = glob(i->path, GLOB_NOSORT|GLOB_BRACE|GLOB_ALTDIRFUNC, NULL, &g);
a9339c
-        if (k != 0 && k != GLOB_NOMATCH)
a9339c
-                return log_error_errno(errno ?: EIO, "glob(%s) failed: %m", i->path);
a9339c
+        k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g);
a9339c
+        if (k < 0 && k != -ENOENT)
a9339c
+                return log_error_errno(k, "glob(%s) failed: %m", i->path);
a9339c
 
a9339c
         STRV_FOREACH(fn, g.gl_pathv) {
a9339c
                 k = action(i, *fn);