-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Serge.
Three comments in-line:
Serge E. Hallyn wrote:
>>From 24e8c76b50cc40b37ef65b69405cf6d6fa02865f Mon Sep 17 00:00:00 2001
> From: Serge E. Hallyn <[EMAIL PROTECTED]>
> Date: Thu, 12 Jul 2007 12:21:25 -0400
> Subject: [PATCH 1/1] file capabilities: change xattr format and eff meaning
>
> This patch implements three suggestions by Andrew Morgan. The first
> is to change the on disk format. The __le32 version, is renamed
> 'magic_etc', and contains a revision number (starting at 1), and
> a bit representing Fe. The Fe capset is no longer stored, so the
> xattr is now 3 ints instead of 4.
>
> The Fe was previously a full capset which was masked with
> the calculated new_permitted to get the process' new effective
> set. It is now a single bit. When that bit is off, the
> P'e is the empty set. When the bit is on, then P'e is set
> equal to P'p (new_permitted). The rationale for this is
> that either the application does not know about capabilities,
> and needs to start with all permitted caps in its effective
> set, or it does know about capabilities, and can start with
> an empty effective set and enable the caps it wants when it
> wants.
>
> Finally, future format compatibility is reduced. If
> a security.capability xattr is found with too new a version,
> don't run the binary. (It may be better to at least just ignore
> the xattr)
(FWIW I don't agree this would be better. If the running kernel sees a
privilege requirement that it cannot fulfill - because it doesn't
understand it - it should fail safe.)
>
> (I'll send an (17k) ltp patch to test this to anyone who asks.)
>
> Signed-off-by: Serge E. Hallyn <[EMAIL PROTECTED]>
> ---
> include/linux/capability.h | 35 ++++++---------------
> security/commoncap.c | 71 +++++++++++++++++--------------------------
> 2 files changed, 38 insertions(+), 68 deletions(-)
>
> diff --git a/include/linux/capability.h b/include/linux/capability.h
> index cdfaa10..0ee4f91 100644
> --- a/include/linux/capability.h
> +++ b/include/linux/capability.h
> @@ -47,38 +47,23 @@ typedef struct __user_cap_data_struct {
> #define XATTR_CAPS_SUFFIX "capability"
> #define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX
>
> -/* size of caps that we work with */
> -#define XATTR_CAPS_SZ (4*sizeof(__le32))
> +#define XATTR_CAPS_SZ (3*sizeof(__le32))
> +#define VFS_CAP_REVISION_MASK 0xFF000000
> +#define VFS_CAP_REVISION 0x01000000
>
> -/*
> - * data[] is organized as:
> - * effective[0]
> - * permitted[0]
> - * inheritable[0]
> - * effective[1]
> - * ...
> - * this way we can just read as much of the on-disk capability as
> - * we know should exist and know we'll get the data we'll need.
> - */
> -struct vfs_cap_data_disk {
> - __le32 version;
> - __le32 data[]; /* eff[0], perm[0], inh[0], eff[1], ... */
> -};
> -
> -struct vfs_cap_data_disk_v1 {
> - __le32 version;
> - __le32 data[3]; /* eff[0], perm[0], inh[0] */
> -};
> +#define VFS_CAP_FLAGS_MASK ~VFS_CAP_REVISION_MASK
> +#define VFS_CAP_FLAGS_EFFECTIVE 0x000001
>
> #ifdef __KERNEL__
>
> #include <asm/current.h>
>
> struct vfs_cap_data {
> - __u32 version;
> - __u32 effective;
> - __u32 permitted;
> - __u32 inheritable;
> + __u32 magic_etc; /* Little endian */
> + struct {
> + __u32 permitted; /* Little endian */
> + __u32 inheritable; /* Little endian */
> + } data[1];
> };
>
> /* #define STRICT_CAP_T_TYPECHECKS */
> diff --git a/security/commoncap.c b/security/commoncap.c
> index b08d250..284bbb5 100644
> --- a/security/commoncap.c
> +++ b/security/commoncap.c
> @@ -118,27 +118,26 @@ static inline void bprm_clear_caps(struct linux_binprm
> *bprm)
>
> #ifdef CONFIG_SECURITY_FILE_CAPABILITIES
>
> -static inline int cap_from_disk(struct vfs_cap_data_disk *dcap,
> - struct linux_binprm *bprm, int size)
> +static inline int cap_from_disk(__le32 *caps, struct linux_binprm *bprm,
> + int size)
> {
> - int version;
magic_etc seems like it should be __u32:
> + int magic_etc;
>
> - version = le32_to_cpu(dcap->version);
> - if (version != _LINUX_CAPABILITY_VERSION)
> + if (size != XATTR_CAPS_SZ)
> return -EINVAL;
>
> - size /= sizeof(u32);
> - if ((size-1)%3) {
> - printk(KERN_WARNING "%s: size is an invalid size %d for %s\n",
> - __FUNCTION__, size,
> bprm->filename);
> + magic_etc = le32_to_cpu(caps[0]);
> +
> + switch ((magic_etc & VFS_CAP_REVISION_MASK)) {
> + case VFS_CAP_REVISION:
> + bprm->cap_effective = (magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
> + ? CAP_FULL_SET : CAP_EMPTY_SET;
> + bprm->cap_permitted = to_cap_t( le32_to_cpu(caps[1]) );
> + bprm->cap_inheritable = to_cap_t( le32_to_cpu(caps[2]) );
> + return 0;
> + default:
> return -EINVAL;
> }
> -
> - bprm->cap_effective = le32_to_cpu(dcap->data[0]);
> - bprm->cap_permitted = le32_to_cpu(dcap->data[1]);
> - bprm->cap_inheritable = le32_to_cpu(dcap->data[2]);
> -
> - return 0;
> }
>
> /* Locate any VFS capabilities: */
> @@ -146,11 +145,9 @@ static int get_file_caps(struct linux_binprm *bprm)
> {
> struct dentry *dentry;
> int rc = 0;
> - struct vfs_cap_data_disk_v1 v1caps;
> - struct vfs_cap_data_disk *dcaps;
> + __le32 v1caps[XATTR_CAPS_SZ];
> struct inode *inode;
>
> - dcaps = (struct vfs_cap_data_disk *)&v1caps;
> if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) {
> bprm_clear_caps(bprm);
> return 0;
> @@ -161,40 +158,23 @@ static int get_file_caps(struct linux_binprm *bprm)
> if (!inode->i_op || !inode->i_op->getxattr)
> goto out;
>
> - rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, dcaps,
> + rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, &v1caps,
> XATTR_CAPS_SZ);
> if (rc == -ENODATA || rc == -EOPNOTSUPP) {
> + /* no data, that's ok */
> rc = 0;
> goto out;
> }
> - if (rc == -ERANGE) {
> - int size;
> - size = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, NULL, 0);
> - if (size <= 0) { /* shouldn't ever happen */
> - rc = -EINVAL;
> - goto out;
> - }
> - dcaps = kmalloc(size, GFP_KERNEL);
> - if (!dcaps) {
> - rc = -ENOMEM;
> - goto out;
> - }
> - rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, dcaps,
> - size);
> - }
> if (rc < 0)
> goto out;
> - if (rc < sizeof(struct vfs_cap_data_disk_v1)) {
> - rc = -EINVAL;
> - goto out;
> - }
>
> - rc = cap_from_disk(dcaps, bprm, rc);
> + rc = cap_from_disk(v1caps, bprm, rc);
> + if (rc)
> + printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
> + __FUNCTION__, rc, bprm->filename);
>
> out:
> dput(dentry);
> - if ((void *)dcaps != (void *)&v1caps)
> - kfree(dcaps);
> if (rc)
> bprm_clear_caps(bprm);
>
> @@ -214,6 +194,9 @@ int cap_bprm_set_security (struct linux_binprm *bprm)
> int ret;
>
> ret = get_file_caps(bprm);
> + if (ret)
> + printk(KERN_NOTICE "%s: get_file_caps returned %d for %s\n",
> + __FUNCTION__, ret, bprm->filename);
>
> /* To support inheritance of root-permissions and suid-root
> * executables under compatibility mode, we raise all three
> @@ -269,8 +252,10 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm,
> int unsafe)
> * capability rules */
> if (!is_init(current)) {
> current->cap_permitted = new_permitted;
I'm not sure I agree with this next change. If you are going to change
this code, you can get by with an abbreviated (bool) bprm->cap_effective
(it doesn't need to be stored within the kernel as a full set).
For now, I would just leave this part of the patch out.
> - current->cap_effective =
> - cap_intersect (new_permitted, bprm->cap_effective);
> + if (cap_isclear(bprm->cap_effective))
> + cap_clear(current->cap_effective);
> + else
> + current->cap_effective = new_permitted;
> }
>
> /* AUD: Audit candidate if current->cap_effective is set */
Cheers
Andrew
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (Darwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFGl5jh+bHCR3gb8jsRArXrAJ4sMbGTQiAdN3uZFiohOmKI50FcAQCfWJDw
a4CzlMKcjprEj/Lfxl2c0n0=
=uElm
-----END PGP SIGNATURE-----
-
To unsubscribe from this list: send the line "unsubscribe
linux-security-module" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html