diff options
author | Ingo Molnar <mingo@elte.hu> | 2008-12-31 08:31:57 +0100 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-12-31 08:31:57 +0100 |
commit | a9de18eb761f7c1c860964b2e5addc1a35c7e861 (patch) | |
tree | 886e75fdfd09690cd262ca69cb7f5d1d42b48602 /security | |
parent | b2aaf8f74cdc84a9182f6cabf198b7763bcb9d40 (diff) | |
parent | 6a94cb73064c952255336cc57731904174b2c58f (diff) |
Merge branch 'linus' into stackprotector
Conflicts:
arch/x86/include/asm/pda.h
kernel/fork.c
Diffstat (limited to 'security')
28 files changed, 2034 insertions, 1546 deletions
diff --git a/security/capability.c b/security/capability.c index 24587481903..2dce66fcb99 100644 --- a/security/capability.c +++ b/security/capability.c @@ -32,24 +32,19 @@ static int cap_quota_on(struct dentry *dentry) return 0; } -static int cap_bprm_alloc_security(struct linux_binprm *bprm) +static int cap_bprm_check_security (struct linux_binprm *bprm) { return 0; } -static void cap_bprm_free_security(struct linux_binprm *bprm) +static void cap_bprm_committing_creds(struct linux_binprm *bprm) { } -static void cap_bprm_post_apply_creds(struct linux_binprm *bprm) +static void cap_bprm_committed_creds(struct linux_binprm *bprm) { } -static int cap_bprm_check_security(struct linux_binprm *bprm) -{ - return 0; -} - static int cap_sb_alloc_security(struct super_block *sb) { return 0; @@ -64,7 +59,7 @@ static int cap_sb_copy_data(char *orig, char *copy) return 0; } -static int cap_sb_kern_mount(struct super_block *sb, void *data) +static int cap_sb_kern_mount(struct super_block *sb, int flags, void *data) { return 0; } @@ -330,7 +325,7 @@ static int cap_file_receive(struct file *file) return 0; } -static int cap_dentry_open(struct file *file) +static int cap_dentry_open(struct file *file, const struct cred *cred) { return 0; } @@ -340,15 +335,29 @@ static int cap_task_create(unsigned long clone_flags) return 0; } -static int cap_task_alloc_security(struct task_struct *p) +static void cap_cred_free(struct cred *cred) +{ +} + +static int cap_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp) { return 0; } -static void cap_task_free_security(struct task_struct *p) +static void cap_cred_commit(struct cred *new, const struct cred *old) { } +static int cap_kernel_act_as(struct cred *new, u32 secid) +{ + return 0; +} + +static int cap_kernel_create_files_as(struct cred *new, struct inode *inode) +{ + return 0; +} + static int cap_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags) { return 0; @@ -750,7 +759,7 @@ static void cap_release_secctx(char *secdata, u32 seclen) } #ifdef CONFIG_KEYS -static int cap_key_alloc(struct key *key, struct task_struct *ctx, +static int cap_key_alloc(struct key *key, const struct cred *cred, unsigned long flags) { return 0; @@ -760,7 +769,7 @@ static void cap_key_free(struct key *key) { } -static int cap_key_permission(key_ref_t key_ref, struct task_struct *context, +static int cap_key_permission(key_ref_t key_ref, const struct cred *cred, key_perm_t perm) { return 0; @@ -814,8 +823,7 @@ void security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, ptrace_may_access); set_to_cap_if_null(ops, ptrace_traceme); set_to_cap_if_null(ops, capget); - set_to_cap_if_null(ops, capset_check); - set_to_cap_if_null(ops, capset_set); + set_to_cap_if_null(ops, capset); set_to_cap_if_null(ops, acct); set_to_cap_if_null(ops, capable); set_to_cap_if_null(ops, quotactl); @@ -824,11 +832,9 @@ void security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, syslog); set_to_cap_if_null(ops, settime); set_to_cap_if_null(ops, vm_enough_memory); - set_to_cap_if_null(ops, bprm_alloc_security); - set_to_cap_if_null(ops, bprm_free_security); - set_to_cap_if_null(ops, bprm_apply_creds); - set_to_cap_if_null(ops, bprm_post_apply_creds); - set_to_cap_if_null(ops, bprm_set_security); + set_to_cap_if_null(ops, bprm_set_creds); + set_to_cap_if_null(ops, bprm_committing_creds); + set_to_cap_if_null(ops, bprm_committed_creds); set_to_cap_if_null(ops, bprm_check_security); set_to_cap_if_null(ops, bprm_secureexec); set_to_cap_if_null(ops, sb_alloc_security); @@ -890,10 +896,13 @@ void security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, file_receive); set_to_cap_if_null(ops, dentry_open); set_to_cap_if_null(ops, task_create); - set_to_cap_if_null(ops, task_alloc_security); - set_to_cap_if_null(ops, task_free_security); + set_to_cap_if_null(ops, cred_free); + set_to_cap_if_null(ops, cred_prepare); + set_to_cap_if_null(ops, cred_commit); + set_to_cap_if_null(ops, kernel_act_as); + set_to_cap_if_null(ops, kernel_create_files_as); set_to_cap_if_null(ops, task_setuid); - set_to_cap_if_null(ops, task_post_setuid); + set_to_cap_if_null(ops, task_fix_setuid); set_to_cap_if_null(ops, task_setgid); set_to_cap_if_null(ops, task_setpgid); set_to_cap_if_null(ops, task_getpgid); @@ -910,7 +919,6 @@ void security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, task_wait); set_to_cap_if_null(ops, task_kill); set_to_cap_if_null(ops, task_prctl); - set_to_cap_if_null(ops, task_reparent_to_init); set_to_cap_if_null(ops, task_to_inode); set_to_cap_if_null(ops, ipc_permission); set_to_cap_if_null(ops, ipc_getsecid); diff --git a/security/commoncap.c b/security/commoncap.c index 399bfdb9e2d..79713545cd6 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -8,6 +8,7 @@ */ #include <linux/capability.h> +#include <linux/audit.h> #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> @@ -29,7 +30,7 @@ int cap_netlink_send(struct sock *sk, struct sk_buff *skb) { - NETLINK_CB(skb).eff_cap = current->cap_effective; + NETLINK_CB(skb).eff_cap = current_cap(); return 0; } @@ -39,23 +40,41 @@ int cap_netlink_recv(struct sk_buff *skb, int cap) return -EPERM; return 0; } - EXPORT_SYMBOL(cap_netlink_recv); -/* +/** + * cap_capable - Determine whether a task has a particular effective capability + * @tsk: The task to query + * @cap: The capability to check for + * @audit: Whether to write an audit message or not + * + * Determine whether the nominated task has the specified capability amongst + * its effective set, returning 0 if it does, -ve if it does not. + * * NOTE WELL: cap_capable() cannot be used like the kernel's capable() - * function. That is, it has the reverse semantics: cap_capable() - * returns 0 when a task has a capability, but the kernel's capable() - * returns 1 for this case. + * function. That is, it has the reverse semantics: cap_capable() returns 0 + * when a task has a capability, but the kernel's capable() returns 1 for this + * case. */ -int cap_capable (struct task_struct *tsk, int cap) +int cap_capable(struct task_struct *tsk, int cap, int audit) { + __u32 cap_raised; + /* Derived from include/linux/sched.h:capable. */ - if (cap_raised(tsk->cap_effective, cap)) - return 0; - return -EPERM; + rcu_read_lock(); + cap_raised = cap_raised(__task_cred(tsk)->cap_effective, cap); + rcu_read_unlock(); + return cap_raised ? 0 : -EPERM; } +/** + * cap_settime - Determine whether the current process may set the system clock + * @ts: The time to set + * @tz: The timezone to set + * + * Determine whether the current process may set the system clock and timezone + * information, returning 0 if permission granted, -ve if denied. + */ int cap_settime(struct timespec *ts, struct timezone *tz) { if (!capable(CAP_SYS_TIME)) @@ -63,121 +82,157 @@ int cap_settime(struct timespec *ts, struct timezone *tz) return 0; } +/** + * cap_ptrace_may_access - Determine whether the current process may access + * another + * @child: The process to be accessed + * @mode: The mode of attachment. + * + * Determine whether a process may access another, returning 0 if permission + * granted, -ve if denied. + */ int cap_ptrace_may_access(struct task_struct *child, unsigned int mode) { - /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */ - if (cap_issubset(child->cap_permitted, current->cap_permitted)) - return 0; - if (capable(CAP_SYS_PTRACE)) - return 0; - return -EPERM; + int ret = 0; + + rcu_read_lock(); + if (!cap_issubset(__task_cred(child)->cap_permitted, + current_cred()->cap_permitted) && + !capable(CAP_SYS_PTRACE)) + ret = -EPERM; + rcu_read_unlock(); + return ret; } +/** + * cap_ptrace_traceme - Determine whether another process may trace the current + * @parent: The task proposed to be the tracer + * + * Determine whether the nominated task is permitted to trace the current + * process, returning 0 if permission is granted, -ve if denied. + */ int cap_ptrace_traceme(struct task_struct *parent) { - /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */ - if (cap_issubset(current->cap_permitted, parent->cap_permitted)) - return 0; - if (has_capability(parent, CAP_SYS_PTRACE)) - return 0; - return -EPERM; + int ret = 0; + + rcu_read_lock(); + if (!cap_issubset(current_cred()->cap_permitted, + __task_cred(parent)->cap_permitted) && + !has_capability(parent, CAP_SYS_PTRACE)) + ret = -EPERM; + rcu_read_unlock(); + return ret; } -int cap_capget (struct task_struct *target, kernel_cap_t *effective, - kernel_cap_t *inheritable, kernel_cap_t *permitted) +/** + * cap_capget - Retrieve a task's capability sets + * @target: The task from which to retrieve the capability sets + * @effective: The place to record the effective set + * @inheritable: The place to record the inheritable set + * @permitted: The place to record the permitted set + * + * This function retrieves the capabilities of the nominated task and returns + * them to the caller. + */ +int cap_capget(struct task_struct *target, kernel_cap_t *effective, + kernel_cap_t *inheritable, kernel_cap_t *permitted) { + const struct cred *cred; + /* Derived from kernel/capability.c:sys_capget. */ - *effective = target->cap_effective; - *inheritable = target->cap_inheritable; - *permitted = target->cap_permitted; + rcu_read_lock(); + cred = __task_cred(target); + *effective = cred->cap_effective; + *inheritable = cred->cap_inheritable; + *permitted = cred->cap_permitted; + rcu_read_unlock(); return 0; } -#ifdef CONFIG_SECURITY_FILE_CAPABILITIES - -static inline int cap_block_setpcap(struct task_struct *target) -{ - /* - * No support for remote process capability manipulation with - * filesystem capability support. - */ - return (target != current); -} - +/* + * Determine whether the inheritable capabilities are limited to the old + * permitted set. Returns 1 if they are limited, 0 if they are not. + */ static inline int cap_inh_is_capped(void) { - /* - * Return 1 if changes to the inheritable set are limited - * to the old permitted set. That is, if the current task - * does *not* possess the CAP_SETPCAP capability. - */ - return (cap_capable(current, CAP_SETPCAP) != 0); -} - -static inline int cap_limit_ptraced_target(void) { return 1; } - -#else /* ie., ndef CONFIG_SECURITY_FILE_CAPABILITIES */ +#ifdef CONFIG_SECURITY_FILE_CAPABILITIES -static inline int cap_block_setpcap(struct task_struct *t) { return 0; } -static inline int cap_inh_is_capped(void) { return 1; } -static inline int cap_limit_ptraced_target(void) -{ - return !capable(CAP_SETPCAP); + /* they are so limited unless the current task has the CAP_SETPCAP + * capability + */ + if (cap_capable(current, CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0) + return 0; +#endif + return 1; } -#endif /* def CONFIG_SECURITY_FILE_CAPABILITIES */ - -int cap_capset_check (struct task_struct *target, kernel_cap_t *effective, - kernel_cap_t *inheritable, kernel_cap_t *permitted) -{ - if (cap_block_setpcap(target)) { - return -EPERM; - } - if (cap_inh_is_capped() - && !cap_issubset(*inheritable, - cap_combine(target->cap_inheritable, - current->cap_permitted))) { +/** + * cap_capset - Validate and apply proposed changes to current's capabilities + * @new: The proposed new credentials; alterations should be made here + * @old: The current task's current credentials + * @effective: A pointer to the proposed new effective capabilities set + * @inheritable: A pointer to the proposed new inheritable capabilities set + * @permitted: A pointer to the proposed new permitted capabilities set + * + * This function validates and applies a proposed mass change to the current + * process's capability sets. The changes are made to the proposed new + * credentials, and assuming no error, will be committed by the caller of LSM. + */ +int cap_capset(struct cred *new, + const struct cred *old, + const kernel_cap_t *effective, + const kernel_cap_t *inheritable, + const kernel_cap_t *permitted) +{ + if (cap_inh_is_capped() && + !cap_issubset(*inheritable, + cap_combine(old->cap_inheritable, + old->cap_permitted))) /* incapable of using this inheritable set */ return -EPERM; - } + if (!cap_issubset(*inheritable, - cap_combine(target->cap_inheritable, - current->cap_bset))) { + cap_combine(old->cap_inheritable, + old->cap_bset))) /* no new pI capabilities outside bounding set */ return -EPERM; - } /* verify restrictions on target's new Permitted set */ - if (!cap_issubset (*permitted, - cap_combine (target->cap_permitted, - current->cap_permitted))) { + if (!cap_issubset(*permitted, old->cap_permitted)) return -EPERM; - } /* verify the _new_Effective_ is a subset of the _new_Permitted_ */ - if (!cap_issubset (*effective, *permitted)) { + if (!cap_issubset(*effective, *permitted)) return -EPERM; - } + new->cap_effective = *effective; + new->cap_inheritable = *inheritable; + new->cap_permitted = *permitted; return 0; } -void cap_capset_set (struct task_struct *target, kernel_cap_t *effective, - kernel_cap_t *inheritable, kernel_cap_t *permitted) -{ - target->cap_effective = *effective; - target->cap_inheritable = *inheritable; - target->cap_permitted = *permitted; -} - +/* + * Clear proposed capability sets for execve(). + */ static inline void bprm_clear_caps(struct linux_binprm *bprm) { - cap_clear(bprm->cap_post_exec_permitted); + cap_clear(bprm->cred->cap_permitted); bprm->cap_effective = false; } #ifdef CONFIG_SECURITY_FILE_CAPABILITIES +/** + * cap_inode_need_killpriv - Determine if inode change affects privileges + * @dentry: The inode/dentry in being changed with change marked ATTR_KILL_PRIV + * + * Determine if an inode having a change applied that's marked ATTR_KILL_PRIV + * affects the security markings on that inode, and if it is, should + * inode_killpriv() be invoked or the change rejected? + * + * Returns 0 if granted; +ve if granted, but inode_killpriv() is required; and + * -ve to deny the change. + */ int cap_inode_need_killpriv(struct dentry *dentry) { struct inode *inode = dentry->d_inode; @@ -192,6 +247,14 @@ int cap_inode_need_killpriv(struct dentry *dentry) return 1; } +/** + * cap_inode_killpriv - Erase the security markings on an inode + * @dentry: The inode/dentry to alter + * + * Erase the privilege-enhancing security markings on an inode. + * + * Returns 0 if successful, -ve on error. + */ int cap_inode_killpriv(struct dentry *dentry) { struct inode *inode = dentry->d_inode; @@ -202,19 +265,75 @@ int cap_inode_killpriv(struct dentry *dentry) 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) +/* + * Calculate the new process capability sets from the capability sets attached + * to a file. + */ +static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps, + struct linux_binprm *bprm, + bool *effective) { + struct cred *new = bprm->cred; + unsigned i; + int ret = 0; + + if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE) + *effective = true; + + CAP_FOR_EACH_U32(i) { + __u32 permitted = caps->permitted.cap[i]; + __u32 inheritable = caps->inheritable.cap[i]; + + /* + * pP' = (X & fP) | (pI & fI) + */ + new->cap_permitted.cap[i] = + (new->cap_bset.cap[i] & permitted) | + (new->cap_inheritable.cap[i] & inheritable); + + if (permitted & ~new->cap_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 *effective ? ret : 0; +} + +/* + * Extract the on-exec-apply capability sets for an executable file. + */ +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; 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)) 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: if (size != XATTR_CAPS_SZ_1) return -EINVAL; @@ -229,77 +348,48 @@ static inline int cap_from_disk(struct vfs_cap_data *caps, 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) { - __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: */ -static int get_file_caps(struct linux_binprm *bprm) +/* + * Attempt to get the on-exec apply capability sets for an executable file from + * its xattrs and, if present, apply them to the proposed credentials being + * constructed by execve(). + */ +static int get_file_caps(struct linux_binprm *bprm, bool *effective) { struct dentry *dentry; int rc = 0; - struct vfs_cap_data vcaps; - struct inode *inode; + struct cpu_vfs_cap_data vcaps; - if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) { - bprm_clear_caps(bprm); + bprm_clear_caps(bprm); + + if (!file_caps_enabled) + return 0; + + if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) return 0; - } 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; } - if (rc < 0) - goto out; - rc = cap_from_disk(&vcaps, bprm, rc); + rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective); if (rc == -EINVAL) printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", __func__, rc, bprm->filename); @@ -323,18 +413,57 @@ int cap_inode_killpriv(struct dentry *dentry) return 0; } -static inline int get_file_caps(struct linux_binprm *bprm) +int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps) +{ + memset(cpu_caps, 0, sizeof(struct cpu_vfs_cap_data)); + return -ENODATA; +} + +static inline int get_file_caps(struct linux_binprm *bprm, bool *effective) { bprm_clear_caps(bprm); return 0; } #endif -int cap_bprm_set_security (struct linux_binprm *bprm) +/* + * Determine whether a exec'ing process's new permitted capabilities should be + * limited to just what it already has. + * + * This prevents processes that are being ptraced from gaining access to + * CAP_SETPCAP, unless the process they're tracing already has it, and the + * binary they're executing has filecaps that elevate it. + * + * Returns 1 if they should be limited, 0 if they are not. + */ +static inline int cap_limit_ptraced_target(void) { +#ifndef CONFIG_SECURITY_FILE_CAPABILITIES + if (capable(CAP_SETPCAP)) + return 0; +#endif + return 1; +} + +/** + * cap_bprm_set_creds - Set up the proposed credentials for execve(). + * @bprm: The execution parameters, including the proposed creds + * + * Set up the proposed credentials for a new execution context being + * constructed by execve(). The proposed creds in @bprm->cred is altered, + * which won't take effect immediately. Returns 0 if successful, -ve on error. + */ +int cap_bprm_set_creds(struct linux_binprm *bprm) +{ + const struct cred *old = current_cred(); + struct cred *new = bprm->cred; + bool effective; int ret; - ret = get_file_caps(bprm); + effective = false; + ret = get_file_caps(bprm, &effective); + if (ret < 0) + return ret; if (!issecure(SECURE_NOROOT)) { /* @@ -342,75 +471,113 @@ int cap_bprm_set_security (struct linux_binprm *bprm) * executables under compatibility mode, we override the * capability sets for the file. * - * If only the real uid is 0, we do not set the effective - * bit. + * If only the real uid is 0, we do not set the effective bit. */ - if (bprm->e_uid == 0 || current->uid == 0) { + if (new->euid == 0 || new->uid == 0) { /* pP' = (cap_bset & ~0) | (pI & ~0) */ - bprm->cap_post_exec_permitted = cap_combine( - current->cap_bset, current->cap_inheritable - ); - bprm->cap_effective = (bprm->e_uid == 0); - ret = 0; + new->cap_permitted = cap_combine(old->cap_bset, + old->cap_inheritable); } + if (new->euid == 0) + effective = true; } - return ret; -} - -void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) -{ - if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || - !cap_issubset(bprm->cap_post_exec_permitted, - current->cap_permitted)) { - set_dumpable(current->mm, suid_dumpable); - current->pdeath_signal = 0; - - if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) { - if (!capable(CAP_SETUID)) { - bprm->e_uid = current->uid; - bprm->e_gid = current->gid; - } - if (cap_limit_ptraced_target()) { - bprm->cap_post_exec_permitted = cap_intersect( - bprm->cap_post_exec_permitted, - current->cap_permitted); - } + /* Don't let someone trace a set[ug]id/setpcap binary with the revised + * credentials unless they have the appropriate permit + */ + if ((new->euid != old->uid || + new->egid != old->gid || + !cap_issubset(new->cap_permitted, old->cap_permitted)) && + bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) { + /* downgrade; they get no more than they had, and maybe less */ + if (!capable(CAP_SETUID)) { + new->euid = new->uid; + new->egid = new->gid; } + if (cap_limit_ptraced_target()) + new->cap_permitted = cap_intersect(new->cap_permitted, + old->cap_permitted); } - current->suid = current->euid = current->fsuid = bprm->e_uid; - current->sgid = current->egid = current->fsgid = bprm->e_gid; + new->suid = new->fsuid = new->euid; + new->sgid = new->fsgid = new->egid; - /* For init, we want to retain the capabilities set - * in the init_task struct. Thus we skip the usual - * capability rules */ + /* For init, we want to retain the capabilities set in the initial + * task. Thus we skip the usual capability rules + */ if (!is_global_init(current)) { - current->cap_permitted = bprm->cap_post_exec_permitted; - if (bprm->cap_effective) - current->cap_effective = bprm->cap_post_exec_permitted; + if (effective) + new->cap_effective = new->cap_permitted; else - cap_clear(current->cap_effective); + cap_clear(new->cap_effective); } + bprm->cap_effective = effective; - /* AUD: Audit candidate if current->cap_effective is set */ + /* + * Audit candidate if current->cap_effective is set + * + * We do not bother to audit if 3 things are true: + * 1) cap_effective has all caps + * 2) we are root + * 3) root is supposed to have all caps (SECURE_NOROOT) + * Since this is just a normal root execing a process. + * + * Number 1 above might fail if you don't have a full bset, but I think + * that is interesting information to audit. + */ + if (!cap_isclear(new->cap_effective)) { + if (!cap_issubset(CAP_FULL_SET, new->cap_effective) || + new->euid != 0 || new->uid != 0 || + issecure(SECURE_NOROOT)) { + ret = audit_log_bprm_fcaps(bprm, new, old); + if (ret < 0) + return ret; + } + } - current->securebits &= ~issecure_mask(SECURE_KEEP_CAPS); + new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS); + return 0; } -int cap_bprm_secureexec (struct linux_binprm *bprm) +/** + * cap_bprm_secureexec - Determine whether a secure execution is required + * @bprm: The execution parameters + * + * Determine whether a secure execution is required, return 1 if it is, and 0 + * if it is not. + * + * The credentials have been committed by this point, and so are no longer + * available through @bprm->cred. + */ +int cap_bprm_secureexec(struct linux_binprm *bprm) { - if (current->uid != 0) { + const struct cred *cred = current_cred(); + + if (cred->uid != 0) { if (bprm->cap_effective) return 1; - if (!cap_isclear(bprm->cap_post_exec_permitted)) + if (!cap_isclear(cred->cap_permitted)) return 1; } - return (current->euid != current->uid || - current->egid != current->gid); + return (cred->euid != cred->uid || + cred->egid != cred->gid); } +/** + * cap_inode_setxattr - Determine whether an xattr may be altered + * @dentry: The inode/dentry being altered + * @name: The name of the xattr to be changed + * @value: The value that the xattr will be changed to + * @size: The size of value + * @flags: The replacement flag + * + * Determine whether an xattr may be altered or set on an inode, returning 0 if + * permission is granted, -ve if denied. + * + * This is used to make sure security xattrs don't get updated or set by those + * who aren't privileged to do so. + */ int cap_inode_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { @@ -418,28 +585,42 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name, |