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