| From: |
| Serge Hallyn <serue@us.ibm.com> |
| To: |
| LSM Mailing List <linux-security-module@wirex.com> |
| Subject: |
| [PATCH] new bprm_final_setup patch |
| Date: |
| Fri, 10 Dec 2004 12:21:20 -0600 |
| Cc: |
| Chris Wright <chrisw@osdl.org>,
Stephen Smalley <sds@epoch.ncsc.mil> |
| Archive-link: |
| Article,
Thread
|
A new patch taking in Stephen's comments is attached. This patch
defines the bprm_final_setup LSM hook, which is called after apply_creds
but with task_lock dropped.
This patch is also on a new sourceforge project
(sf.net/projects/lsm-stacker) where I'm going to start keeping patches,
so that when I don't have to keep sending out 5 patches at a time when I
send out stacker.
Again, I also attach the relevant parts of selinux/hooks.c.
thanks,
-serge
--
Serge Hallyn <serue@us.ibm.com>
Index: linux-2.6.10-stack/fs/exec.c
===================================================================
--- linux-2.6.10-stack.orig/fs/exec.c 2004-12-07 19:35:47.000000000 -0600
+++ linux-2.6.10-stack/fs/exec.c 2004-12-09 13:00:57.000000000 -0600
@@ -953,6 +953,7 @@
unsafe = unsafe_exec(current);
security_bprm_apply_creds(bprm, unsafe);
task_unlock(current);
+ security_bprm_final_setup(bprm);
}
EXPORT_SYMBOL(compute_creds);
Index: linux-2.6.10-stack/include/linux/security.h
===================================================================
--- linux-2.6.10-stack.orig/include/linux/security.h 2004-12-08 18:45:30.000000000
-0600
+++ linux-2.6.10-stack/include/linux/security.h 2004-12-10 10:17:41.000000000
-0600
@@ -115,6 +115,11 @@
* bprm_apply_creds is called under task_lock. @unsafe indicates various
* reasons why it may be unsafe to change security state.
* @bprm contains the linux_binprm structure.
+ * @bprm_final_setup:
+ * Runs after bprm_apply_creds with the task_lock dropped, so that
+ * functions which cannot be called safely under the task_list can
+ * be used.
+ * @bprm contains the linux_binprm structure.
* @bprm_set_security:
* Save security information in the bprm->security field, typically based
* on information about the bprm->file, for later use by the apply_creds
@@ -1041,6 +1046,7 @@
int (*bprm_alloc_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_final_setup) (struct linux_binprm * bprm);
int (*bprm_set_security) (struct linux_binprm * bprm);
int (*bprm_check_security) (struct linux_binprm * bprm);
int (*bprm_secureexec) (struct linux_binprm * bprm);
@@ -1313,6 +1319,10 @@
{
security_ops->bprm_apply_creds (bprm, unsafe);
}
+static inline void security_bprm_final_setup (struct linux_binprm *bprm)
+{
+ security_ops->bprm_final_setup (bprm);
+}
static inline int security_bprm_set (struct linux_binprm *bprm)
{
return security_ops->bprm_set_security (bprm);
@@ -1990,6 +2000,11 @@
cap_bprm_apply_creds (bprm, unsafe);
}
+static inline void security_bprm_final_setup (struct linux_binprm *bprm)
+{
+ return;
+}
+
static inline int security_bprm_set (struct linux_binprm *bprm)
{
return cap_bprm_set_security (bprm);
Index: linux-2.6.10-stack/security/dummy.c
===================================================================
--- linux-2.6.10-stack.orig/security/dummy.c 2004-12-08 18:09:39.000000000
-0600
+++ linux-2.6.10-stack/security/dummy.c 2004-12-09 13:02:21.000000000 -0600
@@ -193,6 +193,11 @@
current->sgid = current->egid = current->fsgid = bprm->e_gid;
}
+static void dummy_bprm_final_setup (struct linux_binprm *bprm)
+{
+ return;
+}
+
static int dummy_bprm_set_security (struct linux_binprm *bprm)
{
return 0;
@@ -908,6 +913,7 @@
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_apply_creds);
+ set_to_dummy_if_null(ops, bprm_final_setup);
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_secureexec);
Index: linux-2.6.10-stack/security/selinux/hooks.c
===================================================================
--- linux-2.6.10-stack.orig/security/selinux/hooks.c 2004-12-08 18:45:29.000000000
-0600
+++ linux-2.6.10-stack/security/selinux/hooks.c 2004-12-10 10:29:58.000000000
-0600
@@ -1807,11 +1807,9 @@
{
struct task_security_struct *tsec;
struct bprm_security_struct *bsec;
- u32 sid;
struct av_decision avd;
- struct itimerval itimer;
- struct rlimit *rlim, *initrlim;
- int rc, i;
+ u32 sid;
+ int rc;
secondary_ops->bprm_apply_creds(bprm, unsafe);
@@ -1821,6 +1819,7 @@
sid = bsec->sid;
tsec->osid = tsec->sid;
+ tsec->unsafe = 0;
if (tsec->sid != sid) {
/* Check for shared state. If not ok, leave SID
unchanged and kill. */
@@ -1829,87 +1828,102 @@
SECCLASS_PROCESS, PROCESS__SHARE,
NULL, &avd);
if (rc) {
- task_unlock(current);
avc_audit(tsec->sid, sid, SECCLASS_PROCESS,
PROCESS__SHARE, &avd, rc, NULL);
- force_sig_specific(SIGKILL, current);
- goto lock_out;
+ tsec->unsafe = 1;
+ 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)) {
- rc = avc_has_perm_noaudit(tsec->ptrace_sid, sid,
+ rc = avc_has_perm(tsec->ptrace_sid, sid,
SECCLASS_PROCESS, PROCESS__PTRACE,
- NULL, &avd);
- if (!rc)
- tsec->sid = sid;
- task_unlock(current);
- avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS,
- PROCESS__PTRACE, &avd, rc, NULL);
+ NULL, NULL);
if (rc) {
- force_sig_specific(SIGKILL, current);
- goto lock_out;
+ tsec->unsafe = 1;
+ return;
}
- } else {
- tsec->sid = sid;
- task_unlock(current);
}
- /* Close files for which the new task SID is not authorized. */
- flush_unauthorized_files(current->files);
+ tsec->sid = sid;
+ }
+}
- /* 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, NULL);
- if (rc) {
- memset(&itimer, 0, sizeof itimer);
- for (i = 0; i < 3; i++)
- do_setitimer(i, &itimer, NULL);
- flush_signals(current);
- spin_lock_irq(¤t->sighand->siglock);
- flush_signal_handlers(current, 1);
- sigemptyset(¤t->blocked);
- recalc_sigpending();
- spin_unlock_irq(¤t->sighand->siglock);
- }
+/*
+ * called after apply_creds without the task lock held
+ */
+static void selinux_bprm_final_setup(struct linux_binprm *bprm)
+{
+ struct task_security_struct *tsec;
+ struct bprm_security_struct *bsec;
+ struct rlimit *rlim, *initrlim;
+ struct itimerval itimer;
+ int rc, i;
+ u32 sid;
- /* 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, 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 = current->security;
+ bsec = bprm->security;
+ sid = bsec->sid;
- /* Wake up the parent if it is waiting so that it can
- recheck wait permission to the new task SID. */
- wake_up_interruptible(¤t->parent->wait_chldexit);
+ if (tsec->unsafe) {
+ force_sig_specific(SIGKILL, current);
+ return;
+ }
-lock_out:
- task_lock(current);
+ if (tsec->osid == tsec->sid)
return;
+
+ /* Close files for which the new task SID is not authorized. */
+ flush_unauthorized_files(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, NULL);
+ if (rc) {
+ memset(&itimer, 0, sizeof itimer);
+ for (i = 0; i < 3; i++)
+ do_setitimer(i, &itimer, NULL);
+ flush_signals(current);
+ spin_lock_irq(¤t->sighand->siglock);
+ flush_signal_handlers(current, 1);
+ sigemptyset(¤t->blocked);
+ recalc_sigpending();
+ spin_unlock_irq(¤t->sighand->siglock);
+ }
+
+ /* 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, 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);
+ }
}
+
+ /* Wake up the parent if it is waiting so that it can
+ recheck wait permission to the new task SID. */
+ wake_up_interruptible(¤t->parent->wait_chldexit);
}
+
/* superblock security operations */
static int selinux_sb_alloc_security(struct super_block *sb)
@@ -4180,6 +4194,7 @@
.bprm_alloc_security = selinux_bprm_alloc_security,
.bprm_free_security = selinux_bprm_free_security,
.bprm_apply_creds = selinux_bprm_apply_creds,
+ .bprm_final_setup = selinux_bprm_final_setup,
.bprm_set_security = selinux_bprm_set_security,
.bprm_check_security = selinux_bprm_check_security,
.bprm_secureexec = selinux_bprm_secureexec,
Index: linux-2.6.10-stack/security/selinux/include/objsec.h
===================================================================
--- linux-2.6.10-stack.orig/security/selinux/include/objsec.h 2004-12-07 19:35:54.000000000
-0600
+++ linux-2.6.10-stack/security/selinux/include/objsec.h 2004-12-09 17:19:48.000000000
-0600
@@ -35,6 +35,12 @@
u32 create_sid; /* fscreate SID */
struct avc_entry_ref avcr; /* reference to process permissions */
u32 ptrace_sid; /* SID of ptrace parent */
+
+ /*
+ * used to share failure information from bprm_apply_creds()
+ * to bprm_final_setup().
+ */
+ char unsafe;
};
struct inode_security_struct {
static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
{
struct task_security_struct *tsec;
struct bprm_security_struct *bsec;
struct av_decision avd;
u32 sid;
int rc;
secondary_ops->bprm_apply_creds(bprm, unsafe);
tsec = current->security;
bsec = bprm->security;
sid = bsec->sid;
tsec->osid = tsec->sid;
tsec->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_noaudit(tsec->sid, sid,
SECCLASS_PROCESS, PROCESS__SHARE,
NULL, &avd);
if (rc) {
avc_audit(tsec->sid, sid, SECCLASS_PROCESS,
PROCESS__SHARE, &avd, rc, NULL);
tsec->unsafe = 1;
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)) {
rc = avc_has_perm(tsec->ptrace_sid, sid,
SECCLASS_PROCESS, PROCESS__PTRACE,
NULL, NULL);
if (rc) {
tsec->unsafe = 1;
return;
}
}
tsec->sid = sid;
}
}
/*
* called after apply_creds without the task lock held
*/
static void selinux_bprm_final_setup(struct linux_binprm *bprm)
{
struct task_security_struct *tsec;
struct bprm_security_struct *bsec;
struct rlimit *rlim, *initrlim;
struct itimerval itimer;
int rc, i;
u32 sid;
tsec = current->security;
bsec = bprm->security;
sid = bsec->sid;
if (tsec->unsafe) {
force_sig_specific(SIGKILL, current);
return;
}
if (tsec->osid == tsec->sid)
return;
/* Close files for which the new task SID is not authorized. */
flush_unauthorized_files(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, NULL);
if (rc) {
memset(&itimer, 0, sizeof itimer);
for (i = 0; i < 3; i++)
do_setitimer(i, &itimer, NULL);
flush_signals(current);
spin_lock_irq(¤t->sighand->siglock);
flush_signal_handlers(current, 1);
sigemptyset(¤t->blocked);
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
}
/* 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, 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);
}
}
/* Wake up the parent if it is waiting so that it can
recheck wait permission to the new task SID. */
wake_up_interruptible(¤t->parent->wait_chldexit);
}