capabilities: remove the task from capable LSM hook entirely

The capabilities framework is based around credentials, not necessarily the
current task.  Yet we still passed the current task down into LSMs from the
security_capable() LSM hook as if it was a meaningful portion of the security
decision.  This patch removes the 'generic' passing of current and instead
forces individual LSMs to use current explicitly if they think it is
appropriate.  In our case those LSMs are SELinux and AppArmor.

I believe the AppArmor use of current is incorrect, but that is wholely
unrelated to this patch.  This patch does not change what AppArmor does, it
just makes it clear in the AppArmor code that it is doing it.

The SELinux code still uses current in it's audit message, which may also be
wrong and needs further investigation.  Again this is NOT a change, it may
have always been wrong, this patch just makes it clear what is happening.

Signed-off-by: Eric Paris <eparis@redhat.com>
This commit is contained in:
Eric Paris 2012-01-03 12:25:14 -05:00
parent 2653812e14
commit 6a9de49115
5 changed files with 31 additions and 39 deletions

View File

@ -53,8 +53,8 @@ struct user_namespace;
* These functions are in security/capability.c and are used * These functions are in security/capability.c and are used
* as the default capabilities functions * as the default capabilities functions
*/ */
extern int cap_capable(struct task_struct *tsk, const struct cred *cred, extern int cap_capable(const struct cred *cred, struct user_namespace *ns,
struct user_namespace *ns, int cap, int audit); int cap, int audit);
extern int cap_settime(const struct timespec *ts, const struct timezone *tz); extern int cap_settime(const struct timespec *ts, const struct timezone *tz);
extern int cap_ptrace_access_check(struct task_struct *child, unsigned int mode); extern int cap_ptrace_access_check(struct task_struct *child, unsigned int mode);
extern int cap_ptrace_traceme(struct task_struct *parent); extern int cap_ptrace_traceme(struct task_struct *parent);
@ -1261,7 +1261,6 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* @capable: * @capable:
* Check whether the @tsk process has the @cap capability in the indicated * Check whether the @tsk process has the @cap capability in the indicated
* credentials. * credentials.
* @tsk contains the task_struct for the process.
* @cred contains the credentials to use. * @cred contains the credentials to use.
* @ns contains the user namespace we want the capability in * @ns contains the user namespace we want the capability in
* @cap contains the capability <include/linux/capability.h>. * @cap contains the capability <include/linux/capability.h>.
@ -1385,8 +1384,8 @@ struct security_operations {
const kernel_cap_t *effective, const kernel_cap_t *effective,
const kernel_cap_t *inheritable, const kernel_cap_t *inheritable,
const kernel_cap_t *permitted); const kernel_cap_t *permitted);
int (*capable) (struct task_struct *tsk, const struct cred *cred, int (*capable) (const struct cred *cred, struct user_namespace *ns,
struct user_namespace *ns, int cap, int audit); int cap, int audit);
int (*quotactl) (int cmds, int type, int id, struct super_block *sb); int (*quotactl) (int cmds, int type, int id, struct super_block *sb);
int (*quota_on) (struct dentry *dentry); int (*quota_on) (struct dentry *dentry);
int (*syslog) (int type); int (*syslog) (int type);
@ -1867,7 +1866,7 @@ static inline int security_capset(struct cred *new,
static inline int security_capable(struct user_namespace *ns, static inline int security_capable(struct user_namespace *ns,
const struct cred *cred, int cap) const struct cred *cred, int cap)
{ {
return cap_capable(current, cred, ns, cap, SECURITY_CAP_AUDIT); return cap_capable(cred, ns, cap, SECURITY_CAP_AUDIT);
} }
static inline int security_real_capable(struct task_struct *tsk, struct user_namespace *ns, int cap) static inline int security_real_capable(struct task_struct *tsk, struct user_namespace *ns, int cap)
@ -1875,7 +1874,7 @@ static inline int security_real_capable(struct task_struct *tsk, struct user_nam
int ret; int ret;
rcu_read_lock(); rcu_read_lock();
ret = cap_capable(tsk, __task_cred(tsk), ns, cap, SECURITY_CAP_AUDIT); ret = cap_capable(__task_cred(tsk), ns, cap, SECURITY_CAP_AUDIT);
rcu_read_unlock(); rcu_read_unlock();
return ret; return ret;
} }
@ -1886,8 +1885,7 @@ int security_real_capable_noaudit(struct task_struct *tsk, struct user_namespace
int ret; int ret;
rcu_read_lock(); rcu_read_lock();
ret = cap_capable(tsk, __task_cred(tsk), ns, cap, ret = cap_capable(__task_cred(tsk), ns, cap, SECURITY_CAP_NOAUDIT);
SECURITY_CAP_NOAUDIT);
rcu_read_unlock(); rcu_read_unlock();
return ret; return ret;
} }

View File

@ -136,16 +136,16 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
return 0; return 0;
} }
static int apparmor_capable(struct task_struct *task, const struct cred *cred, static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,
struct user_namespace *ns, int cap, int audit) int cap, int audit)
{ {
struct aa_profile *profile; struct aa_profile *profile;
/* cap_capable returns 0 on success, else -EPERM */ /* cap_capable returns 0 on success, else -EPERM */
int error = cap_capable(task, cred, ns, cap, audit); int error = cap_capable(cred, ns, cap, audit);
if (!error) { if (!error) {
profile = aa_cred_profile(cred); profile = aa_cred_profile(cred);
if (!unconfined(profile)) if (!unconfined(profile))
error = aa_capable(task, profile, cap, audit); error = aa_capable(current, profile, cap, audit);
} }
return error; return error;
} }

View File

@ -66,7 +66,6 @@ EXPORT_SYMBOL(cap_netlink_recv);
/** /**
* cap_capable - Determine whether a task has a particular effective capability * cap_capable - Determine whether a task has a particular effective capability
* @tsk: The task to query
* @cred: The credentials to use * @cred: The credentials to use
* @ns: The user namespace in which we need the capability * @ns: The user namespace in which we need the capability
* @cap: The capability to check for * @cap: The capability to check for
@ -80,8 +79,8 @@ EXPORT_SYMBOL(cap_netlink_recv);
* cap_has_capability() returns 0 when a task has a capability, but the * cap_has_capability() returns 0 when a task has a capability, but the
* kernel's capable() and has_capability() returns 1 for this case. * kernel's capable() and has_capability() returns 1 for this case.
*/ */
int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap_capable(const struct cred *cred, struct user_namespace *targ_ns,
struct user_namespace *targ_ns, int cap, int audit) int cap, int audit)
{ {
for (;;) { for (;;) {
/* The creator of the user namespace has all caps. */ /* The creator of the user namespace has all caps. */
@ -222,9 +221,8 @@ static inline int cap_inh_is_capped(void)
/* they are so limited unless the current task has the CAP_SETPCAP /* they are so limited unless the current task has the CAP_SETPCAP
* capability * capability
*/ */
if (cap_capable(current, current_cred(), if (cap_capable(current_cred(), current_cred()->user->user_ns,
current_cred()->user->user_ns, CAP_SETPCAP, CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0)
SECURITY_CAP_AUDIT) == 0)
return 0; return 0;
return 1; return 1;
} }
@ -870,7 +868,7 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
& (new->securebits ^ arg2)) /*[1]*/ & (new->securebits ^ arg2)) /*[1]*/
|| ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/ || ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/
|| (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/ || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/
|| (cap_capable(current, current_cred(), || (cap_capable(current_cred(),
current_cred()->user->user_ns, CAP_SETPCAP, current_cred()->user->user_ns, CAP_SETPCAP,
SECURITY_CAP_AUDIT) != 0) /*[4]*/ SECURITY_CAP_AUDIT) != 0) /*[4]*/
/* /*
@ -936,7 +934,7 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages)
{ {
int cap_sys_admin = 0; int cap_sys_admin = 0;
if (cap_capable(current, current_cred(), &init_user_ns, CAP_SYS_ADMIN, if (cap_capable(current_cred(), &init_user_ns, CAP_SYS_ADMIN,
SECURITY_CAP_NOAUDIT) == 0) SECURITY_CAP_NOAUDIT) == 0)
cap_sys_admin = 1; cap_sys_admin = 1;
return __vm_enough_memory(mm, pages, cap_sys_admin); return __vm_enough_memory(mm, pages, cap_sys_admin);
@ -963,7 +961,7 @@ int cap_file_mmap(struct file *file, unsigned long reqprot,
int ret = 0; int ret = 0;
if (addr < dac_mmap_min_addr) { if (addr < dac_mmap_min_addr) {
ret = cap_capable(current, current_cred(), &init_user_ns, CAP_SYS_RAWIO, ret = cap_capable(current_cred(), &init_user_ns, CAP_SYS_RAWIO,
SECURITY_CAP_AUDIT); SECURITY_CAP_AUDIT);
/* set PF_SUPERPRIV if it turns out we allow the low mmap */ /* set PF_SUPERPRIV if it turns out we allow the low mmap */
if (ret == 0) if (ret == 0)

View File

@ -157,8 +157,7 @@ int security_capset(struct cred *new, const struct cred *old,
int security_capable(struct user_namespace *ns, const struct cred *cred, int security_capable(struct user_namespace *ns, const struct cred *cred,
int cap) int cap)
{ {
return security_ops->capable(current, cred, ns, cap, return security_ops->capable(cred, ns, cap, SECURITY_CAP_AUDIT);
SECURITY_CAP_AUDIT);
} }
int security_real_capable(struct task_struct *tsk, struct user_namespace *ns, int security_real_capable(struct task_struct *tsk, struct user_namespace *ns,
@ -168,7 +167,7 @@ int security_real_capable(struct task_struct *tsk, struct user_namespace *ns,
int ret; int ret;
cred = get_task_cred(tsk); cred = get_task_cred(tsk);
ret = security_ops->capable(tsk, cred, ns, cap, SECURITY_CAP_AUDIT); ret = security_ops->capable(cred, ns, cap, SECURITY_CAP_AUDIT);
put_cred(cred); put_cred(cred);
return ret; return ret;
} }
@ -180,7 +179,7 @@ int security_real_capable_noaudit(struct task_struct *tsk,
int ret; int ret;
cred = get_task_cred(tsk); cred = get_task_cred(tsk);
ret = security_ops->capable(tsk, cred, ns, cap, SECURITY_CAP_NOAUDIT); ret = security_ops->capable(cred, ns, cap, SECURITY_CAP_NOAUDIT);
put_cred(cred); put_cred(cred);
return ret; return ret;
} }

View File

@ -1414,8 +1414,7 @@ static int current_has_perm(const struct task_struct *tsk,
#endif #endif
/* Check whether a task is allowed to use a capability. */ /* Check whether a task is allowed to use a capability. */
static int task_has_capability(struct task_struct *tsk, static int cred_has_capability(const struct cred *cred,
const struct cred *cred,
int cap, int audit) int cap, int audit)
{ {
struct common_audit_data ad; struct common_audit_data ad;
@ -1426,7 +1425,7 @@ static int task_has_capability(struct task_struct *tsk,
int rc; int rc;
COMMON_AUDIT_DATA_INIT(&ad, CAP); COMMON_AUDIT_DATA_INIT(&ad, CAP);
ad.tsk = tsk; ad.tsk = current;
ad.u.cap = cap; ad.u.cap = cap;
switch (CAP_TO_INDEX(cap)) { switch (CAP_TO_INDEX(cap)) {
@ -1867,16 +1866,16 @@ static int selinux_capset(struct cred *new, const struct cred *old,
* the CAP_SETUID and CAP_SETGID capabilities using the capable hook. * the CAP_SETUID and CAP_SETGID capabilities using the capable hook.
*/ */
static int selinux_capable(struct task_struct *tsk, const struct cred *cred, static int selinux_capable(const struct cred *cred, struct user_namespace *ns,
struct user_namespace *ns, int cap, int audit) int cap, int audit)
{ {
int rc; int rc;
rc = cap_capable(tsk, cred, ns, cap, audit); rc = cap_capable(cred, ns, cap, audit);
if (rc) if (rc)
return rc; return rc;
return task_has_capability(tsk, cred, cap, audit); return cred_has_capability(cred, cap, audit);
} }
static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb) static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
@ -1953,8 +1952,7 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
{ {
int rc, cap_sys_admin = 0; int rc, cap_sys_admin = 0;
rc = selinux_capable(current, current_cred(), rc = selinux_capable(current_cred(), &init_user_ns, CAP_SYS_ADMIN,
&init_user_ns, CAP_SYS_ADMIN,
SECURITY_CAP_NOAUDIT); SECURITY_CAP_NOAUDIT);
if (rc == 0) if (rc == 0)
cap_sys_admin = 1; cap_sys_admin = 1;
@ -2858,8 +2856,7 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name
* and lack of permission just means that we fall back to the * and lack of permission just means that we fall back to the
* in-core context value, not a denial. * in-core context value, not a denial.
*/ */
error = selinux_capable(current, current_cred(), error = selinux_capable(current_cred(), &init_user_ns, CAP_MAC_ADMIN,
&init_user_ns, CAP_MAC_ADMIN,
SECURITY_CAP_NOAUDIT); SECURITY_CAP_NOAUDIT);
if (!error) if (!error)
error = security_sid_to_context_force(isec->sid, &context, error = security_sid_to_context_force(isec->sid, &context,
@ -2992,8 +2989,8 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
case KDSKBENT: case KDSKBENT:
case KDSKBSENT: case KDSKBSENT:
error = task_has_capability(current, cred, CAP_SYS_TTY_CONFIG, error = cred_has_capability(cred, CAP_SYS_TTY_CONFIG,
SECURITY_CAP_AUDIT); SECURITY_CAP_AUDIT);
break; break;
/* default case assumes that the command will go /* default case assumes that the command will go