summaryrefslogtreecommitdiffstats
path: root/security/selinux/hooks.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r--security/selinux/hooks.c283
1 files changed, 118 insertions, 165 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index c71bba78872f..21a592184633 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2029,59 +2029,45 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
/* binprm security operations */
-static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
+static int selinux_bprm_set_creds(struct linux_binprm *bprm)
{
- struct bprm_security_struct *bsec;
-
- bsec = kzalloc(sizeof(struct bprm_security_struct), GFP_KERNEL);
- if (!bsec)
- return -ENOMEM;
-
- bsec->sid = SECINITSID_UNLABELED;
- bsec->set = 0;
-
- bprm->security = bsec;
- return 0;
-}
-
-static int selinux_bprm_set_security(struct linux_binprm *bprm)
-{
- struct task_security_struct *tsec;
- struct inode *inode = bprm->file->f_path.dentry->d_inode;
+ const struct task_security_struct *old_tsec;
+ struct task_security_struct *new_tsec;
struct inode_security_struct *isec;
- struct bprm_security_struct *bsec;
- u32 newsid;
struct avc_audit_data ad;
+ struct inode *inode = bprm->file->f_path.dentry->d_inode;
int rc;
- rc = secondary_ops->bprm_set_security(bprm);
+ rc = secondary_ops->bprm_set_creds(bprm);
if (rc)
return rc;
- bsec = bprm->security;
-
- if (bsec->set)
+ /* SELinux context only depends on initial program or script and not
+ * the script interpreter */
+ if (bprm->cred_prepared)
return 0;
- tsec = current_security();
+ old_tsec = current_security();
+ new_tsec = bprm->cred->security;
isec = inode->i_security;
/* Default to the current task SID. */
- bsec->sid = tsec->sid;
+ new_tsec->sid = old_tsec->sid;
+ new_tsec->osid = old_tsec->sid;
/* Reset fs, key, and sock SIDs on execve. */
- tsec->create_sid = 0;
- tsec->keycreate_sid = 0;
- tsec->sockcreate_sid = 0;
+ new_tsec->create_sid = 0;
+ new_tsec->keycreate_sid = 0;
+ new_tsec->sockcreate_sid = 0;
- if (tsec->exec_sid) {
- newsid = tsec->exec_sid;
+ if (old_tsec->exec_sid) {
+ new_tsec->sid = old_tsec->exec_sid;
/* Reset exec SID on execve. */
- tsec->exec_sid = 0;
+ new_tsec->exec_sid = 0;
} else {
/* Check for a default transition on this program. */
- rc = security_transition_sid(tsec->sid, isec->sid,
- SECCLASS_PROCESS, &newsid);
+ rc = security_transition_sid(old_tsec->sid, isec->sid,
+ SECCLASS_PROCESS, &new_tsec->sid);
if (rc)
return rc;
}
@@ -2090,33 +2076,63 @@ static int selinux_bprm_set_security(struct linux_binprm *bprm)
ad.u.fs.path = bprm->file->f_path;
if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
- newsid = tsec->sid;
+ new_tsec->sid = old_tsec->sid;
- if (tsec->sid == newsid) {
- rc = avc_has_perm(tsec->sid, isec->sid,
+ if (new_tsec->sid == old_tsec->sid) {
+ rc = avc_has_perm(old_tsec->sid, isec->sid,
SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
if (rc)
return rc;
} else {
/* Check permissions for the transition. */
- rc = avc_has_perm(tsec->sid, newsid,
+ rc = avc_has_perm(old_tsec->sid, new_tsec->sid,
SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
if (rc)
return rc;
- rc = avc_has_perm(newsid, isec->sid,
+ rc = avc_has_perm(new_tsec->sid, isec->sid,
SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
if (rc)
return rc;
- /* Clear any possibly unsafe personality bits on exec: */
- current->personality &= ~PER_CLEAR_ON_SETID;
+ /* Check for shared state */
+ if (bprm->unsafe & LSM_UNSAFE_SHARE) {
+ rc = avc_has_perm(old_tsec->sid, new_tsec->sid,
+ SECCLASS_PROCESS, PROCESS__SHARE,
+ NULL);
+ if (rc)
+ return -EPERM;
+ }
+
+ /* Make sure that anyone attempting to ptrace over a task that
+ * changes its SID has the appropriate permit */
+ if (bprm->unsafe &
+ (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
+ struct task_struct *tracer;
+ struct task_security_struct *sec;
+ u32 ptsid = 0;
+
+ rcu_read_lock();
+ tracer = tracehook_tracer_task(current);
+ if (likely(tracer != NULL)) {
+ sec = __task_cred(tracer)->security;
+ ptsid = sec->sid;
+ }
+ rcu_read_unlock();
+
+ if (ptsid != 0) {
+ rc = avc_has_perm(ptsid, new_tsec->sid,
+ SECCLASS_PROCESS,
+ PROCESS__PTRACE, NULL);
+ if (rc)
+ return -EPERM;
+ }
+ }
- /* Set the security field to the new SID. */
- bsec->sid = newsid;
+ /* Clear any possibly unsafe personality bits on exec: */
+ bprm->per_clear |= PER_CLEAR_ON_SETID;
}
- bsec->set = 1;
return 0;
}
@@ -2125,7 +2141,6 @@ static int selinux_bprm_check_security(struct linux_binprm *bprm)
return secondary_ops->bprm_check_security(bprm);
}
-
static int selinux_bprm_secureexec(struct linux_binprm *bprm)
{
const struct cred *cred = current_cred();
@@ -2141,19 +2156,13 @@ static int selinux_bprm_secureexec(struct linux_binprm *bprm)
the noatsecure permission is granted between
the two SIDs, i.e. ahp returns 0. */
atsecure = avc_has_perm(osid, sid,
- SECCLASS_PROCESS,
- PROCESS__NOATSECURE, NULL);
+ SECCLASS_PROCESS,
+ PROCESS__NOATSECURE, NULL);
}
return (atsecure || secondary_ops->bprm_secureexec(bprm));
}
-static void selinux_bprm_free_security(struct linux_binprm *bprm)
-{
- kfree(bprm->security);
- bprm->security = NULL;
-}
-
extern struct vfsmount *selinuxfs_mount;
extern struct dentry *selinux_null;
@@ -2252,108 +2261,78 @@ static inline void flush_unauthorized_files(const struct cred *cred,
spin_unlock(&files->file_lock);
}
-static int selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
+/*
+ * Prepare a process for imminent new credential changes due to exec
+ */
+static void selinux_bprm_committing_creds(struct linux_binprm *bprm)
{
- struct task_security_struct *tsec;
- struct bprm_security_struct *bsec;
- struct cred *new;
- u32 sid;
- int rc;
-
- rc = secondary_ops->bprm_apply_creds(bprm, unsafe);
- if (rc < 0)
- return rc;
-
- new = prepare_creds();
- if (!new)
- return -ENOMEM;
+ struct task_security_struct *new_tsec;
+ struct rlimit *rlim, *initrlim;
+ int rc, i;
- tsec = new->security;
+ secondary_ops->bprm_committing_creds(bprm);
- bsec = bprm->security;
- sid = bsec->sid;
-
- tsec->osid = tsec->sid;
- bsec->unsafe = 0;
- if (tsec->sid != sid) {
- /* Check for shared state. If not ok, leave SID
- unchanged and kill. */
- if (unsafe & LSM_UNSAFE_SHARE) {
- rc = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
- PROCESS__SHARE, NULL);
- if (rc) {
- bsec->unsafe = 1;
- goto out;
- }
- }
+ new_tsec = bprm->cred->security;
+ if (new_tsec->sid == new_tsec->osid)
+ return;
- /* Check for ptracing, and update the task SID if ok.
- Otherwise, leave SID unchanged and kill. */
- if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
- struct task_struct *tracer;
- struct task_security_struct *sec;
- u32 ptsid = 0;
+ /* Close files for which the new task SID is not authorized. */
+ flush_unauthorized_files(bprm->cred, current->files);
- rcu_read_lock();
- tracer = tracehook_tracer_task(current);
- if (likely(tracer != NULL)) {
- sec = __task_cred(tracer)->security;
- ptsid = sec->sid;
- }
- rcu_read_unlock();
+ /* Always clear parent death signal on SID transitions. */
+ current->pdeath_signal = 0;
- if (ptsid != 0) {
- rc = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
- PROCESS__PTRACE, NULL);
- if (rc) {
- bsec->unsafe = 1;
- goto out;
- }
- }
+ /* Check whether the new SID can inherit resource limits from the old
+ * SID. If not, reset all soft limits to the lower of the current
+ * task's hard limit and the init task's soft limit.
+ *
+ * Note that the setting of hard limits (even to lower them) can be
+ * controlled by the setrlimit check. The inclusion of the init task's
+ * soft limit into the computation is to avoid resetting soft limits
+ * higher than the default soft limit for cases where the default is
+ * lower than the hard limit, e.g. RLIMIT_CORE or RLIMIT_STACK.
+ */
+ rc = avc_has_perm(new_tsec->osid, new_tsec->sid, SECCLASS_PROCESS,
+ PROCESS__RLIMITINH, NULL);
+ if (rc) {
+ for (i = 0; i < RLIM_NLIMITS; i++) {
+ rlim = current->signal->rlim + i;
+ initrlim = init_task.signal->rlim + i;
+ rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur);
}
- tsec->sid = sid;
+ update_rlimit_cpu(rlim->rlim_cur);
}
-
-out:
- commit_creds(new);
- return 0;
}
/*
- * called after apply_creds without the task lock held
+ * Clean up the process immediately after the installation of new credentials
+ * due to exec
*/
-static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
+static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
{
- const struct cred *cred = current_cred();
- struct task_security_struct *tsec;
- struct rlimit *rlim, *initrlim;
+ const struct task_security_struct *tsec = current_security();
struct itimerval itimer;
- struct bprm_security_struct *bsec;
struct sighand_struct *psig;
+ u32 osid, sid;
int rc, i;
unsigned long flags;
- tsec = current_security();
- bsec = bprm->security;
+ secondary_ops->bprm_committed_creds(bprm);
- if (bsec->unsafe) {
- force_sig_specific(SIGKILL, current);
- return;
- }
- if (tsec->osid == tsec->sid)
+ osid = tsec->osid;
+ sid = tsec->sid;
+
+ if (sid == osid)
return;
- /* Close files for which the new task SID is not authorized. */
- flush_unauthorized_files(cred, current->files);
-
- /* Check whether the new SID can inherit signal state
- from the old SID. If not, clear itimers to avoid
- subsequent signal generation and flush and unblock
- signals. This must occur _after_ the task SID has
- been updated so that any kill done after the flush
- will be checked against the new SID. */
- rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
- PROCESS__SIGINH, NULL);
+ /* Check whether the new SID can inherit signal state from the old SID.
+ * If not, clear itimers to avoid subsequent signal generation and
+ * flush and unblock signals.
+ *
+ * This must occur _after_ the task SID has been updated so that any
+ * kill done after the flush will be checked against the new SID.
+ */
+ rc = avc_has_perm(osid, sid, SECCLASS_PROCESS, PROCESS__SIGINH, NULL);
if (rc) {
memset(&itimer, 0, sizeof itimer);
for (i = 0; i < 3; i++)
@@ -2366,32 +2345,8 @@ static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
spin_unlock_irq(&current->sighand->siglock);
}
- /* Always clear parent death signal on SID transitions. */
- current->pdeath_signal = 0;
-
- /* Check whether the new SID can inherit resource limits
- from the old SID. If not, reset all soft limits to
- the lower of the current task's hard limit and the init
- task's soft limit. Note that the setting of hard limits
- (even to lower them) can be controlled by the setrlimit
- check. The inclusion of the init task's soft limit into
- the computation is to avoid resetting soft limits higher
- than the default soft limit for cases where the default
- is lower than the hard limit, e.g. RLIMIT_CORE or
- RLIMIT_STACK.*/
- rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
- PROCESS__RLIMITINH, NULL);
- if (rc) {
- for (i = 0; i < RLIM_NLIMITS; i++) {
- rlim = current->signal->rlim + i;
- initrlim = init_task.signal->rlim+i;
- rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur);
- }
- update_rlimit_cpu(rlim->rlim_cur);
- }
-
- /* Wake up the parent if it is waiting so that it can
- recheck wait permission to the new task SID. */
+ /* Wake up the parent if it is waiting so that it can recheck
+ * wait permission to the new task SID. */
read_lock_irq(&tasklist_lock);
psig = current->parent->sighand;
spin_lock_irqsave(&psig->siglock, flags);
@@ -5556,12 +5511,10 @@ static struct security_operations selinux_ops = {
.netlink_send = selinux_netlink_send,
.netlink_recv = selinux_netlink_recv,
- .bprm_alloc_security = selinux_bprm_alloc_security,
- .bprm_free_security = selinux_bprm_free_security,
- .bprm_apply_creds = selinux_bprm_apply_creds,
- .bprm_post_apply_creds = selinux_bprm_post_apply_creds,
- .bprm_set_security = selinux_bprm_set_security,
+ .bprm_set_creds = selinux_bprm_set_creds,
.bprm_check_security = selinux_bprm_check_security,
+ .bprm_committing_creds = selinux_bprm_committing_creds,
+ .bprm_committed_creds = selinux_bprm_committed_creds,
.bprm_secureexec = selinux_bprm_secureexec,
.sb_alloc_security = selinux_sb_alloc_security,