Subject: Re: verified exec per page fingerprints
To: Brett Lymn <blymn@baesystems.com.au>
From: Brett Lymn <blymn@baesystems.com.au>
List: tech-kern
Date: 11/17/2005 23:45:56
On Mon, Nov 14, 2005 at 11:17:28PM +1030, Brett Lymn wrote:
> 
> Elad Efrat took the original code, fixed it up and made it work with
> the new style verified exec, this code was committed to the tree but
> was partially backed out due to me putting the page check in the wrong
> place, Elad fixed this problem and attached is the resultant patch
> that will allow per page fingerprints.
> 

Many thanks to those who offered comments, especially to Yamamoto-san
for his behind the scenes help.  Below is an updated patch that
actually enables the per page fingerprints and attempts to release
pages if a fingerprint mismatch occurs (this code bears a striking
resemblence to a fragment in genfs_getpages()).  The behaviour of this
patch is not 100% and I need to work on why.  The page fingerprints
work and correctly pick up modified pages on page in but it looks like
if there is another instance of the modified file running then it
appears to have the "bad" page mapped into it's space and can use that
page.  I suspect I am not doing enough to unmap the "bad" page when a
fingerprint mismatch is detected - I need to look at this further.

Index: sbin/veriexecctl/veriexecctl.8
===================================================================
RCS file: /cvsroot/src/sbin/veriexecctl/veriexecctl.8,v
retrieving revision 1.19
diff -u -r1.19 veriexecctl.8
--- sbin/veriexecctl/veriexecctl.8	5 Oct 2005 13:58:49 -0000	1.19
+++ sbin/veriexecctl/veriexecctl.8	17 Nov 2005 12:52:43 -0000
@@ -69,8 +69,11 @@
 .Em path
 is the full path to the file and
 .Em type
-is the type of fingerprint used, see above for the default list.
-Other fingerprints may be available depending on kernel configuration.
+is the type of fingerprint used.
+The fingerprint types available depends on the kernel configuration.
+A list of supported fingerprint types can by found by querying the
+variable kern.veriexec.algorithms using
+.Xr sysctl 8 .
 The
 .Em fingerprint
 field is a hexadecimal representation of the fingerprint for
@@ -78,7 +81,7 @@
 The field
 .Em options
 contains the associated options for the file.
-Currently there are seven valid options:
+Valid options are:
 .Pp
 .Bl -tag -width INTERPRETER -compact
 .It Dv DIRECT
@@ -117,10 +120,14 @@
 .Dv FILE
 option.
 .It Dv UNTRUSTED
-This option is used to indicate that the file is located on
-untrusted storage, and its fingerprint should not be cached,
-but calculated each time it is accessed and when pages with
-this file as backing store are paged in.
+Indicates that the storage the file is on is not under direct control
+of the kernel and, therefore, the file cannot be guaranteed not to be
+modified.
+Enabling this flag will cause the veriexec kernel subsystem
+to perform fingerprinting at the memory page level and verify these
+fingerprints when paging parts of the file into memory.
+Enabling this flag will cause an increase in executable start up time,
+an increase in kernel memory usage and decrease in paging performance.
 .El
 .Pp
 There must be only one executable/fingerprint pair per line.
Index: sbin/veriexecctl/veriexecctl.c
===================================================================
RCS file: /cvsroot/src/sbin/veriexecctl/veriexecctl.c,v
retrieving revision 1.17
diff -u -r1.17 veriexecctl.c
--- sbin/veriexecctl/veriexecctl.c	5 Oct 2005 13:48:48 -0000	1.17
+++ sbin/veriexecctl/veriexecctl.c	17 Nov 2005 12:52:43 -0000
@@ -141,7 +141,7 @@
 	 * If there's no access type specified, use the default.
 	 */
 	if (!(params.type & (VERIEXEC_DIRECT|VERIEXEC_INDIRECT|VERIEXEC_FILE)))
-		params.type |= VERIEXEC_DIRECT;
+		params.type |= (VERIEXEC_DIRECT | VERIEXEC_INDIRECT);
 	if (ioctl(gfd, VERIEXEC_LOAD, &params) == -1)
 		warn("Cannot load params from `%s'", params.file);
 	free(params.fingerprint);
Index: sys/kern/kern_verifiedexec.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_verifiedexec.c,v
retrieving revision 1.45
diff -u -r1.45 kern_verifiedexec.c
--- sys/kern/kern_verifiedexec.c	12 Oct 2005 14:26:47 -0000	1.45
+++ sys/kern/kern_verifiedexec.c	17 Nov 2005 12:52:56 -0000
@@ -56,6 +56,7 @@
 #include <crypto/sha2/sha2.h>
 #include <crypto/ripemd160/rmd160.h>
 #include <sys/md5.h>
+#include <uvm/uvm.h>
 #include <uvm/uvm_extern.h>
 
 int veriexec_verbose;
@@ -220,12 +221,10 @@
 		panic("veriexec: Operations vector is NULL");
 	}
 
-#if 0 /* XXX - for now */
 	if ((vhe->type & VERIEXEC_UNTRUSTED) &&
 	    (vhe->page_fp_status == PAGE_FP_NONE))
 		do_perpage = 1;
 	else
-#endif
 		do_perpage = 0;
 
 	ctx = (void *) malloc(vhe->ops->context_size, M_TEMP, M_WAITOK);
@@ -514,7 +513,7 @@
  */
 int
 veriexec_page_verify(struct veriexec_hash_entry *vhe, struct vattr *va,
-		     struct vm_page *pg, size_t idx)
+		     const struct vm_page *pg, size_t idx)
 {
 	void *ctx;
 	u_char *fp;
@@ -591,6 +590,61 @@
 	return (error);
 }
 
+int
+veriexec_block_verify(struct vnode *vp, struct vm_page **pps,
+    voff_t offset, int npages)
+{
+	struct veriexec_hash_entry *vhe;
+	struct vattr va;
+	struct uvm_object *uobj = &vp->v_uobj;
+	voff_t offidx, i;
+	int error;
+	UVMHIST_FUNC("veriexec_block_verify"); UVMHIST_CALLED(ubchist);
+
+	error = VOP_GETATTR(vp, &va, curlwp->l_proc->p_ucred,
+	    curlwp->l_proc);
+	if (error)
+		return (error);
+
+	vhe = veriexec_lookup(va.va_fsid, va.va_fileid);
+	if ((vhe == NULL) || (vhe->page_fp == NULL))
+		return (0);
+
+	offidx = (offset >> PAGE_SHIFT);
+
+	for (i = 0; i < npages; i++) {
+		error = veriexec_page_verify(vhe, &va, pps[i], i + offidx);
+		if (error) {
+			  /*
+			   * On error, release all the pages requested,
+			   * the page in may have worked but we are not
+			   * interested if the fingerprinting fails.
+			   */
+			simple_lock(&uobj->vmobjlock); /* XXX needed ? */
+			for (i = 0; i < npages; i++) {
+				if (pps[i] == NULL) {
+					continue;
+				}
+				UVMHIST_LOG(ubchist, "veriexec examining pg %p"
+					    "flags 0x%x",
+					    pps[i], pps[i]->flags, 0,0);
+				if (pps[i]->flags & PG_FAKE) {
+					pps[i]->flags |= PG_RELEASED;
+				}
+			}
+			uvm_lock_pageq();
+			uvm_page_unbusy(pps, npages);
+			uvm_unlock_pageq();
+			simple_unlock(&uobj->vmobjlock);
+			UVMHIST_LOG(ubchist, "veriexec returning error %d",
+				    error,0,0,0);
+			return (error);
+		}
+	}
+
+	return (0);
+}
+
 /*
  * Veriexec remove policy code.
  */
Index: sys/sys/verified_exec.h
===================================================================
RCS file: /cvsroot/src/sys/sys/verified_exec.h,v
retrieving revision 1.21
diff -u -r1.21 verified_exec.h
--- sys/sys/verified_exec.h	7 Oct 2005 18:07:46 -0000	1.21
+++ sys/sys/verified_exec.h	17 Nov 2005 12:52:57 -0000
@@ -199,7 +199,8 @@
 int veriexec_verify(struct proc *, struct vnode *, struct vattr *,
 		    const u_char *, int, struct veriexec_hash_entry **);
 int veriexec_page_verify(struct veriexec_hash_entry *, struct vattr *,
-			 struct vm_page *, size_t);
+			 const struct vm_page *, size_t);
+int veriexec_block_verify(struct vnode *, struct vm_page **, voff_t, int);
 int veriexec_removechk(struct proc *, struct vnode *, const char *);
 int veriexec_renamechk(struct vnode *, const char *, const char *);
 void veriexec_init_fp_ops(void);
Index: sys/uvm/uvm_vnode.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_vnode.c,v
retrieving revision 1.66
diff -u -r1.66 uvm_vnode.c
--- sys/uvm/uvm_vnode.c	27 Jun 2005 02:29:32 -0000	1.66
+++ sys/uvm/uvm_vnode.c	17 Nov 2005 12:52:57 -0000
@@ -55,6 +55,7 @@
 #include "fs_nfs.h"
 #include "opt_uvmhist.h"
 #include "opt_ddb.h"
+#include "opt_verified_exec.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -68,6 +69,9 @@
 #include <sys/conf.h>
 #include <sys/pool.h>
 #include <sys/mount.h>
+#ifdef VERIFIED_EXEC
+#include <sys/verified_exec.h>
+#endif /* VERIFIED_EXEC */
 
 #include <miscfs/specfs/specdev.h>
 
@@ -299,6 +303,12 @@
 	UVMHIST_LOG(ubchist, "vp %p off 0x%x", vp, (int)offset, 0,0);
 	error = VOP_GETPAGES(vp, offset, pps, npagesp, centeridx,
 			     access_type, advice, flags);
+
+#ifdef VERIFIED_EXEC
+	if ((!error) && !(flags & PGO_LOCKED))
+		error = veriexec_block_verify(vp, pps, offset, *npagesp);
+#endif /* VERIFIED_EXEC */
+
 	return error;
 }

-- 
Brett Lymn