util: rm_rf() refuse cleaning non-memory file systems, as extra paranoia

This commit is contained in:
Lennart Poettering 2012-07-10 19:05:58 +02:00
parent 825c6fe5eb
commit f56d5db919
3 changed files with 71 additions and 8 deletions

View file

@ -3243,7 +3243,7 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
return 0; return 0;
} }
int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) { int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
DIR *d; DIR *d;
int ret = 0; int ret = 0;
@ -3335,14 +3335,43 @@ int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root
return ret; return ret;
} }
int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) { int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
int fd; struct statfs s;
int r;
assert(fd >= 0);
if (fstatfs(fd, &s) < 0) {
close_nointr_nofail(fd);
return -errno;
}
/* We refuse to clean disk file systems with this call. This
* is extra paranoia just to be sure we never ever remove
* non-state data */
if (s.f_type != TMPFS_MAGIC &&
s.f_type != RAMFS_MAGIC) {
log_error("Attempted to remove disk file system, and we can't allow that.");
close_nointr_nofail(fd);
return -EPERM;
}
return rm_rf_children_dangerous(fd, only_dirs, honour_sticky, root_dev);
}
static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bool honour_sticky, bool dangerous) {
int fd, r;
struct statfs s;
assert(path); assert(path);
/* Be paranoid */ /* We refuse to clean the root file system with this
assert(!streq(path, "/")); * call. This is extra paranoia to never cause a really
* seriously broken system. */
if (path_equal(path, "/")) {
log_error("Attempted to remove entire root file system, and we can't allow that.");
return -EPERM;
}
fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
if (fd < 0) { if (fd < 0) {
@ -3350,6 +3379,17 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky
if (errno != ENOTDIR) if (errno != ENOTDIR)
return -errno; return -errno;
if (!dangerous) {
if (statfs(path, &s) < 0)
return -errno;
if (s.f_type != TMPFS_MAGIC &&
s.f_type != RAMFS_MAGIC) {
log_error("Attempted to remove disk file system, and we can't allow that.");
return -EPERM;
}
}
if (delete_root && !only_dirs) if (delete_root && !only_dirs)
if (unlink(path) < 0 && errno != ENOENT) if (unlink(path) < 0 && errno != ENOENT)
return -errno; return -errno;
@ -3357,8 +3397,21 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky
return 0; return 0;
} }
r = rm_rf_children(fd, only_dirs, honour_sticky, NULL); if (!dangerous) {
if (fstatfs(fd, &s) < 0) {
close_nointr_nofail(fd);
return -errno;
}
if (s.f_type != TMPFS_MAGIC &&
s.f_type != RAMFS_MAGIC) {
log_error("Attempted to remove disk file system, and we can't allow that.");
close_nointr_nofail(fd);
return -EPERM;
}
}
r = rm_rf_children_dangerous(fd, only_dirs, honour_sticky, NULL);
if (delete_root) { if (delete_root) {
if (honour_sticky && file_is_priv_sticky(path) > 0) if (honour_sticky && file_is_priv_sticky(path) > 0)
@ -3373,6 +3426,14 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky
return r; return r;
} }
int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, false);
}
int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, true);
}
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
assert(path); assert(path);

View file

@ -358,7 +358,9 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid); int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);
int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev); int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky); int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
int pipe_eof(int fd); int pipe_eof(int fd);

View file

@ -845,7 +845,7 @@ static int remove_item_instance(Item *i, const char *instance) {
case RECURSIVE_REMOVE_PATH: case RECURSIVE_REMOVE_PATH:
/* FIXME: we probably should use dir_cleanup() here /* FIXME: we probably should use dir_cleanup() here
* instead of rm_rf() so that 'x' is honoured. */ * instead of rm_rf() so that 'x' is honoured. */
r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH, false); r = rm_rf_dangerous(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
if (r < 0 && r != -ENOENT) { if (r < 0 && r != -ENOENT) {
log_error("rm_rf(%s): %s", instance, strerror(-r)); log_error("rm_rf(%s): %s", instance, strerror(-r));
return r; return r;