Hello all, I have included a patch to allow systemd tmpfile services to use white listing.
-- William Douglas, Intel Open Source Technology Center
>From ddbc0e7bd178227e12df461942bc13363a3023e0 Mon Sep 17 00:00:00 2001 From: William Douglas <william.doug...@linux.intel.com> Date: Tue, 19 Apr 2011 15:12:19 -0700 Subject: [PATCH] Implement tmpfile white listing. x, in addition to its old behavior, now will protect (recursively) files from removal. D has been modified to also "protect" directories so that r/R won't touch them. src/util.h: Modify rm_rf signature to take a function pointer used to assess if files are protected or not. src/util.c: Update item deletion to check if they are on the protected list and not delete if they are. Also change deleting a folder failure behavior to not report failure if the folder wasn't empty. src/tmpfiles.c: Add new hashmap for filenames that are protected and add paths (post glob expansion) for items that are of type IGNORE_PATH or TRUNCATE_DIRECTORY to that hashmap. --- src/tmpfiles.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++------- src/util.c | 42 ++++++++++++++++++++++------------- src/util.h | 2 +- 3 files changed, 84 insertions(+), 25 deletions(-) diff --git a/src/tmpfiles.c b/src/tmpfiles.c index 2526d1e..f99173e 100644 --- a/src/tmpfiles.c +++ b/src/tmpfiles.c @@ -78,7 +78,7 @@ typedef struct Item { bool age_set:1; } Item; -static Hashmap *items = NULL, *globs = NULL; +static Hashmap *items = NULL, *globs = NULL, *kept = NULL; static Set *unix_sockets = NULL; static bool arg_create = false; @@ -89,6 +89,10 @@ static const char *arg_prefix = NULL; #define MAX_DEPTH 256 +static bool protected(const char *key) { + return hashmap_get(kept, key) != NULL; +} + static bool needs_glob(int t) { return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH; } @@ -533,16 +537,18 @@ static int remove_item(Item *i, const char *instance) { break; case REMOVE_PATH: - if (remove(instance) < 0 && errno != ENOENT) { - log_error("remove(%s): %m", instance); - return -errno; + if (!(protected(instance))) { + if (remove(instance) < 0 && errno != ENOENT) { + log_error("remove(%s): %m", instance); + return -errno; + } } break; case TRUNCATE_DIRECTORY: case RECURSIVE_REMOVE_PATH: - if ((r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH)) < 0 && + if ((r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH, (bool (*)(const char *)) protected)) < 0 && r != -ENOENT) { log_error("rm_rf(%s): %s", instance, strerror(-r)); return r; @@ -654,9 +660,10 @@ static bool item_equal(Item *a, Item *b) { static int parse_line(const char *fname, unsigned line, const char *buffer) { Item *i, *existing; - char *mode = NULL, *user = NULL, *group = NULL, *age = NULL; + char *mode = NULL, *user = NULL, *group = NULL, *age = NULL, **keep = NULL; Hashmap *h; - int r; + int r, k; + glob_t glb; assert(fname); assert(line >= 1); @@ -772,6 +779,42 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { i->age_set = true; } + if (i->type == IGNORE_PATH || i->type == TRUNCATE_DIRECTORY) { + if ((k = glob(i->path, GLOB_BRACE, NULL, &glb)) != 0) { + if (k != GLOB_NOMATCH) { + if (errno != 0) + errno = EIO; + + log_error("glob(%s) failed: %m", i->path); + r = -errno; + } + r = 0; + goto finish; + } + + STRV_FOREACH(keep, glb.gl_pathv) { + char *ks = NULL; + if ((existing = hashmap_get(kept, *keep))) { + log_warning("Two or more conflicting lines for %s configured, ignoring.", *keep); + continue; + } + + if (!(ks = strdup(*keep))) { + log_error("strdup(%s) failed: %m", *keep); + r = -errno; + goto finish; + } + + if ((r = hashmap_put(kept, ks, ks)) < 0) { + log_warning("Failed to insert item %s: %s", *keep, strerror(-r)); + globfree(&glb); + free(ks); + goto finish; + } + } + globfree(&glb); + } + h = needs_glob(i->type) ? globs : items; if ((existing = hashmap_get(h, i->path))) { @@ -945,6 +988,7 @@ int main(int argc, char *argv[]) { int r; Item *i; Iterator iterator; + char *kp; if ((r = parse_argv(argc, argv)) <= 0) return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; @@ -957,8 +1001,9 @@ int main(int argc, char *argv[]) { items = hashmap_new(string_hash_func, string_compare_func); globs = hashmap_new(string_hash_func, string_compare_func); + kept = hashmap_new(string_hash_func, string_compare_func); - if (!items || !globs) { + if (!items || !globs || !kept) { log_error("Out of memory"); r = EXIT_FAILURE; goto finish; @@ -1024,8 +1069,12 @@ finish: while ((i = hashmap_steal_first(globs))) item_free(i); + while ((kp = hashmap_steal_first(kept))) + free(kp); + hashmap_free(items); hashmap_free(globs); + hashmap_free(kept); set_free_free(unix_sockets); diff --git a/src/util.c b/src/util.c index 5a076e6..f20d9c0 100644 --- a/src/util.c +++ b/src/util.c @@ -3077,9 +3077,10 @@ int get_ctty(char **r, dev_t *_devnr) { return 0; } -static int rm_rf_children(int fd, bool only_dirs) { +static int rm_rf_children(int fd, bool only_dirs, const char *path, bool (*protected)(const char *)) { DIR *d; int ret = 0; + char new_path[2*PATH_MAX]; assert(fd >= 0); @@ -3122,29 +3123,36 @@ static int rm_rf_children(int fd, bool only_dirs) { } else is_dir = de->d_type == DT_DIR; + snprintf(new_path, 2*PATH_MAX, "%s/%s", path, de->d_name); + if (is_dir) { int subdir_fd; + if (protected && (*protected)(new_path)) + continue; + if ((subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) { if (ret == 0 && errno != ENOENT) ret = -errno; continue; } - if ((r = rm_rf_children(subdir_fd, only_dirs)) < 0) { + if ((r = rm_rf_children(subdir_fd, only_dirs, new_path, protected)) < 0) { if (ret == 0) ret = r; } if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) { - if (ret == 0 && errno != ENOENT) + if (ret == 0 && errno != ENOENT && errno != EEXIST && errno != ENOTEMPTY) ret = -errno; } } else if (!only_dirs) { - if (unlinkat(fd, de->d_name, 0) < 0) { - if (ret == 0 && errno != ENOENT) - ret = -errno; + if (!(protected && (*protected)(new_path))) { + if (unlinkat(fd, de->d_name, 0) < 0) { + if (ret == 0 && errno != ENOENT) + ret = -errno; + } } } } @@ -3154,7 +3162,7 @@ static int rm_rf_children(int fd, bool only_dirs) { return ret; } -int rm_rf(const char *path, bool only_dirs, bool delete_root) { +int rm_rf(const char *path, bool only_dirs, bool delete_root, bool (*protected)(const char *)) { int fd; int r; @@ -3165,20 +3173,22 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root) { if (errno != ENOTDIR) return -errno; - if (delete_root && !only_dirs) - if (unlink(path) < 0) - return -errno; + if (!(protected && (*protected)(path))) + if (delete_root && !only_dirs) + if (unlink(path) < 0) + return -errno; return 0; } - r = rm_rf_children(fd, only_dirs); + r = rm_rf_children(fd, only_dirs, path, protected); - if (delete_root) - if (rmdir(path) < 0) { - if (r == 0) - r = -errno; - } + if (!(protected && (*protected)(path))) + if (delete_root) + if (rmdir(path) < 0) { + if (r == 0 && errno != EEXIST && errno != ENOTEMPTY) + r = -errno; + } return r; } diff --git a/src/util.h b/src/util.h index fcaeac4..7f7b0fd 100644 --- a/src/util.h +++ b/src/util.h @@ -350,7 +350,7 @@ int get_ctty(char **r, dev_t *_devnr); int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); -int rm_rf(const char *path, bool only_dirs, bool delete_root); +int rm_rf(const char *path, bool only_dirs, bool delete_root, bool (*protected)(const char *)); cpu_set_t* cpu_set_malloc(unsigned *ncpus); -- 1.7.2.3
_______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel