Subject: simple tpe implementation
To: None <tech-kern@NetBSD.org>
From: Elad Efrat <elad@NetBSD.org>
List: tech-security
Date: 02/02/2007 00:41:00
This is a multi-part message in MIME format.
--------------020307070103000404000206
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

hi,

attached is a very simple patch that adds a "security.tpe" sysctl node
to control a tpe (or, trusted path execution) feature.

what it does: prevent execution of any program that does not live in a
directory that is owned by root and writable by neither group or other.

why would you need it: quick knob you can enable to prevent any users
from running their own stuff. kinda useful if there's a now 0-day out
or you're in the middle of patching your system or whatever.

caveats: it doesn't use kauth yet. if it could it would, so let's not
get into that now. it also doesn't address interpreters (i.e., someone
starting python and feeding it stuff) yet. we will do that -- we have
the mechanism in place, but I'm holding it back for now.

default: disabled.

demo:
phyre:elad {8} test/hi
hi!
phyre:elad {9} su
Password:
phyre:elad {1} sysctl -w security.tpe.enabled=1
security.tpe.enabled: 0 -> 1
phyre:elad {2} exit
exit
phyre:elad {10} test/hi
test/hi: Operation not permitted.
phyre:elad {11} su
Password:
phyre:elad {1} sysctl -w security.tpe.enabled=0
security.tpe.enabled: 1 -> 0
phyre:elad {2} exit
exit
phyre:elad {12} test/hi
hi!
phyre:elad {13}

unless there are objections, this will be committed soon.

-e.

--------------020307070103000404000206
Content-Type: text/plain;
 name="tpe.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="tpe.diff"

Index: kern_exec.c
===================================================================
RCS file: /usr/cvs/src/sys/kern/kern_exec.c,v
retrieving revision 1.234
diff -u -p -r1.234 kern_exec.c
--- kern_exec.c	23 Dec 2006 17:23:51 -0000	1.234
+++ kern_exec.c	31 Jan 2007 11:58:49 -0000
@@ -62,6 +62,7 @@ __KERNEL_RCSID(0, "$NetBSD: kern_exec.c,
 #include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/kauth.h>
+#include <sys/sysctl.h>
 
 #include <sys/sa.h>
 #include <sys/savar.h>
@@ -216,6 +217,72 @@ struct lock exec_lock;
 static void link_es(struct execsw_entry **, const struct execsw *);
 #endif /* LKM */
 
+#ifndef	TPE_ENABLED
+#define	TPE_ENABLED	0
+#endif /* !TPE_ENABLED */
+
+static int tpe_enabled = TPE_ENABLED;
+
+static int tpe_check(struct lwp *, struct vnode *, struct vattr *);
+
+SYSCTL_SETUP(sysctl_security_tpe_setup, "sysctl security.tpe subtree setup")
+{
+	const struct sysctlnode *rnode = NULL;
+
+	sysctl_createv(clog, 0, NULL, &rnode,
+		       CTLFLAG_PERMANENT,
+		       CTLTYPE_NODE, "security", NULL,
+		       NULL, 0, NULL, 0,
+		       CTL_SECURITY, CTL_EOL);
+
+	sysctl_createv(clog, 0, &rnode, &rnode,
+		       CTLFLAG_PERMANENT,
+		       CTLTYPE_NODE, "tpe",
+		       SYSCTL_DESCR("Trusted Path Execution settings."),
+		       NULL, 0, NULL, 0,
+		       CTL_CREATE, CTL_EOL);
+
+	sysctl_createv(clog, 0, &rnode, NULL,
+		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
+		       CTLTYPE_INT, "enabled",
+		       SYSCTL_DESCR("Enable Trusted Path Execution."),
+		       NULL, 0, &tpe_enabled, 0,
+		       CTL_CREATE, CTL_EOL);
+}
+
+/*
+ * Check if the vnode is in a trusted path.
+ */
+int
+tpe_check(struct lwp *l, struct vnode *vp, struct vattr *va)
+{
+	struct vattr dva;
+	int error;
+
+	/* Must be a directory. */
+	if (vp->v_type != VDIR)
+		return (0);
+
+	/* Get directory attributes if needed. */
+	if (va == NULL) {
+		error = VOP_GETATTR(vp, &dva, l->l_cred, l);
+		if (error)
+			return (error);
+
+		va = &dva;
+	}
+
+	/* XXX Must be owned by root. */
+	if (va->va_uid != 0)
+		return (EPERM);
+
+	/* Must not be writable by group or other. */
+	if (va->va_mode & (S_IWGRP | S_IWOTH))
+		return (EPERM);
+
+	return (0);
+}
+
 /*
  * check exec:
  * given an "executable" described in the exec package's namei info,
@@ -278,6 +345,13 @@ check_exec(struct lwp *l, struct exec_pa
 	if (vp->v_mount->mnt_flag & MNT_NOSUID)
 		epp->ep_vap->va_mode &= ~(S_ISUID | S_ISGID);
 
+	/* Trusted Path Execution. */
+	if (tpe_enabled) {
+		error = tpe_check(l, ndp->ni_dvp, NULL);
+		if (error)
+			goto bad1;
+	}
+
 	/* try to open it */
 	if ((error = VOP_OPEN(vp, FREAD, l->l_cred, l)) != 0)
 		goto bad1;

--------------020307070103000404000206--