|
@@ -202,17 +202,70 @@ int cap_inode_killpriv(struct dentry *dentry)
|
|
return inode->i_op->removexattr(dentry, XATTR_NAME_CAPS);
|
|
return inode->i_op->removexattr(dentry, XATTR_NAME_CAPS);
|
|
}
|
|
}
|
|
|
|
|
|
-static inline int cap_from_disk(struct vfs_cap_data *caps,
|
|
|
|
- struct linux_binprm *bprm, unsigned size)
|
|
|
|
|
|
+static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
|
|
|
|
+ struct linux_binprm *bprm)
|
|
{
|
|
{
|
|
|
|
+ unsigned i;
|
|
|
|
+ int ret = 0;
|
|
|
|
+
|
|
|
|
+ if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
|
|
|
|
+ bprm->cap_effective = true;
|
|
|
|
+ else
|
|
|
|
+ bprm->cap_effective = false;
|
|
|
|
+
|
|
|
|
+ CAP_FOR_EACH_U32(i) {
|
|
|
|
+ __u32 permitted = caps->permitted.cap[i];
|
|
|
|
+ __u32 inheritable = caps->inheritable.cap[i];
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * pP' = (X & fP) | (pI & fI)
|
|
|
|
+ */
|
|
|
|
+ bprm->cap_post_exec_permitted.cap[i] =
|
|
|
|
+ (current->cap_bset.cap[i] & permitted) |
|
|
|
|
+ (current->cap_inheritable.cap[i] & inheritable);
|
|
|
|
+
|
|
|
|
+ if (permitted & ~bprm->cap_post_exec_permitted.cap[i]) {
|
|
|
|
+ /*
|
|
|
|
+ * insufficient to execute correctly
|
|
|
|
+ */
|
|
|
|
+ ret = -EPERM;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * For legacy apps, with no internal support for recognizing they
|
|
|
|
+ * do not have enough capabilities, we return an error if they are
|
|
|
|
+ * missing some "forced" (aka file-permitted) capabilities.
|
|
|
|
+ */
|
|
|
|
+ return bprm->cap_effective ? ret : 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps)
|
|
|
|
+{
|
|
|
|
+ struct inode *inode = dentry->d_inode;
|
|
__u32 magic_etc;
|
|
__u32 magic_etc;
|
|
unsigned tocopy, i;
|
|
unsigned tocopy, i;
|
|
- int ret;
|
|
|
|
|
|
+ int size;
|
|
|
|
+ struct vfs_cap_data caps;
|
|
|
|
+
|
|
|
|
+ memset(cpu_caps, 0, sizeof(struct cpu_vfs_cap_data));
|
|
|
|
+
|
|
|
|
+ if (!inode || !inode->i_op || !inode->i_op->getxattr)
|
|
|
|
+ return -ENODATA;
|
|
|
|
+
|
|
|
|
+ size = inode->i_op->getxattr((struct dentry *)dentry, XATTR_NAME_CAPS, &caps,
|
|
|
|
+ XATTR_CAPS_SZ);
|
|
|
|
+ if (size == -ENODATA || size == -EOPNOTSUPP) {
|
|
|
|
+ /* no data, that's ok */
|
|
|
|
+ return -ENODATA;
|
|
|
|
+ }
|
|
|
|
+ if (size < 0)
|
|
|
|
+ return size;
|
|
|
|
|
|
if (size < sizeof(magic_etc))
|
|
if (size < sizeof(magic_etc))
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
- magic_etc = le32_to_cpu(caps->magic_etc);
|
|
|
|
|
|
+ cpu_caps->magic_etc = magic_etc = le32_to_cpu(caps.magic_etc);
|
|
|
|
|
|
switch ((magic_etc & VFS_CAP_REVISION_MASK)) {
|
|
switch ((magic_etc & VFS_CAP_REVISION_MASK)) {
|
|
case VFS_CAP_REVISION_1:
|
|
case VFS_CAP_REVISION_1:
|
|
@@ -229,46 +282,13 @@ static inline int cap_from_disk(struct vfs_cap_data *caps,
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
|
|
- if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE) {
|
|
|
|
- bprm->cap_effective = true;
|
|
|
|
- } else {
|
|
|
|
- bprm->cap_effective = false;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- ret = 0;
|
|
|
|
-
|
|
|
|
CAP_FOR_EACH_U32(i) {
|
|
CAP_FOR_EACH_U32(i) {
|
|
- __u32 value_cpu;
|
|
|
|
-
|
|
|
|
- if (i >= tocopy) {
|
|
|
|
- /*
|
|
|
|
- * Legacy capability sets have no upper bits
|
|
|
|
- */
|
|
|
|
- bprm->cap_post_exec_permitted.cap[i] = 0;
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- /*
|
|
|
|
- * pP' = (X & fP) | (pI & fI)
|
|
|
|
- */
|
|
|
|
- value_cpu = le32_to_cpu(caps->data[i].permitted);
|
|
|
|
- bprm->cap_post_exec_permitted.cap[i] =
|
|
|
|
- (current->cap_bset.cap[i] & value_cpu) |
|
|
|
|
- (current->cap_inheritable.cap[i] &
|
|
|
|
- le32_to_cpu(caps->data[i].inheritable));
|
|
|
|
- if (value_cpu & ~bprm->cap_post_exec_permitted.cap[i]) {
|
|
|
|
- /*
|
|
|
|
- * insufficient to execute correctly
|
|
|
|
- */
|
|
|
|
- ret = -EPERM;
|
|
|
|
- }
|
|
|
|
|
|
+ if (i >= tocopy)
|
|
|
|
+ break;
|
|
|
|
+ cpu_caps->permitted.cap[i] = le32_to_cpu(caps.data[i].permitted);
|
|
|
|
+ cpu_caps->inheritable.cap[i] = le32_to_cpu(caps.data[i].inheritable);
|
|
}
|
|
}
|
|
-
|
|
|
|
- /*
|
|
|
|
- * For legacy apps, with no internal support for recognizing they
|
|
|
|
- * do not have enough capabilities, we return an error if they are
|
|
|
|
- * missing some "forced" (aka file-permitted) capabilities.
|
|
|
|
- */
|
|
|
|
- return bprm->cap_effective ? ret : 0;
|
|
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
/* Locate any VFS capabilities: */
|
|
/* Locate any VFS capabilities: */
|
|
@@ -276,8 +296,7 @@ static int get_file_caps(struct linux_binprm *bprm)
|
|
{
|
|
{
|
|
struct dentry *dentry;
|
|
struct dentry *dentry;
|
|
int rc = 0;
|
|
int rc = 0;
|
|
- struct vfs_cap_data vcaps;
|
|
|
|
- struct inode *inode;
|
|
|
|
|
|
+ struct cpu_vfs_cap_data vcaps;
|
|
|
|
|
|
bprm_clear_caps(bprm);
|
|
bprm_clear_caps(bprm);
|
|
|
|
|
|
@@ -288,24 +307,18 @@ static int get_file_caps(struct linux_binprm *bprm)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
dentry = dget(bprm->file->f_dentry);
|
|
dentry = dget(bprm->file->f_dentry);
|
|
- inode = dentry->d_inode;
|
|
|
|
- if (!inode->i_op || !inode->i_op->getxattr)
|
|
|
|
- goto out;
|
|
|
|
|
|
|
|
- rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, &vcaps,
|
|
|
|
- XATTR_CAPS_SZ);
|
|
|
|
- if (rc == -ENODATA || rc == -EOPNOTSUPP) {
|
|
|
|
- /* no data, that's ok */
|
|
|
|
- rc = 0;
|
|
|
|
|
|
+ rc = get_vfs_caps_from_disk(dentry, &vcaps);
|
|
|
|
+ if (rc < 0) {
|
|
|
|
+ if (rc == -EINVAL)
|
|
|
|
+ printk(KERN_NOTICE "%s: get_vfs_caps_from_disk returned %d for %s\n",
|
|
|
|
+ __func__, rc, bprm->filename);
|
|
|
|
+ else if (rc == -ENODATA)
|
|
|
|
+ rc = 0;
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
- if (rc < 0)
|
|
|
|
- goto out;
|
|
|
|
|
|
|
|
- rc = cap_from_disk(&vcaps, bprm, rc);
|
|
|
|
- if (rc == -EINVAL)
|
|
|
|
- printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
|
|
|
|
- __func__, rc, bprm->filename);
|
|
|
|
|
|
+ rc = bprm_caps_from_vfs_caps(&vcaps, bprm);
|
|
|
|
|
|
out:
|
|
out:
|
|
dput(dentry);
|
|
dput(dentry);
|