From b8086b58fa19975713777bf68f9b4d980ffe872a Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 21 Aug 2020 19:02:56 +0200 Subject: [PATCH] main: support writing uid/gid/mode to xattr Signed-off-by: Giuseppe Scrivano --- direct.c | 8 +-- fuse-overlayfs.h | 1 + main.c | 148 +++++++++++++++++++++++++++++++++++++++++++++-- utils.c | 12 +++- utils.h | 2 +- 5 files changed, 160 insertions(+), 11 deletions(-) diff --git a/direct.c b/direct.c index 2fc2efc..a25c6b3 100644 --- a/direct.c +++ b/direct.c @@ -90,7 +90,7 @@ direct_fstat (struct ovl_layer *l, int fd, const char *path, unsigned int mask, if (ret == 0) { statx_to_stat (&stx, st); - return override_mode (l, fd, path, st); + return override_mode (l, fd, NULL, path, st); } return ret; @@ -101,7 +101,7 @@ direct_fstat (struct ovl_layer *l, int fd, const char *path, unsigned int mask, if (ret != 0) return ret; - return override_mode (l, fd, path, st); + return override_mode (l, fd, NULL, path, st); } static int @@ -118,7 +118,7 @@ direct_statat (struct ovl_layer *l, const char *path, struct stat *st, int flags if (ret == 0) { statx_to_stat (&stx, st); - return override_mode (l, -1, path, st); + return override_mode (l, -1, NULL, path, st); } return ret; @@ -128,7 +128,7 @@ direct_statat (struct ovl_layer *l, const char *path, struct stat *st, int flags if (ret != 0) return ret; - return override_mode (l, -1, path, st); + return override_mode (l, -1, NULL, path, st); } static struct dirent * diff --git a/fuse-overlayfs.h b/fuse-overlayfs.h index f02156b..d599fbd 100644 --- a/fuse-overlayfs.h +++ b/fuse-overlayfs.h @@ -94,6 +94,7 @@ struct ovl_data int fast_ino_check; int writeback; int disable_xattrs; + int xattr_permissions; /* current uid/gid*/ uid_t uid; diff --git a/main.c b/main.c index 034aa08..d4553de 100644 --- a/main.c +++ b/main.c @@ -203,6 +203,8 @@ static const struct fuse_opt ovl_opts[] = { offsetof (struct ovl_data, disable_xattrs), 1}, {"plugins=%s", offsetof (struct ovl_data, plugins), 0}, + {"xattr_permissions=%d", + offsetof (struct ovl_data, xattr_permissions), 0}, FUSE_OPT_END }; @@ -464,10 +466,54 @@ can_access_xattr (const char *name) && !has_prefix (name, PRIVILEGED_XATTR_PREFIX); } +static ssize_t +write_permission_xattr (struct ovl_data *lo, int fd, const char *path, uid_t uid, gid_t gid, mode_t mode) +{ + char buf[64]; + size_t len; + int ret; + const char *name = NULL; + + switch (lo->xattr_permissions) + { + case 0: + return 0; + + case 1: + name = XATTR_PRIVILEGED_OVERRIDE_STAT; + break; + + case 2: + name = XATTR_OVERRIDE_STAT; + break; + + default: + errno = EINVAL; + return -1; + } + + if (path == NULL && fd < 0) + { + errno = EINVAL; + return -1; + } + + len = sprintf (buf, "%d:%d:%o", uid, gid, mode); + if (fd >= 0) + return fsetxattr (fd, name, buf, len, 0); + + ret = lsetxattr (path, name, buf, len, 0); + /* Ignore EPERM in unprivileged mode. */ + if (ret < 0 && lo->xattr_permissions == 2 && errno == EPERM) + return 0; + return ret; +} static int do_fchown (struct ovl_data *lo, int fd, uid_t uid, gid_t gid, mode_t mode) { + if (lo->xattr_permissions) + return write_permission_xattr (lo, fd, NULL, uid, gid, mode); return fchown (fd, uid, gid); } /* Make sure it is not used anymore. */ @@ -476,6 +522,8 @@ do_fchown (struct ovl_data *lo, int fd, uid_t uid, gid_t gid, mode_t mode) static int do_chown (struct ovl_data *lo, const char *path, uid_t uid, gid_t gid, mode_t mode) { + if (lo->xattr_permissions) + return write_permission_xattr (lo, -1, path, uid, gid, mode); return chown (path, uid, gid); } /* Make sure it is not used anymore. */ @@ -484,6 +532,16 @@ do_chown (struct ovl_data *lo, const char *path, uid_t uid, gid_t gid, mode_t mo static int do_fchownat (struct ovl_data *lo, int dfd, const char *path, uid_t uid, gid_t gid, mode_t mode, int flags) { + if (lo->xattr_permissions) + { + char proc_path[32]; + cleanup_close int fd = openat (dfd, path, O_NOFOLLOW|O_PATH); + if (fd < 0) + return fd; + + sprintf (proc_path, "/proc/self/fd/%d", fd); + return write_permission_xattr (lo, -1, proc_path, uid, gid, mode); + } return fchownat (dfd, path, uid, gid, 0); } /* Make sure it is not used anymore. */ @@ -492,6 +550,24 @@ do_fchownat (struct ovl_data *lo, int dfd, const char *path, uid_t uid, gid_t gi static int do_fchmod (struct ovl_data *lo, int fd, mode_t mode) { + if (lo->xattr_permissions) + { + struct ovl_layer *upper = get_upper_layer (lo); + struct stat st; + + if (upper == NULL) + { + errno = EROFS; + return -1; + } + + st.st_uid = 0; + st.st_gid = 0; + if (override_mode (upper, fd, NULL, NULL, &st) < 0 && errno != ENODATA) + return -1; + + return write_permission_xattr (lo, fd, NULL, st.st_uid, st.st_gid, mode); + } return fchmod (fd, mode); } /* Make sure it is not used anymore. */ @@ -500,6 +576,24 @@ do_fchmod (struct ovl_data *lo, int fd, mode_t mode) static int do_chmod (struct ovl_data *lo, const char *path, mode_t mode) { + if (lo->xattr_permissions) + { + struct ovl_layer *upper = get_upper_layer (lo); + struct stat st; + + if (upper == NULL) + { + errno = EROFS; + return -1; + } + + st.st_uid = 0; + st.st_gid = 0; + if (override_mode (upper, -1, path, NULL, &st) < 0 && errno != ENODATA) + return -1; + + return write_permission_xattr (lo, -1, path, st.st_uid, st.st_gid, mode); + } return chmod (path, mode); } /* Make sure it is not used anymore. */ @@ -2540,7 +2634,7 @@ create_directory (struct ovl_data *lo, int dirfd, const char *name, const struct if (ret < 0) goto out; - if (uid != lo->uid || gid != lo->gid) + if (uid != lo->uid || gid != lo->gid || get_upper_layer (lo)->has_stat_override || get_upper_layer (lo)->has_privileged_stat_override) { ret = do_fchown (lo, dfd, uid, gid, mode); if (ret < 0) @@ -2764,7 +2858,7 @@ copyup (struct ovl_data *lo, struct ovl_node *node) if (dfd < 0) goto exit; - if (st.st_uid != lo->uid || st.st_gid != lo->gid) + if (st.st_uid != lo->uid || st.st_gid != lo->gid || get_upper_layer (lo)->has_stat_override || get_upper_layer (lo)->has_privileged_stat_override) { ret = do_fchown (lo, dfd, st.st_uid, st.st_gid, st.st_mode); if (ret < 0) @@ -3220,7 +3314,7 @@ direct_create_file (struct ovl_layer *l, int dirfd, const char *path, uid_t uid, int ret; /* try to create directly the file if it doesn't need to be chowned. */ - if (uid == lo->uid && gid == lo->gid) + if (uid == lo->uid && gid == lo->gid && !l->has_stat_override && !l->has_privileged_stat_override) { ret = TEMP_FAILURE_RETRY (safe_openat (get_upper_layer (lo)->fd, path, flags, mode)); if (ret >= 0) @@ -3234,7 +3328,7 @@ direct_create_file (struct ovl_layer *l, int dirfd, const char *path, uid_t uid, fd = TEMP_FAILURE_RETRY (safe_openat (lo->workdir_fd, wd_tmp_file_name, flags, mode)); if (fd < 0) return -1; - if (uid != lo->uid || gid != lo->gid) + if (uid != lo->uid || gid != lo->gid || l->has_stat_override || l->has_privileged_stat_override) { if (do_fchown (lo, fd, uid, gid, mode) < 0) { @@ -3877,7 +3971,7 @@ direct_symlinkat (struct ovl_layer *l, const char *target, const char *linkpath, if (ret < 0) return ret; - if (uid != lo->uid || gid != lo->gid) + if (uid != lo->uid || gid != lo->gid || l->has_stat_override || l->has_privileged_stat_override) { ret = do_fchownat (lo, lo->workdir_fd, wd_tmp_file_name, uid, gid, AT_SYMLINK_NOFOLLOW, 0755); if (ret < 0) @@ -5146,6 +5240,7 @@ main (int argc, char *argv[]) .redirect_dir = NULL, .mountpoint = NULL, .fsync = 1, + .xattr_permissions = 0, .timeout = 1000000000.0, .timeout_str = NULL, .writeback = 1, @@ -5255,8 +5350,51 @@ main (int argc, char *argv[]) error (EXIT_FAILURE, errno, "cannot read upper dir"); layers = tmp_layer; } + lo.layers = layers; + if (lo.upperdir) + { + if (lo.xattr_permissions) + { + const char *name = NULL; + char data[64]; + ssize_t s; + if (lo.xattr_permissions == 1) + { + get_upper_layer (&lo)->has_privileged_stat_override = 1; + name = XATTR_PRIVILEGED_OVERRIDE_STAT; + } + else if (lo.xattr_permissions == 2) + { + get_upper_layer (&lo)->has_stat_override = 1; + name = XATTR_OVERRIDE_STAT; + } + else + error (EXIT_FAILURE, 0, "invalid value for xattr_permissions"); + + s = fgetxattr (get_upper_layer (&lo)->fd, name, data, sizeof (data)); + if (s < 0) + { + if (errno != ENODATA) + error (EXIT_FAILURE, errno, "read xattr `%s` from upperdir", name); + else + { + struct stat st; + ret = fstat (get_upper_layer (&lo)->fd, &st); + if (ret < 0) + error (EXIT_FAILURE, errno, "stat upperdir"); + + ret = write_permission_xattr (&lo, get_upper_layer (&lo)->fd, + lo.upperdir, + st.st_uid, st.st_gid, st.st_mode); + if (ret < 0) + error (EXIT_FAILURE, errno, "write xattr `%s` to upperdir", name); + } + } + } + } + lo.inodes = hash_initialize (2048, NULL, node_inode_hasher, node_inode_compare, inode_free); lo.root = load_dir (&lo, NULL, lo.layers, ".", ""); diff --git a/utils.c b/utils.c index 2aa7560..3fdb5fb 100644 --- a/utils.c +++ b/utils.c @@ -225,7 +225,7 @@ open_fd_or_get_path (struct ovl_layer *l, const char *path, char *out, int *fd, } int -override_mode (struct ovl_layer *l, int fd, const char *path, struct stat *st) +override_mode (struct ovl_layer *l, int fd, const char *abs_path, const char *path, struct stat *st) { int ret; uid_t uid; @@ -246,6 +246,12 @@ override_mode (struct ovl_layer *l, int fd, const char *path, struct stat *st) if (ret < 0) return ret; } + else if (abs_path) + { + ret = lgetxattr (abs_path, xattr_name, buf, sizeof (buf) - 1); + if (ret < 0) + return ret; + } else { char full_path[PATH_MAX]; @@ -259,7 +265,11 @@ override_mode (struct ovl_layer *l, int fd, const char *path, struct stat *st) if (fd >= 0) ret = fgetxattr (fd, xattr_name, buf, sizeof (buf) - 1); else + { ret = lgetxattr (full_path, xattr_name, buf, sizeof (buf) - 1); + if (ret < 0 && errno == ENODATA) + return 0; + } if (ret < 0) return ret; diff --git a/utils.h b/utils.h index 0d4a54e..99e911a 100644 --- a/utils.h +++ b/utils.h @@ -58,6 +58,6 @@ void statx_to_stat (struct statx *stx, struct stat *st); int safe_openat (int dirfd, const char *pathname, int flags, mode_t mode); -int override_mode (struct ovl_layer *l, int fd, const char *path, struct stat *st); +int override_mode (struct ovl_layer *l, int fd, const char *abs_path, const char *path, struct stat *st); #endif