Gmane
Favicon
From: Serge Hallyn <serue <at> us.ibm.com>
Subject: [PATCH] new bprm_final_setup patch
Newsgroups: gmane.linux.kernel.lsm
Date: 2004-12-10 18:21:20 GMT (4 years, 29 weeks, 3 days, 4 hours and 17 minutes ago)
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 <at> 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(&current->sighand->siglock);
-			flush_signal_handlers(current, 1);
-			sigemptyset(&current->blocked);
-			recalc_sigpending();
-			spin_unlock_irq(&current->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(&current->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(&current->sighand->siglock);
+		flush_signal_handlers(current, 1);
+		sigemptyset(&current->blocked);
+		recalc_sigpending();
+		spin_unlock_irq(&current->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(&current->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 {
Attachment (selinux-hooks.c): text/x-csrc, 3429 bytes