Skip to content

Commit f2b00be

Browse files
author
Miklos Szeredi
committed
cap: fix conversions on getxattr
If a capability is stored on disk in v2 format cap_inode_getsecurity() will currently return in v2 format unconditionally. This is wrong: v2 cap should be equivalent to a v3 cap with zero rootid, and so the same conversions performed on it. If the rootid cannot be mapped, v3 is returned unconverted. Fix this so that both v2 and v3 return -EOVERFLOW if the rootid (or the owner of the fs user namespace in case of v2) cannot be mapped into the current user namespace. Signed-off-by: Miklos Szeredi <[email protected]> Acked-by: "Eric W. Biederman" <[email protected]>
1 parent 554677b commit f2b00be

File tree

1 file changed

+43
-24
lines changed

1 file changed

+43
-24
lines changed

security/commoncap.c

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -371,10 +371,11 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer,
371371
{
372372
int size, ret;
373373
kuid_t kroot;
374+
u32 nsmagic, magic;
374375
uid_t root, mappedroot;
375376
char *tmpbuf = NULL;
376377
struct vfs_cap_data *cap;
377-
struct vfs_ns_cap_data *nscap;
378+
struct vfs_ns_cap_data *nscap = NULL;
378379
struct dentry *dentry;
379380
struct user_namespace *fs_ns;
380381

@@ -396,56 +397,74 @@ int cap_inode_getsecurity(struct inode *inode, const char *name, void **buffer,
396397
fs_ns = inode->i_sb->s_user_ns;
397398
cap = (struct vfs_cap_data *) tmpbuf;
398399
if (is_v2header((size_t) ret, cap)) {
399-
/* If this is sizeof(vfs_cap_data) then we're ok with the
400-
* on-disk value, so return that. */
401-
if (alloc)
402-
*buffer = tmpbuf;
403-
else
404-
kfree(tmpbuf);
405-
return ret;
406-
} else if (!is_v3header((size_t) ret, cap)) {
407-
kfree(tmpbuf);
408-
return -EINVAL;
400+
root = 0;
401+
} else if (is_v3header((size_t) ret, cap)) {
402+
nscap = (struct vfs_ns_cap_data *) tmpbuf;
403+
root = le32_to_cpu(nscap->rootid);
404+
} else {
405+
size = -EINVAL;
406+
goto out_free;
409407
}
410408

411-
nscap = (struct vfs_ns_cap_data *) tmpbuf;
412-
root = le32_to_cpu(nscap->rootid);
413409
kroot = make_kuid(fs_ns, root);
414410

415411
/* If the root kuid maps to a valid uid in current ns, then return
416412
* this as a nscap. */
417413
mappedroot = from_kuid(current_user_ns(), kroot);
418414
if (mappedroot != (uid_t)-1 && mappedroot != (uid_t)0) {
415+
size = sizeof(struct vfs_ns_cap_data);
419416
if (alloc) {
420-
*buffer = tmpbuf;
417+
if (!nscap) {
418+
/* v2 -> v3 conversion */
419+
nscap = kzalloc(size, GFP_ATOMIC);
420+
if (!nscap) {
421+
size = -ENOMEM;
422+
goto out_free;
423+
}
424+
nsmagic = VFS_CAP_REVISION_3;
425+
magic = le32_to_cpu(cap->magic_etc);
426+
if (magic & VFS_CAP_FLAGS_EFFECTIVE)
427+
nsmagic |= VFS_CAP_FLAGS_EFFECTIVE;
428+
memcpy(&nscap->data, &cap->data, sizeof(__le32) * 2 * VFS_CAP_U32);
429+
nscap->magic_etc = cpu_to_le32(nsmagic);
430+
} else {
431+
/* use allocated v3 buffer */
432+
tmpbuf = NULL;
433+
}
421434
nscap->rootid = cpu_to_le32(mappedroot);
422-
} else
423-
kfree(tmpbuf);
424-
return size;
435+
*buffer = nscap;
436+
}
437+
goto out_free;
425438
}
426439

427440
if (!rootid_owns_currentns(kroot)) {
428-
kfree(tmpbuf);
429-
return -EOPNOTSUPP;
441+
size = -EOVERFLOW;
442+
goto out_free;
430443
}
431444

432445
/* This comes from a parent namespace. Return as a v2 capability */
433446
size = sizeof(struct vfs_cap_data);
434447
if (alloc) {
435-
*buffer = kmalloc(size, GFP_ATOMIC);
436-
if (*buffer) {
437-
struct vfs_cap_data *cap = *buffer;
438-
__le32 nsmagic, magic;
448+
if (nscap) {
449+
/* v3 -> v2 conversion */
450+
cap = kzalloc(size, GFP_ATOMIC);
451+
if (!cap) {
452+
size = -ENOMEM;
453+
goto out_free;
454+
}
439455
magic = VFS_CAP_REVISION_2;
440456
nsmagic = le32_to_cpu(nscap->magic_etc);
441457
if (nsmagic & VFS_CAP_FLAGS_EFFECTIVE)
442458
magic |= VFS_CAP_FLAGS_EFFECTIVE;
443459
memcpy(&cap->data, &nscap->data, sizeof(__le32) * 2 * VFS_CAP_U32);
444460
cap->magic_etc = cpu_to_le32(magic);
445461
} else {
446-
size = -ENOMEM;
462+
/* use unconverted v2 */
463+
tmpbuf = NULL;
447464
}
465+
*buffer = cap;
448466
}
467+
out_free:
449468
kfree(tmpbuf);
450469
return size;
451470
}

0 commit comments

Comments
 (0)