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