77ceadf1df65fe829710bdbdd2e4ca7d797151a4
[oweals/openwrt.git] /
1 From 3fe6e52f062643676eb4518d68cee3bc1272091b Mon Sep 17 00:00:00 2001
2 From: Antonio Murdaca <amurdaca@redhat.com>
3 Date: Thu, 7 Apr 2016 15:48:25 +0200
4 Subject: [PATCH] ovl: override creds with the ones from the superblock mounter
5
6 In user namespace the whiteout creation fails with -EPERM because the
7 current process isn't capable(CAP_SYS_ADMIN) when setting xattr.
8
9 A simple reproducer:
10
11 $ mkdir upper lower work merged lower/dir
12 $ sudo mount -t overlay overlay -olowerdir=lower,upperdir=upper,workdir=work merged
13 $ unshare -m -p -f -U -r bash
14
15 Now as root in the user namespace:
16
17 \# touch merged/dir/{1,2,3} # this will force a copy up of lower/dir
18 \# rm -fR merged/*
19
20 This ends up failing with -EPERM after the files in dir has been
21 correctly deleted:
22
23 unlinkat(4, "2", 0)                     = 0
24 unlinkat(4, "1", 0)                     = 0
25 unlinkat(4, "3", 0)                     = 0
26 close(4)                                = 0
27 unlinkat(AT_FDCWD, "merged/dir", AT_REMOVEDIR) = -1 EPERM (Operation not
28 permitted)
29
30 Interestingly, if you don't place files in merged/dir you can remove it,
31 meaning if upper/dir does not exist, creating the char device file works
32 properly in that same location.
33
34 This patch uses ovl_sb_creator_cred() to get the cred struct from the
35 superblock mounter and override the old cred with these new ones so that
36 the whiteout creation is possible because overlay is wrong in assuming that
37 the creds it will get with prepare_creds will be in the initial user
38 namespace.  The old cap_raise game is removed in favor of just overriding
39 the old cred struct.
40
41 This patch also drops from ovl_copy_up_one() the following two lines:
42
43 override_cred->fsuid = stat->uid;
44 override_cred->fsgid = stat->gid;
45
46 This is because the correct uid and gid are taken directly with the stat
47 struct and correctly set with ovl_set_attr().
48
49 Signed-off-by: Antonio Murdaca <runcom@redhat.com>
50 Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
51 ---
52  fs/overlayfs/copy_up.c   | 26 +------------------
53  fs/overlayfs/dir.c       | 67 ++++--------------------------------------------
54  fs/overlayfs/overlayfs.h |  1 +
55  fs/overlayfs/readdir.c   | 14 +++-------
56  fs/overlayfs/super.c     | 18 ++++++++++++-
57  5 files changed, 27 insertions(+), 99 deletions(-)
58
59 --- a/fs/overlayfs/copy_up.c
60 +++ b/fs/overlayfs/copy_up.c
61 @@ -305,7 +305,6 @@ int ovl_copy_up_one(struct dentry *paren
62         struct dentry *upperdir;
63         struct dentry *upperdentry;
64         const struct cred *old_cred;
65 -       struct cred *override_cred;
66         char *link = NULL;
67  
68         if (WARN_ON(!workdir))
69 @@ -324,28 +323,7 @@ int ovl_copy_up_one(struct dentry *paren
70                         return PTR_ERR(link);
71         }
72  
73 -       err = -ENOMEM;
74 -       override_cred = prepare_creds();
75 -       if (!override_cred)
76 -               goto out_free_link;
77 -
78 -       override_cred->fsuid = stat->uid;
79 -       override_cred->fsgid = stat->gid;
80 -       /*
81 -        * CAP_SYS_ADMIN for copying up extended attributes
82 -        * CAP_DAC_OVERRIDE for create
83 -        * CAP_FOWNER for chmod, timestamp update
84 -        * CAP_FSETID for chmod
85 -        * CAP_CHOWN for chown
86 -        * CAP_MKNOD for mknod
87 -        */
88 -       cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
89 -       cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
90 -       cap_raise(override_cred->cap_effective, CAP_FOWNER);
91 -       cap_raise(override_cred->cap_effective, CAP_FSETID);
92 -       cap_raise(override_cred->cap_effective, CAP_CHOWN);
93 -       cap_raise(override_cred->cap_effective, CAP_MKNOD);
94 -       old_cred = override_creds(override_cred);
95 +       old_cred = ovl_override_creds(dentry->d_sb);
96  
97         err = -EIO;
98         if (lock_rename(workdir, upperdir) != NULL) {
99 @@ -368,9 +346,7 @@ int ovl_copy_up_one(struct dentry *paren
100  out_unlock:
101         unlock_rename(workdir, upperdir);
102         revert_creds(old_cred);
103 -       put_cred(override_cred);
104  
105 -out_free_link:
106         if (link)
107                 free_page((unsigned long) link);
108  
109 --- a/fs/overlayfs/dir.c
110 +++ b/fs/overlayfs/dir.c
111 @@ -405,28 +405,13 @@ static int ovl_create_or_link(struct den
112                 err = ovl_create_upper(dentry, inode, &stat, link, hardlink);
113         } else {
114                 const struct cred *old_cred;
115 -               struct cred *override_cred;
116  
117 -               err = -ENOMEM;
118 -               override_cred = prepare_creds();
119 -               if (!override_cred)
120 -                       goto out_iput;
121 -
122 -               /*
123 -                * CAP_SYS_ADMIN for setting opaque xattr
124 -                * CAP_DAC_OVERRIDE for create in workdir, rename
125 -                * CAP_FOWNER for removing whiteout from sticky dir
126 -                */
127 -               cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
128 -               cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
129 -               cap_raise(override_cred->cap_effective, CAP_FOWNER);
130 -               old_cred = override_creds(override_cred);
131 +               old_cred = ovl_override_creds(dentry->d_sb);
132  
133                 err = ovl_create_over_whiteout(dentry, inode, &stat, link,
134                                                hardlink);
135  
136                 revert_creds(old_cred);
137 -               put_cred(override_cred);
138         }
139  
140         if (!err)
141 @@ -656,32 +641,11 @@ static int ovl_do_remove(struct dentry *
142         if (OVL_TYPE_PURE_UPPER(type)) {
143                 err = ovl_remove_upper(dentry, is_dir);
144         } else {
145 -               const struct cred *old_cred;
146 -               struct cred *override_cred;
147 -
148 -               err = -ENOMEM;
149 -               override_cred = prepare_creds();
150 -               if (!override_cred)
151 -                       goto out_drop_write;
152 -
153 -               /*
154 -                * CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir
155 -                * CAP_DAC_OVERRIDE for create in workdir, rename
156 -                * CAP_FOWNER for removing whiteout from sticky dir
157 -                * CAP_FSETID for chmod of opaque dir
158 -                * CAP_CHOWN for chown of opaque dir
159 -                */
160 -               cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
161 -               cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
162 -               cap_raise(override_cred->cap_effective, CAP_FOWNER);
163 -               cap_raise(override_cred->cap_effective, CAP_FSETID);
164 -               cap_raise(override_cred->cap_effective, CAP_CHOWN);
165 -               old_cred = override_creds(override_cred);
166 +               const struct cred *old_cred = ovl_override_creds(dentry->d_sb);
167  
168                 err = ovl_remove_and_whiteout(dentry, is_dir);
169  
170                 revert_creds(old_cred);
171 -               put_cred(override_cred);
172         }
173  out_drop_write:
174         ovl_drop_write(dentry);
175 @@ -720,7 +684,6 @@ static int ovl_rename2(struct inode *old
176         bool new_is_dir = false;
177         struct dentry *opaquedir = NULL;
178         const struct cred *old_cred = NULL;
179 -       struct cred *override_cred = NULL;
180  
181         err = -EINVAL;
182         if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE))
183 @@ -789,26 +752,8 @@ static int ovl_rename2(struct inode *old
184         old_opaque = !OVL_TYPE_PURE_UPPER(old_type);
185         new_opaque = !OVL_TYPE_PURE_UPPER(new_type);
186  
187 -       if (old_opaque || new_opaque) {
188 -               err = -ENOMEM;
189 -               override_cred = prepare_creds();
190 -               if (!override_cred)
191 -                       goto out_drop_write;
192 -
193 -               /*
194 -                * CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir
195 -                * CAP_DAC_OVERRIDE for create in workdir
196 -                * CAP_FOWNER for removing whiteout from sticky dir
197 -                * CAP_FSETID for chmod of opaque dir
198 -                * CAP_CHOWN for chown of opaque dir
199 -                */
200 -               cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);
201 -               cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
202 -               cap_raise(override_cred->cap_effective, CAP_FOWNER);
203 -               cap_raise(override_cred->cap_effective, CAP_FSETID);
204 -               cap_raise(override_cred->cap_effective, CAP_CHOWN);
205 -               old_cred = override_creds(override_cred);
206 -       }
207 +       if (old_opaque || new_opaque)
208 +               old_cred = ovl_override_creds(old->d_sb);
209  
210         if (overwrite && OVL_TYPE_MERGE_OR_LOWER(new_type) && new_is_dir) {
211                 opaquedir = ovl_check_empty_and_clear(new);
212 @@ -939,10 +884,8 @@ out_dput_old:
213  out_unlock:
214         unlock_rename(new_upperdir, old_upperdir);
215  out_revert_creds:
216 -       if (old_opaque || new_opaque) {
217 +       if (old_opaque || new_opaque)
218                 revert_creds(old_cred);
219 -               put_cred(override_cred);
220 -       }
221  out_drop_write:
222         ovl_drop_write(old);
223  out:
224 --- a/fs/overlayfs/overlayfs.h
225 +++ b/fs/overlayfs/overlayfs.h
226 @@ -150,6 +150,7 @@ void ovl_drop_write(struct dentry *dentr
227  bool ovl_dentry_is_opaque(struct dentry *dentry);
228  void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque);
229  bool ovl_is_whiteout(struct dentry *dentry);
230 +const struct cred *ovl_override_creds(struct super_block *sb);
231  void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry);
232  struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
233                           unsigned int flags);
234 --- a/fs/overlayfs/readdir.c
235 +++ b/fs/overlayfs/readdir.c
236 @@ -36,6 +36,7 @@ struct ovl_dir_cache {
237  
238  struct ovl_readdir_data {
239         struct dir_context ctx;
240 +       struct dentry *dentry;
241         bool is_lowest;
242         struct rb_root root;
243         struct list_head *list;
244 @@ -205,17 +206,8 @@ static int ovl_check_whiteouts(struct de
245         struct ovl_cache_entry *p;
246         struct dentry *dentry;
247         const struct cred *old_cred;
248 -       struct cred *override_cred;
249 -
250 -       override_cred = prepare_creds();
251 -       if (!override_cred)
252 -               return -ENOMEM;
253  
254 -       /*
255 -        * CAP_DAC_OVERRIDE for lookup
256 -        */
257 -       cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
258 -       old_cred = override_creds(override_cred);
259 +       old_cred = ovl_override_creds(rdd->dentry->d_sb);
260  
261         err = mutex_lock_killable(&dir->d_inode->i_mutex);
262         if (!err) {
263 @@ -231,7 +223,6 @@ static int ovl_check_whiteouts(struct de
264                 mutex_unlock(&dir->d_inode->i_mutex);
265         }
266         revert_creds(old_cred);
267 -       put_cred(override_cred);
268  
269         return err;
270  }
271 @@ -287,6 +278,7 @@ static int ovl_dir_read_merged(struct de
272         struct path realpath;
273         struct ovl_readdir_data rdd = {
274                 .ctx.actor = ovl_fill_merge,
275 +               .dentry = dentry,
276                 .list = list,
277                 .root = RB_ROOT,
278                 .is_lowest = false,
279 --- a/fs/overlayfs/super.c
280 +++ b/fs/overlayfs/super.c
281 @@ -42,6 +42,8 @@ struct ovl_fs {
282         long lower_namelen;
283         /* pathnames of lower and upper dirs, for show_options */
284         struct ovl_config config;
285 +       /* creds of process who forced instantiation of super block */
286 +       const struct cred *creator_cred;
287  };
288  
289  struct ovl_dir_cache;
290 @@ -246,6 +248,13 @@ bool ovl_is_whiteout(struct dentry *dent
291         return inode && IS_WHITEOUT(inode);
292  }
293  
294 +const struct cred *ovl_override_creds(struct super_block *sb)
295 +{
296 +       struct ovl_fs *ofs = sb->s_fs_info;
297 +
298 +       return override_creds(ofs->creator_cred);
299 +}
300 +
301  static bool ovl_is_opaquedir(struct dentry *dentry)
302  {
303         int res;
304 @@ -587,6 +596,7 @@ static void ovl_put_super(struct super_b
305         kfree(ufs->config.lowerdir);
306         kfree(ufs->config.upperdir);
307         kfree(ufs->config.workdir);
308 +       put_cred(ufs->creator_cred);
309         kfree(ufs);
310  }
311  
312 @@ -1087,10 +1097,14 @@ static int ovl_fill_super(struct super_b
313         else
314                 sb->s_d_op = &ovl_dentry_operations;
315  
316 +       ufs->creator_cred = prepare_creds();
317 +       if (!ufs->creator_cred)
318 +               goto out_put_lower_mnt;
319 +
320         err = -ENOMEM;
321         oe = ovl_alloc_entry(numlower);
322         if (!oe)
323 -               goto out_put_lower_mnt;
324 +               goto out_put_cred;
325  
326         root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, oe));
327         if (!root_dentry)
328 @@ -1123,6 +1137,8 @@ static int ovl_fill_super(struct super_b
329  
330  out_free_oe:
331         kfree(oe);
332 +out_put_cred:
333 +       put_cred(ufs->creator_cred);
334  out_put_lower_mnt:
335         for (i = 0; i < ufs->numlower; i++)
336                 mntput(ufs->lower_mnt[i]);