Commit 7f01bafb authored by Serge Hallyn's avatar Serge Hallyn Committed by Linus Torvalds

[PATCH] split bprm_apply_creds into two functions

The following patch splits bprm_apply_creds into two functions,
bprm_apply_creds and bprm_post_apply_creds.  The latter is called after the
task_lock has been dropped.  Without this patch, SELinux must drop the
task_lock and re-acquire it during apply_creds, making the 'unsafe' flag
meaningless to any later security modules.  Please apply.
Signed-off-by: default avatarSerge Hallyn <serue@us.ibm.com>
Signed-off-by: default avatarStephen Smalley <sds@epoch.ncsc.mil>
Signed-off-by: default avatarChris Wright <chrisw@osdl.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent a8ec257c
...@@ -963,6 +963,7 @@ void compute_creds(struct linux_binprm *bprm) ...@@ -963,6 +963,7 @@ void compute_creds(struct linux_binprm *bprm)
unsafe = unsafe_exec(current); unsafe = unsafe_exec(current);
security_bprm_apply_creds(bprm, unsafe); security_bprm_apply_creds(bprm, unsafe);
task_unlock(current); task_unlock(current);
security_bprm_post_apply_creds(bprm);
} }
EXPORT_SYMBOL(compute_creds); EXPORT_SYMBOL(compute_creds);
......
...@@ -109,13 +109,20 @@ struct swap_info_struct; ...@@ -109,13 +109,20 @@ struct swap_info_struct;
* and the information saved in @bprm->security by the set_security hook. * and the information saved in @bprm->security by the set_security hook.
* Since this hook function (and its caller) are void, this hook can not * Since this hook function (and its caller) are void, this hook can not
* return an error. However, it can leave the security attributes of the * return an error. However, it can leave the security attributes of the
* process unchanged if an access failure occurs at this point. It can * process unchanged if an access failure occurs at this point.
* also perform other state changes on the process (e.g. closing open
* file descriptors to which access is no longer granted if the attributes
* were changed).
* bprm_apply_creds is called under task_lock. @unsafe indicates various * bprm_apply_creds is called under task_lock. @unsafe indicates various
* reasons why it may be unsafe to change security state. * reasons why it may be unsafe to change security state.
* @bprm contains the linux_binprm structure. * @bprm contains the linux_binprm structure.
* @bprm_post_apply_creds:
* Runs after bprm_apply_creds with the task_lock dropped, so that
* functions which cannot be called safely under the task_lock can
* be used. This hook is a good place to perform state changes on
* the process such as closing open file descriptors to which access
* is no longer granted if the attributes were changed.
* Note that a security module might need to save state between
* bprm_apply_creds and bprm_post_apply_creds to store the decision
* on whether the process may proceed.
* @bprm contains the linux_binprm structure.
* @bprm_set_security: * @bprm_set_security:
* Save security information in the bprm->security field, typically based * Save security information in the bprm->security field, typically based
* on information about the bprm->file, for later use by the apply_creds * on information about the bprm->file, for later use by the apply_creds
...@@ -1042,6 +1049,7 @@ struct security_operations { ...@@ -1042,6 +1049,7 @@ struct security_operations {
int (*bprm_alloc_security) (struct linux_binprm * bprm); int (*bprm_alloc_security) (struct linux_binprm * bprm);
void (*bprm_free_security) (struct linux_binprm * bprm); void (*bprm_free_security) (struct linux_binprm * bprm);
void (*bprm_apply_creds) (struct linux_binprm * bprm, int unsafe); void (*bprm_apply_creds) (struct linux_binprm * bprm, int unsafe);
void (*bprm_post_apply_creds) (struct linux_binprm * bprm);
int (*bprm_set_security) (struct linux_binprm * bprm); int (*bprm_set_security) (struct linux_binprm * bprm);
int (*bprm_check_security) (struct linux_binprm * bprm); int (*bprm_check_security) (struct linux_binprm * bprm);
int (*bprm_secureexec) (struct linux_binprm * bprm); int (*bprm_secureexec) (struct linux_binprm * bprm);
...@@ -1314,6 +1322,10 @@ static inline void security_bprm_apply_creds (struct linux_binprm *bprm, int uns ...@@ -1314,6 +1322,10 @@ static inline void security_bprm_apply_creds (struct linux_binprm *bprm, int uns
{ {
security_ops->bprm_apply_creds (bprm, unsafe); security_ops->bprm_apply_creds (bprm, unsafe);
} }
static inline void security_bprm_post_apply_creds (struct linux_binprm *bprm)
{
security_ops->bprm_post_apply_creds (bprm);
}
static inline int security_bprm_set (struct linux_binprm *bprm) static inline int security_bprm_set (struct linux_binprm *bprm)
{ {
return security_ops->bprm_set_security (bprm); return security_ops->bprm_set_security (bprm);
...@@ -1992,6 +2004,11 @@ static inline void security_bprm_apply_creds (struct linux_binprm *bprm, int uns ...@@ -1992,6 +2004,11 @@ static inline void security_bprm_apply_creds (struct linux_binprm *bprm, int uns
cap_bprm_apply_creds (bprm, unsafe); cap_bprm_apply_creds (bprm, unsafe);
} }
static inline void security_bprm_post_apply_creds (struct linux_binprm *bprm)
{
return;
}
static inline int security_bprm_set (struct linux_binprm *bprm) static inline int security_bprm_set (struct linux_binprm *bprm)
{ {
return cap_bprm_set_security (bprm); return cap_bprm_set_security (bprm);
......
...@@ -200,6 +200,11 @@ static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) ...@@ -200,6 +200,11 @@ static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
dummy_capget(current, &current->cap_effective, &current->cap_inheritable, &current->cap_permitted); dummy_capget(current, &current->cap_effective, &current->cap_inheritable, &current->cap_permitted);
} }
static void dummy_bprm_post_apply_creds (struct linux_binprm *bprm)
{
return;
}
static int dummy_bprm_set_security (struct linux_binprm *bprm) static int dummy_bprm_set_security (struct linux_binprm *bprm)
{ {
return 0; return 0;
...@@ -916,6 +921,7 @@ void security_fixup_ops (struct security_operations *ops) ...@@ -916,6 +921,7 @@ void security_fixup_ops (struct security_operations *ops)
set_to_dummy_if_null(ops, bprm_alloc_security); set_to_dummy_if_null(ops, bprm_alloc_security);
set_to_dummy_if_null(ops, bprm_free_security); set_to_dummy_if_null(ops, bprm_free_security);
set_to_dummy_if_null(ops, bprm_apply_creds); set_to_dummy_if_null(ops, bprm_apply_creds);
set_to_dummy_if_null(ops, bprm_post_apply_creds);
set_to_dummy_if_null(ops, bprm_set_security); set_to_dummy_if_null(ops, bprm_set_security);
set_to_dummy_if_null(ops, bprm_check_security); set_to_dummy_if_null(ops, bprm_check_security);
set_to_dummy_if_null(ops, bprm_secureexec); set_to_dummy_if_null(ops, bprm_secureexec);
......
...@@ -1795,10 +1795,7 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) ...@@ -1795,10 +1795,7 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
struct task_security_struct *tsec; struct task_security_struct *tsec;
struct bprm_security_struct *bsec; struct bprm_security_struct *bsec;
u32 sid; u32 sid;
struct av_decision avd; int rc;
struct itimerval itimer;
struct rlimit *rlim, *initrlim;
int rc, i;
secondary_ops->bprm_apply_creds(bprm, unsafe); secondary_ops->bprm_apply_creds(bprm, unsafe);
...@@ -1808,39 +1805,54 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) ...@@ -1808,39 +1805,54 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
sid = bsec->sid; sid = bsec->sid;
tsec->osid = tsec->sid; tsec->osid = tsec->sid;
bsec->unsafe = 0;
if (tsec->sid != sid) { if (tsec->sid != sid) {
/* Check for shared state. If not ok, leave SID /* Check for shared state. If not ok, leave SID
unchanged and kill. */ unchanged and kill. */
if (unsafe & LSM_UNSAFE_SHARE) { if (unsafe & LSM_UNSAFE_SHARE) {
rc = avc_has_perm_noaudit(tsec->sid, sid, rc = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
SECCLASS_PROCESS, PROCESS__SHARE, &avd); PROCESS__SHARE, NULL);
if (rc) { if (rc) {
task_unlock(current); bsec->unsafe = 1;
avc_audit(tsec->sid, sid, SECCLASS_PROCESS, return;
PROCESS__SHARE, &avd, rc, NULL);
force_sig_specific(SIGKILL, current);
goto lock_out;
} }
} }
/* Check for ptracing, and update the task SID if ok. /* Check for ptracing, and update the task SID if ok.
Otherwise, leave SID unchanged and kill. */ Otherwise, leave SID unchanged and kill. */
if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
rc = avc_has_perm_noaudit(tsec->ptrace_sid, sid, rc = avc_has_perm(tsec->ptrace_sid, sid,
SECCLASS_PROCESS, PROCESS__PTRACE, &avd); SECCLASS_PROCESS, PROCESS__PTRACE,
if (!rc) NULL);
tsec->sid = sid;
task_unlock(current);
avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
PROCESS__PTRACE, &avd, rc, NULL);
if (rc) { if (rc) {
force_sig_specific(SIGKILL, current); bsec->unsafe = 1;
goto lock_out; return;
}
} }
} else {
tsec->sid = sid; tsec->sid = sid;
task_unlock(current);
} }
}
/*
* called after apply_creds without the task lock held
*/
static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
{
struct task_security_struct *tsec;
struct rlimit *rlim, *initrlim;
struct itimerval itimer;
struct bprm_security_struct *bsec;
int rc, i;
tsec = current->security;
bsec = bprm->security;
if (bsec->unsafe) {
force_sig_specific(SIGKILL, current);
return;
}
if (tsec->osid == tsec->sid)
return;
/* Close files for which the new task SID is not authorized. */ /* Close files for which the new task SID is not authorized. */
flush_unauthorized_files(current->files); flush_unauthorized_files(current->files);
...@@ -1888,11 +1900,6 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) ...@@ -1888,11 +1900,6 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
/* Wake up the parent if it is waiting so that it can /* Wake up the parent if it is waiting so that it can
recheck wait permission to the new task SID. */ recheck wait permission to the new task SID. */
wake_up_interruptible(&current->parent->signal->wait_chldexit); wake_up_interruptible(&current->parent->signal->wait_chldexit);
lock_out:
task_lock(current);
return;
}
} }
/* superblock security operations */ /* superblock security operations */
...@@ -4212,6 +4219,7 @@ struct security_operations selinux_ops = { ...@@ -4212,6 +4219,7 @@ struct security_operations selinux_ops = {
.bprm_alloc_security = selinux_bprm_alloc_security, .bprm_alloc_security = selinux_bprm_alloc_security,
.bprm_free_security = selinux_bprm_free_security, .bprm_free_security = selinux_bprm_free_security,
.bprm_apply_creds = selinux_bprm_apply_creds, .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_security = selinux_bprm_set_security,
.bprm_check_security = selinux_bprm_check_security, .bprm_check_security = selinux_bprm_check_security,
.bprm_secureexec = selinux_bprm_secureexec, .bprm_secureexec = selinux_bprm_secureexec,
......
...@@ -87,6 +87,12 @@ struct bprm_security_struct { ...@@ -87,6 +87,12 @@ struct bprm_security_struct {
struct linux_binprm *bprm; /* back pointer to bprm object */ struct linux_binprm *bprm; /* back pointer to bprm object */
u32 sid; /* SID for transformed process */ u32 sid; /* SID for transformed process */
unsigned char set; unsigned char set;
/*
* unsafe is used to share failure information from bprm_apply_creds()
* to bprm_post_apply_creds().
*/
char unsafe;
}; };
struct netif_security_struct { struct netif_security_struct {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment