diff options
Diffstat (limited to 'security/commoncap.c')
-rw-r--r-- | security/commoncap.c | 129 |
1 files changed, 71 insertions, 58 deletions
diff --git a/security/commoncap.c b/security/commoncap.c index f88119cb2bc2..d7eff5797b91 100644 --- a/security/commoncap.c +++ b/security/commoncap.c | |||
@@ -202,17 +202,70 @@ int cap_inode_killpriv(struct dentry *dentry) | |||
202 | return inode->i_op->removexattr(dentry, XATTR_NAME_CAPS); | 202 | return inode->i_op->removexattr(dentry, XATTR_NAME_CAPS); |
203 | } | 203 | } |
204 | 204 | ||
205 | static inline int cap_from_disk(struct vfs_cap_data *caps, | 205 | static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps, |
206 | struct linux_binprm *bprm, unsigned size) | 206 | struct linux_binprm *bprm) |
207 | { | 207 | { |
208 | unsigned i; | ||
209 | int ret = 0; | ||
210 | |||
211 | if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE) | ||
212 | bprm->cap_effective = true; | ||
213 | else | ||
214 | bprm->cap_effective = false; | ||
215 | |||
216 | CAP_FOR_EACH_U32(i) { | ||
217 | __u32 permitted = caps->permitted.cap[i]; | ||
218 | __u32 inheritable = caps->inheritable.cap[i]; | ||
219 | |||
220 | /* | ||
221 | * pP' = (X & fP) | (pI & fI) | ||
222 | */ | ||
223 | bprm->cap_post_exec_permitted.cap[i] = | ||
224 | (current->cap_bset.cap[i] & permitted) | | ||
225 | (current->cap_inheritable.cap[i] & inheritable); | ||
226 | |||
227 | if (permitted & ~bprm->cap_post_exec_permitted.cap[i]) { | ||
228 | /* | ||
229 | * insufficient to execute correctly | ||
230 | */ | ||
231 | ret = -EPERM; | ||
232 | } | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * For legacy apps, with no internal support for recognizing they | ||
237 | * do not have enough capabilities, we return an error if they are | ||
238 | * missing some "forced" (aka file-permitted) capabilities. | ||
239 | */ | ||
240 | return bprm->cap_effective ? ret : 0; | ||
241 | } | ||
242 | |||
243 | int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps) | ||
244 | { | ||
245 | struct inode *inode = dentry->d_inode; | ||
208 | __u32 magic_etc; | 246 | __u32 magic_etc; |
209 | unsigned tocopy, i; | 247 | unsigned tocopy, i; |
210 | int ret; | 248 | int size; |
249 | struct vfs_cap_data caps; | ||
250 | |||
251 | memset(cpu_caps, 0, sizeof(struct cpu_vfs_cap_data)); | ||
252 | |||
253 | if (!inode || !inode->i_op || !inode->i_op->getxattr) | ||
254 | return -ENODATA; | ||
255 | |||
256 | size = inode->i_op->getxattr((struct dentry *)dentry, XATTR_NAME_CAPS, &caps, | ||
257 | XATTR_CAPS_SZ); | ||
258 | if (size == -ENODATA || size == -EOPNOTSUPP) { | ||
259 | /* no data, that's ok */ | ||
260 | return -ENODATA; | ||
261 | } | ||
262 | if (size < 0) | ||
263 | return size; | ||
211 | 264 | ||
212 | if (size < sizeof(magic_etc)) | 265 | if (size < sizeof(magic_etc)) |
213 | return -EINVAL; | 266 | return -EINVAL; |
214 | 267 | ||
215 | magic_etc = le32_to_cpu(caps->magic_etc); | 268 | cpu_caps->magic_etc = magic_etc = le32_to_cpu(caps.magic_etc); |
216 | 269 | ||
217 | switch ((magic_etc & VFS_CAP_REVISION_MASK)) { | 270 | switch ((magic_etc & VFS_CAP_REVISION_MASK)) { |
218 | case VFS_CAP_REVISION_1: | 271 | case VFS_CAP_REVISION_1: |
@@ -229,46 +282,13 @@ static inline int cap_from_disk(struct vfs_cap_data *caps, | |||
229 | return -EINVAL; | 282 | return -EINVAL; |
230 | } | 283 | } |
231 | 284 | ||
232 | if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE) { | ||
233 | bprm->cap_effective = true; | ||
234 | } else { | ||
235 | bprm->cap_effective = false; | ||
236 | } | ||
237 | |||
238 | ret = 0; | ||
239 | |||
240 | CAP_FOR_EACH_U32(i) { | 285 | CAP_FOR_EACH_U32(i) { |
241 | __u32 value_cpu; | 286 | if (i >= tocopy) |
242 | 287 | break; | |
243 | if (i >= tocopy) { | 288 | cpu_caps->permitted.cap[i] = le32_to_cpu(caps.data[i].permitted); |
244 | /* | 289 | cpu_caps->inheritable.cap[i] = le32_to_cpu(caps.data[i].inheritable); |
245 | * Legacy capability sets have no upper bits | ||
246 | */ | ||
247 | bprm->cap_post_exec_permitted.cap[i] = 0; | ||
248 | continue; | ||
249 | } | ||
250 | /* | ||
251 | * pP' = (X & fP) | (pI & fI) | ||
252 | */ | ||
253 | value_cpu = le32_to_cpu(caps->data[i].permitted); | ||
254 | bprm->cap_post_exec_permitted.cap[i] = | ||
255 | (current->cap_bset.cap[i] & value_cpu) | | ||
256 | (current->cap_inheritable.cap[i] & | ||
257 | le32_to_cpu(caps->data[i].inheritable)); | ||
258 | if (value_cpu & ~bprm->cap_post_exec_permitted.cap[i]) { | ||
259 | /* | ||
260 | * insufficient to execute correctly | ||
261 | */ | ||
262 | ret = -EPERM; | ||
263 | } | ||
264 | } | 290 | } |
265 | 291 | return 0; | |
266 | /* | ||
267 | * For legacy apps, with no internal support for recognizing they | ||
268 | * do not have enough capabilities, we return an error if they are | ||
269 | * missing some "forced" (aka file-permitted) capabilities. | ||
270 | */ | ||
271 | return bprm->cap_effective ? ret : 0; | ||
272 | } | 292 | } |
273 | 293 | ||
274 | /* Locate any VFS capabilities: */ | 294 | /* Locate any VFS capabilities: */ |
@@ -276,8 +296,7 @@ static int get_file_caps(struct linux_binprm *bprm) | |||
276 | { | 296 | { |
277 | struct dentry *dentry; | 297 | struct dentry *dentry; |
278 | int rc = 0; | 298 | int rc = 0; |
279 | struct vfs_cap_data vcaps; | 299 | struct cpu_vfs_cap_data vcaps; |
280 | struct inode *inode; | ||
281 | 300 | ||
282 | bprm_clear_caps(bprm); | 301 | bprm_clear_caps(bprm); |
283 | 302 | ||
@@ -288,24 +307,18 @@ static int get_file_caps(struct linux_binprm *bprm) | |||
288 | return 0; | 307 | return 0; |
289 | 308 | ||
290 | dentry = dget(bprm->file->f_dentry); | 309 | dentry = dget(bprm->file->f_dentry); |
291 | inode = dentry->d_inode; | ||
292 | if (!inode->i_op || !inode->i_op->getxattr) | ||
293 | goto out; | ||
294 | 310 | ||
295 | rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, &vcaps, | 311 | rc = get_vfs_caps_from_disk(dentry, &vcaps); |
296 | XATTR_CAPS_SZ); | 312 | if (rc < 0) { |
297 | if (rc == -ENODATA || rc == -EOPNOTSUPP) { | 313 | if (rc == -EINVAL) |
298 | /* no data, that's ok */ | 314 | printk(KERN_NOTICE "%s: get_vfs_caps_from_disk returned %d for %s\n", |
299 | rc = 0; | 315 | __func__, rc, bprm->filename); |
316 | else if (rc == -ENODATA) | ||
317 | rc = 0; | ||
300 | goto out; | 318 | goto out; |
301 | } | 319 | } |
302 | if (rc < 0) | ||
303 | goto out; | ||
304 | 320 | ||
305 | rc = cap_from_disk(&vcaps, bprm, rc); | 321 | rc = bprm_caps_from_vfs_caps(&vcaps, bprm); |
306 | if (rc == -EINVAL) | ||
307 | printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", | ||
308 | __func__, rc, bprm->filename); | ||
309 | 322 | ||
310 | out: | 323 | out: |
311 | dput(dentry); | 324 | dput(dentry); |