]>
Commit | Line | Data |
---|---|---|
9208eabb GKH |
1 | From 98487de318a6f33312471ae1e2afa16fbf8361fe Mon Sep 17 00:00:00 2001 |
2 | From: Jiufei Xue <jiufei.xue@linux.alibaba.com> | |
3 | Date: Mon, 6 May 2019 15:41:02 +0800 | |
4 | Subject: ovl: check the capability before cred overridden | |
5 | ||
6 | From: Jiufei Xue <jiufei.xue@linux.alibaba.com> | |
7 | ||
8 | commit 98487de318a6f33312471ae1e2afa16fbf8361fe upstream. | |
9 | ||
10 | We found that it return success when we set IMMUTABLE_FL flag to a file in | |
11 | docker even though the docker didn't have the capability | |
12 | CAP_LINUX_IMMUTABLE. | |
13 | ||
14 | The commit d1d04ef8572b ("ovl: stack file ops") and dab5ca8fd9dd ("ovl: add | |
15 | lsattr/chattr support") implemented chattr operations on a regular overlay | |
16 | file. ovl_real_ioctl() overridden the current process's subjective | |
17 | credentials with ofs->creator_cred which have the capability | |
18 | CAP_LINUX_IMMUTABLE so that it will return success in | |
19 | vfs_ioctl()->cap_capable(). | |
20 | ||
21 | Fix this by checking the capability before cred overridden. And here we | |
22 | only care about APPEND_FL and IMMUTABLE_FL, so get these information from | |
23 | inode. | |
24 | ||
25 | [SzM: move check and call to underlying fs inside inode locked region to | |
26 | prevent two such calls from racing with each other] | |
27 | ||
28 | Signed-off-by: Jiufei Xue <jiufei.xue@linux.alibaba.com> | |
29 | Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> | |
30 | Cc: Amir Goldstein <amir73il@gmail.com> | |
31 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
32 | ||
33 | --- | |
34 | fs/overlayfs/file.c | 79 ++++++++++++++++++++++++++++++++++++++++------------ | |
35 | 1 file changed, 61 insertions(+), 18 deletions(-) | |
36 | ||
37 | --- a/fs/overlayfs/file.c | |
38 | +++ b/fs/overlayfs/file.c | |
39 | @@ -11,6 +11,7 @@ | |
40 | #include <linux/mount.h> | |
41 | #include <linux/xattr.h> | |
42 | #include <linux/uio.h> | |
43 | +#include <linux/uaccess.h> | |
44 | #include "overlayfs.h" | |
45 | ||
46 | static char ovl_whatisit(struct inode *inode, struct inode *realinode) | |
47 | @@ -372,10 +373,68 @@ static long ovl_real_ioctl(struct file * | |
48 | return ret; | |
49 | } | |
50 | ||
51 | -static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |
52 | +static unsigned int ovl_get_inode_flags(struct inode *inode) | |
53 | +{ | |
54 | + unsigned int flags = READ_ONCE(inode->i_flags); | |
55 | + unsigned int ovl_iflags = 0; | |
56 | + | |
57 | + if (flags & S_SYNC) | |
58 | + ovl_iflags |= FS_SYNC_FL; | |
59 | + if (flags & S_APPEND) | |
60 | + ovl_iflags |= FS_APPEND_FL; | |
61 | + if (flags & S_IMMUTABLE) | |
62 | + ovl_iflags |= FS_IMMUTABLE_FL; | |
63 | + if (flags & S_NOATIME) | |
64 | + ovl_iflags |= FS_NOATIME_FL; | |
65 | + | |
66 | + return ovl_iflags; | |
67 | +} | |
68 | + | |
69 | +static long ovl_ioctl_set_flags(struct file *file, unsigned long arg) | |
70 | { | |
71 | long ret; | |
72 | struct inode *inode = file_inode(file); | |
73 | + unsigned int flags; | |
74 | + unsigned int old_flags; | |
75 | + | |
76 | + if (!inode_owner_or_capable(inode)) | |
77 | + return -EACCES; | |
78 | + | |
79 | + if (get_user(flags, (int __user *) arg)) | |
80 | + return -EFAULT; | |
81 | + | |
82 | + ret = mnt_want_write_file(file); | |
83 | + if (ret) | |
84 | + return ret; | |
85 | + | |
86 | + inode_lock(inode); | |
87 | + | |
88 | + /* Check the capability before cred override */ | |
89 | + ret = -EPERM; | |
90 | + old_flags = ovl_get_inode_flags(inode); | |
91 | + if (((flags ^ old_flags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) && | |
92 | + !capable(CAP_LINUX_IMMUTABLE)) | |
93 | + goto unlock; | |
94 | + | |
95 | + ret = ovl_maybe_copy_up(file_dentry(file), O_WRONLY); | |
96 | + if (ret) | |
97 | + goto unlock; | |
98 | + | |
99 | + ret = ovl_real_ioctl(file, FS_IOC_SETFLAGS, arg); | |
100 | + | |
101 | + ovl_copyflags(ovl_inode_real(inode), inode); | |
102 | +unlock: | |
103 | + inode_unlock(inode); | |
104 | + | |
105 | + mnt_drop_write_file(file); | |
106 | + | |
107 | + return ret; | |
108 | + | |
109 | +} | |
110 | + | |
111 | +static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |
112 | +{ | |
113 | + long ret; | |
114 | ||
115 | switch (cmd) { | |
116 | case FS_IOC_GETFLAGS: | |
117 | @@ -383,23 +442,7 @@ static long ovl_ioctl(struct file *file, | |
118 | break; | |
119 | ||
120 | case FS_IOC_SETFLAGS: | |
121 | - if (!inode_owner_or_capable(inode)) | |
122 | - return -EACCES; | |
123 | - | |
124 | - ret = mnt_want_write_file(file); | |
125 | - if (ret) | |
126 | - return ret; | |
127 | - | |
128 | - ret = ovl_maybe_copy_up(file_dentry(file), O_WRONLY); | |
129 | - if (!ret) { | |
130 | - ret = ovl_real_ioctl(file, cmd, arg); | |
131 | - | |
132 | - inode_lock(inode); | |
133 | - ovl_copyflags(ovl_inode_real(inode), inode); | |
134 | - inode_unlock(inode); | |
135 | - } | |
136 | - | |
137 | - mnt_drop_write_file(file); | |
138 | + ret = ovl_ioctl_set_flags(file, arg); | |
139 | break; | |
140 | ||
141 | default: |