lkml.org 
[lkml]   [2006]   [Sep]   [10]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH 3/4] security: capabilities patch (version 0.4.4), part 3/4: introduce new capabilities

    Introduce six new "regular" (=on-by-default) capabilities:

    * CAP_REG_FORK, CAP_REG_OPEN, CAP_REG_EXEC allow access to the
    fork(), open() and exec() syscalls,

    * CAP_REG_SXID allows privilege gain on suid/sgid exec,

    * CAP_REG_WRITE controls any write-access to the filesystem,

    * CAP_REG_PTRACE allows ptrace().

    See <URL: http://www.madore.org/~david/linux/newcaps/ > for more
    detailed explanations.

    Signed-off-by: David A. Madore <david.madore@ens.fr>

    ---
    fs/exec.c | 4 ++
    include/linux/binfmts.h | 1
    include/linux/securebits.h | 4 ++
    kernel/capability.c | 2 -
    security/commoncap.c | 90 ++++++++++++++++++++++++++++++--------------
    5 files changed, 72 insertions(+), 29 deletions(-)

    diff --git a/fs/exec.c b/fs/exec.c
    index 54135df..e4d0a2c 100644
    --- a/fs/exec.c
    +++ b/fs/exec.c
    @@ -925,10 +925,13 @@ int prepare_binprm(struct linux_binprm *

    bprm->e_uid = current->euid;
    bprm->e_gid = current->egid;
    + bprm->is_suid = 0;
    + bprm->is_sgid = 0;

    if(!(bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID)) {
    /* Set-uid? */
    if (mode & S_ISUID) {
    + bprm->is_suid = 1;
    current->personality &= ~PER_CLEAR_ON_SETID;
    bprm->e_uid = inode->i_uid;
    }
    @@ -940,6 +943,7 @@ int prepare_binprm(struct linux_binprm *
    * executable.
    */
    if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
    + bprm->is_sgid = 1;
    current->personality &= ~PER_CLEAR_ON_SETID;
    bprm->e_gid = inode->i_gid;
    }
    diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
    index c1e82c5..c7fb183 100644
    --- a/include/linux/binfmts.h
    +++ b/include/linux/binfmts.h
    @@ -29,6 +29,7 @@ struct linux_binprm{
    struct file * file;
    int e_uid, e_gid;
    kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
    + char is_suid, is_sgid;
    void *security;
    int argc, envc;
    char * filename; /* Name of binary as seen by procps */
    diff --git a/include/linux/securebits.h b/include/linux/securebits.h
    index 5b06178..0092332 100644
    --- a/include/linux/securebits.h
    +++ b/include/linux/securebits.h
    @@ -18,6 +18,10 @@ #define SECURE_NOROOT 0
    privileges. When unset, setuid doesn't change privileges. */
    #define SECURE_NO_SETUID_FIXUP 2

    +/* When set, exec()ing a suid/sgid program does not force reinstate
    + all "regular" capabilities. */
    +#define SECURE_NO_SXID_SANITIZE 4
    +
    /* Each securesetting is implemented using two bits. One bit specify
    whether the setting is on or off. The other bit specify whether the
    setting is fixed or not. A setting which is fixed cannot be changed
    diff --git a/kernel/capability.c b/kernel/capability.c
    index 32b2521..2bb802a 100644
    --- a/kernel/capability.c
    +++ b/kernel/capability.c
    @@ -15,7 +15,7 @@ #include <linux/syscalls.h>
    #include <asm/uaccess.h>

    unsigned securebits = SECUREBITS_DEFAULT; /* systemwide security settings */
    -kernel_cap_t cap_bset = CAP_INIT_EFF_SET;
    +kernel_cap_t cap_bset = CAP_INIT_INH_SET;

    EXPORT_SYMBOL(securebits);
    EXPORT_SYMBOL(cap_bset);
    diff --git a/security/commoncap.c b/security/commoncap.c
    index 91dc53d..291a4bd 100644
    --- a/security/commoncap.c
    +++ b/security/commoncap.c
    @@ -12,6 +12,7 @@ #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/kernel.h>
    #include <linux/security.h>
    +#include <linux/securebits.h>
    #include <linux/file.h>
    #include <linux/mm.h>
    #include <linux/mman.h>
    @@ -97,6 +98,8 @@ int cap_capset_check (struct task_struct
    if (!cap_issubset (*effective, *permitted)) {
    return -EPERM;
    }
    + /* we allow Inheritable not to be a subset of Permitted:
    + * cap_capset_set will intersect them anyway */

    return 0;
    }
    @@ -105,7 +108,7 @@ void cap_capset_set (struct task_struct
    kernel_cap_t *inheritable, kernel_cap_t *permitted)
    {
    target->cap_effective = *effective;
    - target->cap_inheritable = *inheritable;
    + target->cap_inheritable = cap_intersect (*permitted, *inheritable);
    target->cap_permitted = *permitted;
    }

    @@ -114,39 +117,66 @@ int cap_bprm_set_security (struct linux_
    /* Copied from fs/exec.c:prepare_binprm. */

    /* We don't have VFS support for capabilities yet */
    - cap_clear (bprm->cap_inheritable);
    + cap_set_full (bprm->cap_inheritable);
    cap_clear (bprm->cap_permitted);
    - cap_clear (bprm->cap_effective);
    + cap_set_full (bprm->cap_effective);
    +
    + /* Sanitize caps for all suid/sgid programs. */
    + if (!issecure (SECURE_NO_SXID_SANITIZE) && (bprm->is_suid
    + || bprm->is_sgid)) {
    + /* Ensure that they get _at least_ regular caps. */
    + bprm->cap_permitted = CAP_REGULAR_SET;
    + if ((current->uid != 0 && current->euid != 0
    + && current->suid != 0)
    + || issecure (SECURE_NOROOT)) {
    + /* Ensure that they don't get _more_ caps when they
    + might not expect it. Note that dropping
    + capabilities on change of ?uid from ==0 to !=0 will
    + be handled by cap_task_post_setuid() called from
    + cap_bprm_apply_creds() below. Yuck!!!!!! This is
    + soooooo ugly! */
    + bprm->cap_inheritable = CAP_REGULAR_SET;
    + bprm->cap_effective = CAP_REGULAR_SET;
    + }
    + }

    /* To support inheritance of root-permissions and suid-root
    * executables under compatibility mode, we raise all three
    * capability sets for the file.
    - *
    - * If only the real uid is 0, we only raise the inheritable
    - * and permitted sets of the executable file.
    */
    -
    if (!issecure (SECURE_NOROOT)) {
    - if (bprm->e_uid == 0 || current->uid == 0) {
    + if (bprm->is_suid && bprm->e_uid == 0) {
    cap_set_full (bprm->cap_inheritable);
    cap_set_full (bprm->cap_permitted);
    - }
    - if (bprm->e_uid == 0)
    cap_set_full (bprm->cap_effective);
    + }
    }
    +
    return 0;
    }

    void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
    {
    /* Derived from fs/exec.c:compute_creds. */
    - kernel_cap_t new_permitted, working;
    + kernel_cap_t new_permitted, new_effective, working;
    + uid_t old_ruid, old_euid, old_suid;

    + /* P'(per) = (P(inh) & F(inh)) | (F(per) & bset) */
    new_permitted = cap_intersect (bprm->cap_permitted, cap_bset);
    working = cap_intersect (bprm->cap_inheritable,
    current->cap_inheritable);
    new_permitted = cap_combine (new_permitted, working);

    + /* P'(eff) = (P(inh) & P(eff) & F(inh)) | (F(per) & F(eff) & bset) */
    + new_effective = cap_intersect (bprm->cap_permitted, bprm->cap_effective);
    + new_effective = cap_intersect (new_effective, cap_bset);
    + working = cap_intersect (bprm->cap_inheritable,
    + current->cap_effective);
    + working = cap_intersect (working, current->cap_inheritable);
    + new_effective = cap_combine (new_effective, working);
    +
    + /* P'(inh) = P'(per) */
    +
    if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
    !cap_issubset (new_permitted, current->cap_permitted)) {
    current->mm->dumpable = suid_dumpable;
    @@ -159,36 +189,37 @@ void cap_bprm_apply_creds (struct linux_
    if (!capable (CAP_SETPCAP)) {
    new_permitted = cap_intersect (new_permitted,
    current->cap_permitted);
    + new_effective = cap_intersect (new_permitted,
    + new_effective);
    }
    }
    }

    + old_ruid = current->uid;
    + old_euid = current->euid;
    + old_suid = current->suid;
    current->suid = current->euid = current->fsuid = bprm->e_uid;
    current->sgid = current->egid = current->fsgid = bprm->e_gid;

    - /* For init, we want to retain the capabilities set
    - * in the init_task struct. Thus we skip the usual
    - * capability rules */
    - if (current->pid != 1) {
    - current->cap_permitted = new_permitted;
    - current->cap_effective =
    - cap_intersect (new_permitted, bprm->cap_effective);
    - }
    -
    - /* AUD: Audit candidate if current->cap_effective is set */
    + current->cap_permitted = new_permitted;
    + current->cap_effective = new_effective;
    + current->cap_inheritable = new_permitted;

    current->keep_capabilities = 0;
    + /* Make sure we drop capabilities if required by suid. */
    + cap_task_post_setuid (old_ruid, old_euid, old_suid, LSM_SETID_RES);
    +
    + /* AUD: Audit candidate if current->cap_effective is set */
    }

    int cap_bprm_secureexec (struct linux_binprm *bprm)
    {
    /* If/when this module is enhanced to incorporate capability
    bits on files, the test below should be extended to also perform a
    - test between the old and new capability sets. For now,
    - it simply preserves the legacy decision algorithm used by
    - the old userland. */
    - return (current->euid != current->uid ||
    - current->egid != current->gid);
    + test between the old and new capability sets. */
    + return ((bprm->is_suid || bprm->is_sgid)
    + && !cap_issubset (bprm->cap_permitted,
    + current->cap_permitted));
    }

    int cap_inode_setxattr(struct dentry *dentry, char *name, void *value,
    @@ -253,12 +284,15 @@ static inline void cap_emulate_setxuid (
    = cap_intersect (current->cap_effective,
    CAP_REGULAR_SET);
    }
    + current->cap_inheritable
    + = cap_intersect (current->cap_inheritable,
    + CAP_REGULAR_SET);
    }
    - if (old_euid == 0 && current->euid != 0) {
    + if (old_euid == 0 && current->euid != 0 && !current->keep_capabilities) {
    current->cap_effective = cap_intersect (current->cap_effective,
    CAP_REGULAR_SET);
    }
    - if (old_euid != 0 && current->euid == 0) {
    + if (old_euid != 0 && current->euid == 0 && !current->keep_capabilities) {
    current->cap_effective = current->cap_permitted;
    }
    }
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/
    \
     
     \ /
      Last update: 2006-09-10 15:45    [W:0.040 / U:0.076 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site