aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorAntonio Murdaca <amurdaca@redhat.com>2016-04-07 09:48:25 -0400
committerMiklos Szeredi <mszeredi@redhat.com>2016-05-27 02:55:26 -0400
commit3fe6e52f062643676eb4518d68cee3bc1272091b (patch)
tree31dc27b85db350a2840d3d08a2597444233da15c /fs
parent2dcd0af568b0cf583645c8a317dd12e344b1c72a (diff)
ovl: override creds with the ones from the superblock mounter
In user namespace the whiteout creation fails with -EPERM because the current process isn't capable(CAP_SYS_ADMIN) when setting xattr. A simple reproducer: $ mkdir upper lower work merged lower/dir $ sudo mount -t overlay overlay -olowerdir=lower,upperdir=upper,workdir=work merged $ unshare -m -p -f -U -r bash Now as root in the user namespace: \# touch merged/dir/{1,2,3} # this will force a copy up of lower/dir \# rm -fR merged/* This ends up failing with -EPERM after the files in dir has been correctly deleted: unlinkat(4, "2", 0) = 0 unlinkat(4, "1", 0) = 0 unlinkat(4, "3", 0) = 0 close(4) = 0 unlinkat(AT_FDCWD, "merged/dir", AT_REMOVEDIR) = -1 EPERM (Operation not permitted) Interestingly, if you don't place files in merged/dir you can remove it, meaning if upper/dir does not exist, creating the char device file works properly in that same location. This patch uses ovl_sb_creator_cred() to get the cred struct from the superblock mounter and override the old cred with these new ones so that the whiteout creation is possible because overlay is wrong in assuming that the creds it will get with prepare_creds will be in the initial user namespace. The old cap_raise game is removed in favor of just overriding the old cred struct. This patch also drops from ovl_copy_up_one() the following two lines: override_cred->fsuid = stat->uid; override_cred->fsgid = stat->gid; This is because the correct uid and gid are taken directly with the stat struct and correctly set with ovl_set_attr(). Signed-off-by: Antonio Murdaca <runcom@redhat.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/overlayfs/copy_up.c26
-rw-r--r--fs/overlayfs/dir.c67
-rw-r--r--fs/overlayfs/overlayfs.h1
-rw-r--r--fs/overlayfs/readdir.c14
-rw-r--r--fs/overlayfs/super.c18
5 files changed, 27 insertions, 99 deletions
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index cc514da6f3e7..80aa6f1eb336 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -336,7 +336,6 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
336 struct dentry *upperdir; 336 struct dentry *upperdir;
337 struct dentry *upperdentry; 337 struct dentry *upperdentry;
338 const struct cred *old_cred; 338 const struct cred *old_cred;
339 struct cred *override_cred;
340 char *link = NULL; 339 char *link = NULL;
341 340
342 if (WARN_ON(!workdir)) 341 if (WARN_ON(!workdir))
@@ -357,28 +356,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
357 return PTR_ERR(link); 356 return PTR_ERR(link);
358 } 357 }
359 358
360 err = -ENOMEM; 359 old_cred = ovl_override_creds(dentry->d_sb);
361 override_cred = prepare_creds();
362 if (!override_cred)
363 goto out_free_link;
364
365 override_cred->fsuid = stat->uid;
366 override_cred->fsgid = stat->gid;
367 /*
368 * CAP_SYS_ADMIN for copying up extended attributes
369 * CAP_DAC_OVERRIDE for create
370 * CAP_FOWNER for chmod, timestamp update
371 * CAP_FSETID for chmod
372 * CAP_CHOWN for chown
373 * CAP_MKNOD for mknod
374 */
375 cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
376 cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
377 cap_raise(override_cred->cap_effective, CAP_FOWNER);
378 cap_raise(override_cred->cap_effective, CAP_FSETID);
379 cap_raise(override_cred->cap_effective, CAP_CHOWN);
380 cap_raise(override_cred->cap_effective, CAP_MKNOD);
381 old_cred = override_creds(override_cred);
382 360
383 err = -EIO; 361 err = -EIO;
384 if (lock_rename(workdir, upperdir) != NULL) { 362 if (lock_rename(workdir, upperdir) != NULL) {
@@ -401,9 +379,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
401out_unlock: 379out_unlock:
402 unlock_rename(workdir, upperdir); 380 unlock_rename(workdir, upperdir);
403 revert_creds(old_cred); 381 revert_creds(old_cred);
404 put_cred(override_cred);
405 382
406out_free_link:
407 if (link) 383 if (link)
408 free_page((unsigned long) link); 384 free_page((unsigned long) link);
409 385
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index b3fc0a35bf62..22f0253a3567 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -405,28 +405,13 @@ static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev,
405 err = ovl_create_upper(dentry, inode, &stat, link, hardlink); 405 err = ovl_create_upper(dentry, inode, &stat, link, hardlink);
406 } else { 406 } else {
407 const struct cred *old_cred; 407 const struct cred *old_cred;
408 struct cred *override_cred;
409 408
410 err = -ENOMEM; 409 old_cred = ovl_override_creds(dentry->d_sb);
411 override_cred = prepare_creds();
412 if (!override_cred)
413 goto out_iput;
414
415 /*
416 * CAP_SYS_ADMIN for setting opaque xattr
417 * CAP_DAC_OVERRIDE for create in workdir, rename
418 * CAP_FOWNER for removing whiteout from sticky dir
419 */
420 cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
421 cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
422 cap_raise(override_cred->cap_effective, CAP_FOWNER);
423 old_cred = override_creds(override_cred);
424 410
425 err = ovl_create_over_whiteout(dentry, inode, &stat, link, 411 err = ovl_create_over_whiteout(dentry, inode, &stat, link,
426 hardlink); 412 hardlink);
427 413
428 revert_creds(old_cred); 414 revert_creds(old_cred);
429 put_cred(override_cred);
430 } 415 }
431 416
432 if (!err) 417 if (!err)
@@ -662,32 +647,11 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
662 if (OVL_TYPE_PURE_UPPER(type)) { 647 if (OVL_TYPE_PURE_UPPER(type)) {
663 err = ovl_remove_upper(dentry, is_dir); 648 err = ovl_remove_upper(dentry, is_dir);
664 } else { 649 } else {
665 const struct cred *old_cred; 650 const struct cred *old_cred = ovl_override_creds(dentry->d_sb);
666 struct cred *override_cred;
667
668 err = -ENOMEM;
669 override_cred = prepare_creds();
670 if (!override_cred)
671 goto out_drop_write;
672
673 /*
674 * CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir
675 * CAP_DAC_OVERRIDE for create in workdir, rename
676 * CAP_FOWNER for removing whiteout from sticky dir
677 * CAP_FSETID for chmod of opaque dir
678 * CAP_CHOWN for chown of opaque dir
679 */
680 cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
681 cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
682 cap_raise(override_cred->cap_effective, CAP_FOWNER);
683 cap_raise(override_cred->cap_effective, CAP_FSETID);
684 cap_raise(override_cred->cap_effective, CAP_CHOWN);
685 old_cred = override_creds(override_cred);
686 651
687 err = ovl_remove_and_whiteout(dentry, is_dir); 652 err = ovl_remove_and_whiteout(dentry, is_dir);
688 653
689 revert_creds(old_cred); 654 revert_creds(old_cred);
690 put_cred(override_cred);
691 } 655 }
692out_drop_write: 656out_drop_write:
693 ovl_drop_write(dentry); 657 ovl_drop_write(dentry);
@@ -725,7 +689,6 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
725 bool new_is_dir = false; 689 bool new_is_dir = false;
726 struct dentry *opaquedir = NULL; 690 struct dentry *opaquedir = NULL;
727 const struct cred *old_cred = NULL; 691 const struct cred *old_cred = NULL;
728 struct cred *override_cred = NULL;
729 692
730 err = -EINVAL; 693 err = -EINVAL;
731 if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE)) 694 if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE))
@@ -794,26 +757,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
794 old_opaque = !OVL_TYPE_PURE_UPPER(old_type); 757 old_opaque = !OVL_TYPE_PURE_UPPER(old_type);
795 new_opaque = !OVL_TYPE_PURE_UPPER(new_type); 758 new_opaque = !OVL_TYPE_PURE_UPPER(new_type);
796 759
797 if (old_opaque || new_opaque) { 760 if (old_opaque || new_opaque)
798 err = -ENOMEM; 761 old_cred = ovl_override_creds(old->d_sb);
799 override_cred = prepare_creds();
800 if (!override_cred)
801 goto out_drop_write;
802
803 /*
804 * CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir
805 * CAP_DAC_OVERRIDE for create in workdir
806 * CAP_FOWNER for removing whiteout from sticky dir
807 * CAP_FSETID for chmod of opaque dir
808 * CAP_CHOWN for chown of opaque dir
809 */
810 cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
811 cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
812 cap_raise(override_cred->cap_effective, CAP_FOWNER);
813 cap_raise(override_cred->cap_effective, CAP_FSETID);
814 cap_raise(override_cred->cap_effective, CAP_CHOWN);
815 old_cred = override_creds(override_cred);
816 }
817 762
818 if (overwrite && OVL_TYPE_MERGE_OR_LOWER(new_type) && new_is_dir) { 763 if (overwrite && OVL_TYPE_MERGE_OR_LOWER(new_type) && new_is_dir) {
819 opaquedir = ovl_check_empty_and_clear(new); 764 opaquedir = ovl_check_empty_and_clear(new);
@@ -943,10 +888,8 @@ out_dput_old:
943out_unlock: 888out_unlock:
944 unlock_rename(new_upperdir, old_upperdir); 889 unlock_rename(new_upperdir, old_upperdir);
945out_revert_creds: 890out_revert_creds:
946 if (old_opaque || new_opaque) { 891 if (old_opaque || new_opaque)
947 revert_creds(old_cred); 892 revert_creds(old_cred);
948 put_cred(override_cred);
949 }
950out_drop_write: 893out_drop_write:
951 ovl_drop_write(old); 894 ovl_drop_write(old);
952out: 895out:
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index 6a7090f4a441..4cebeb24c08d 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -153,6 +153,7 @@ void ovl_drop_write(struct dentry *dentry);
153bool ovl_dentry_is_opaque(struct dentry *dentry); 153bool ovl_dentry_is_opaque(struct dentry *dentry);
154void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque); 154void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque);
155bool ovl_is_whiteout(struct dentry *dentry); 155bool ovl_is_whiteout(struct dentry *dentry);
156const struct cred *ovl_override_creds(struct super_block *sb);
156void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry); 157void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry);
157struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, 158struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
158 unsigned int flags); 159 unsigned int flags);
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
index 6ec1e43a9a54..e9206bc8598f 100644
--- a/fs/overlayfs/readdir.c
+++ b/fs/overlayfs/readdir.c
@@ -36,6 +36,7 @@ struct ovl_dir_cache {
36 36
37struct ovl_readdir_data { 37struct ovl_readdir_data {
38 struct dir_context ctx; 38 struct dir_context ctx;
39 struct dentry *dentry;
39 bool is_lowest; 40 bool is_lowest;
40 struct rb_root root; 41 struct rb_root root;
41 struct list_head *list; 42 struct list_head *list;
@@ -206,17 +207,8 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
206 struct ovl_cache_entry *p; 207 struct ovl_cache_entry *p;
207 struct dentry *dentry; 208 struct dentry *dentry;
208 const struct cred *old_cred; 209 const struct cred *old_cred;
209 struct cred *override_cred;
210
211 override_cred = prepare_creds();
212 if (!override_cred)
213 return -ENOMEM;
214 210
215 /* 211 old_cred = ovl_override_creds(rdd->dentry->d_sb);
216 * CAP_DAC_OVERRIDE for lookup
217 */
218 cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
219 old_cred = override_creds(override_cred);
220 212
221 err = mutex_lock_killable(&dir->d_inode->i_mutex); 213 err = mutex_lock_killable(&dir->d_inode->i_mutex);
222 if (!err) { 214 if (!err) {
@@ -232,7 +224,6 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
232 inode_unlock(dir->d_inode); 224 inode_unlock(dir->d_inode);
233 } 225 }
234 revert_creds(old_cred); 226 revert_creds(old_cred);
235 put_cred(override_cred);
236 227
237 return err; 228 return err;
238} 229}
@@ -288,6 +279,7 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
288 struct path realpath; 279 struct path realpath;
289 struct ovl_readdir_data rdd = { 280 struct ovl_readdir_data rdd = {
290 .ctx.actor = ovl_fill_merge, 281 .ctx.actor = ovl_fill_merge,
282 .dentry = dentry,
291 .list = list, 283 .list = list,
292 .root = RB_ROOT, 284 .root = RB_ROOT,
293 .is_lowest = false, 285 .is_lowest = false,
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 791235e03d17..d659f766ceff 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -42,6 +42,8 @@ struct ovl_fs {
42 long lower_namelen; 42 long lower_namelen;
43 /* pathnames of lower and upper dirs, for show_options */ 43 /* pathnames of lower and upper dirs, for show_options */
44 struct ovl_config config; 44 struct ovl_config config;
45 /* creds of process who forced instantiation of super block */
46 const struct cred *creator_cred;
45}; 47};
46 48
47struct ovl_dir_cache; 49struct ovl_dir_cache;
@@ -265,6 +267,13 @@ bool ovl_is_whiteout(struct dentry *dentry)
265 return inode && IS_WHITEOUT(inode); 267 return inode && IS_WHITEOUT(inode);
266} 268}
267 269
270const struct cred *ovl_override_creds(struct super_block *sb)
271{
272 struct ovl_fs *ofs = sb->s_fs_info;
273
274 return override_creds(ofs->creator_cred);
275}
276
268static bool ovl_is_opaquedir(struct dentry *dentry) 277static bool ovl_is_opaquedir(struct dentry *dentry)
269{ 278{
270 int res; 279 int res;
@@ -603,6 +612,7 @@ static void ovl_put_super(struct super_block *sb)
603 kfree(ufs->config.lowerdir); 612 kfree(ufs->config.lowerdir);
604 kfree(ufs->config.upperdir); 613 kfree(ufs->config.upperdir);
605 kfree(ufs->config.workdir); 614 kfree(ufs->config.workdir);
615 put_cred(ufs->creator_cred);
606 kfree(ufs); 616 kfree(ufs);
607} 617}
608 618
@@ -1108,10 +1118,14 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
1108 else 1118 else
1109 sb->s_d_op = &ovl_dentry_operations; 1119 sb->s_d_op = &ovl_dentry_operations;
1110 1120
1121 ufs->creator_cred = prepare_creds();
1122 if (!ufs->creator_cred)
1123 goto out_put_lower_mnt;
1124
1111 err = -ENOMEM; 1125 err = -ENOMEM;
1112 oe = ovl_alloc_entry(numlower); 1126 oe = ovl_alloc_entry(numlower);
1113 if (!oe) 1127 if (!oe)
1114 goto out_put_lower_mnt; 1128 goto out_put_cred;
1115 1129
1116 root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, oe)); 1130 root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, oe));
1117 if (!root_dentry) 1131 if (!root_dentry)
@@ -1144,6 +1158,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
1144 1158
1145out_free_oe: 1159out_free_oe:
1146 kfree(oe); 1160 kfree(oe);
1161out_put_cred:
1162 put_cred(ufs->creator_cred);
1147out_put_lower_mnt: 1163out_put_lower_mnt:
1148 for (i = 0; i < ufs->numlower; i++) 1164 for (i = 0; i < ufs->numlower; i++)
1149 mntput(ufs->lower_mnt[i]); 1165 mntput(ufs->lower_mnt[i]);