Subject: per page verified exec
To: None <tech-kern@netbsd.org>
From: Brett Lymn <blymn@baesystems.com.au>
List: tech-kern
Date: 09/10/2006 20:56:13
--x+6KMIRAuhnl3hBn
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Folks,
Due to the changes in the fileassoc code the per page verified exec
code has become much simpler. Attached are the diffs to enable
per-page verified exec. If there are no objections, I will commit
this code soon.
--
Brett Lymn
--x+6KMIRAuhnl3hBn
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="veriexec_per_page.diff"
Index: dev/verified_exec.c
===================================================================
RCS file: /cvsroot/src/sys/dev/verified_exec.c,v
retrieving revision 1.43
diff -u -r1.43 verified_exec.c
--- dev/verified_exec.c 5 Sep 2006 13:02:16 -0000 1.43
+++ dev/verified_exec.c 10 Sep 2006 11:18:00 -0000
@@ -168,7 +168,6 @@
if (veriexec_strict > VERIEXEC_LEARNING) {
log(LOG_WARNING, "Veriexec: Strict mode, modifying tables not "
"permitted.\n");
-
return (EPERM);
}
@@ -184,11 +183,12 @@
case VERIEXEC_DELETE:
error = veriexec_delete((struct veriexec_delete_params *)data,
- l);
+ l);
break;
case VERIEXEC_QUERY:
- error = veriexec_query((struct veriexec_query_params *)data, l);
+ error = veriexec_query((struct veriexec_query_params *)data,
+ l);
break;
default:
@@ -297,18 +297,13 @@
(memcmp(hh->fp, params->fingerprint,
min(hh->ops->hash_len, params->size))
!= 0)) ? "different" : "same"));
-
error = 0;
goto out;
}
- e = malloc(sizeof(*e), M_TEMP, M_WAITOK);
+ e = malloc(sizeof(*e), M_TEMP, M_WAITOK | M_ZERO);
e->type = params->type;
- e->status = FINGERPRINT_NOTEVAL;
- e->page_fp = NULL;
- e->page_fp_status = PAGE_FP_NONE;
- e->npages = 0;
- e->last_page_size = 0;
+ veriexec_purge(e);
if ((e->ops = veriexec_find_ops(params->fp_type)) == NULL) {
free(e, M_TEMP);
log(LOG_ERR, "Veriexec: Invalid or unknown fingerprint type "
Index: kern/kern_verifiedexec.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_verifiedexec.c,v
retrieving revision 1.66
diff -u -r1.66 kern_verifiedexec.c
--- kern/kern_verifiedexec.c 11 Aug 2006 19:17:47 -0000 1.66
+++ kern/kern_verifiedexec.c 10 Sep 2006 11:18:02 -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>
#include <sys/fileassoc.h>
#include <sys/kauth.h>
@@ -68,6 +69,9 @@
const struct sysctlnode *veriexec_count_node;
+static void
+veriexec_release_pages(int npages, int error, struct vm_page **pps,
+ struct vnode *vp);
int veriexec_hook;
/* Veriexecs table of hash types and their associated information. */
@@ -139,9 +143,10 @@
/* Register a fileassoc for Veriexec. */
veriexec_hook = fileassoc_register("veriexec", veriexec_clear);
+
#ifdef DIAGNOSTIC
if (veriexec_hook == FILEASSOC_INVAL)
- panic("Veriexec: Can't register meta-hook");
+ panic("Veriexec: Can't register fileassoc");
#endif /* DIAGNOSTIC */
LIST_INIT(&veriexec_ops_list);
@@ -236,12 +241,10 @@
if (error)
return (error);
-#if 0 /* XXX - for now */
if ((vfe->type & VERIEXEC_UNTRUSTED) &&
(vfe->page_fp_status == PAGE_FP_NONE))
do_perpage = 1;
else
-#endif
do_perpage = 0;
ctx = (void *) malloc(vfe->ops->context_size, M_TEMP, M_WAITOK);
@@ -281,6 +284,9 @@
if (do_perpage) {
free(vfe->page_fp, M_TEMP);
vfe->page_fp = NULL;
+ vfe->page_fp_status = PAGE_FP_NONE;
+ /* XXX: make NOMATCH */
+ vfe->status = FINGERPRINT_NOTEVAL;
}
goto bad;
@@ -413,8 +419,7 @@
/* Evaluate fingerprint if needed. */
error = 0;
digest = NULL;
- if ((vfe->status == FINGERPRINT_NOTEVAL) ||
- (vfe->type & VERIEXEC_UNTRUSTED)) {
+ if (vfe->status == FINGERPRINT_NOTEVAL) {
/* Calculate fingerprint for on-disk file. */
digest = (u_char *) malloc(vfe->ops->hash_len, M_TEMP,
M_WAITOK);
@@ -566,6 +571,85 @@
return (error);
}
+int
+veriexec_block_verify(struct vnode *vp, struct vm_page **pps,
+ voff_t offset, int *npages)
+{
+ struct veriexec_file_entry *vfe = NULL;
+ voff_t offidx, i;
+ int error;
+
+ error = 0;
+
+ vfe = veriexec_lookup(vp);
+ if ((vfe == NULL) || (vfe->page_fp == NULL)
+ || ((vfe->type & VERIEXEC_UNTRUSTED) == 0)
+ || ((vfe->type & VERIEXEC_UNTRUSTED) &&
+ (vfe->page_fp_status == PAGE_FP_NONE))) {
+ return (0);
+ }
+
+ offidx = (offset >> PAGE_SHIFT);
+ for (i = 0; i < *npages; i++) {
+ if ((pps[i] == NULL) || (pps[i] == PGO_DONTCARE)) {
+ continue;
+ }
+
+ error = veriexec_page_verify(vfe, pps[i], i + offidx, curlwp);
+
+ if (error) {
+ veriexec_release_pages(*npages, error, pps, vp);
+ *npages = 0;
+ return (error);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Release uvm pages - called if an error occurs during page verification.
+ */
+static void
+veriexec_release_pages(int npages, int error, struct vm_page **pps,
+ struct vnode *vp)
+{
+ struct uvm_object *uobj = &vp->v_uobj;
+ int i;
+ UVMHIST_FUNC("veriexec_release_pages"); UVMHIST_CALLED(ubchist);
+
+ /*
+ * 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);
+ for (i = 0; i < npages; i++) {
+ if ((pps[i] == NULL) || (pps[i] == PGO_DONTCARE)) {
+ continue;
+ }
+ UVMHIST_LOG(ubchist, "veriexec examining pg %p flags 0x%x",
+ pps[i], pps[i]->flags, 0,0);
+ pps[i]->flags |= PG_RELEASED;
+ }
+ uvm_lock_pageq();
+ for (i = 0; i < npages; i++) {
+ if ((pps[i] == NULL) || (pps[i] == PGO_DONTCARE))
+ continue;
+ pmap_clear_reference(pps[i]);
+ uvm_pagedeactivate(pps[i]);
+ if (pps[i]->flags & PG_WANTED) {
+ wakeup(pps[i]);
+ pps[i]->flags &= ~(PG_WANTED);
+ }
+ }
+ uvm_page_unbusy(pps, npages);
+ uvm_unlock_pageq();
+ simple_unlock(&uobj->vmobjlock);
+ UVMHIST_LOG(ubchist, "veriexec returning",
+ 0,0,0,0);
+}
+
/*
* Veriexec remove policy code.
*/
@@ -595,7 +679,7 @@
vte = veriexec_tblfind(vp);
#ifdef DIAGNOSTIC
if (vte == NULL)
- panic("Fileassoc: Inconsistency during entry removel");
+ panic("Fileassoc: Inconsistency during entry removal");
#endif /* DIAGNOSTIC */
vte->vte_count--;
@@ -696,6 +780,13 @@
veriexec_purge(struct veriexec_file_entry *vfe)
{
vfe->status = FINGERPRINT_NOTEVAL;
+ vfe->page_fp_status = PAGE_FP_NONE;
+ vfe->npages = 0;
+ vfe->last_page_size = 0;
+
+ if (vfe->page_fp != NULL)
+ free(vfe->page_fp, M_TEMP);
+ vfe->page_fp = NULL;
}
/*
Index: sys/verified_exec.h
===================================================================
RCS file: /cvsroot/src/sys/sys/verified_exec.h,v
retrieving revision 1.39
diff -u -r1.39 verified_exec.h
--- sys/verified_exec.h 11 Aug 2006 19:17:47 -0000 1.39
+++ sys/verified_exec.h 10 Sep 2006 11:18:03 -0000
@@ -180,17 +180,17 @@
int veriexec_add_fp_ops(struct veriexec_fp_ops *);
void veriexec_init_fp_ops(void);
struct veriexec_fp_ops *veriexec_find_ops(u_char *name);
-int veriexec_fp_calc(struct lwp *, struct vnode *, struct veriexec_file_entry *,
- u_char *);
+int veriexec_fp_calc(struct lwp *, struct vnode *,
+ struct veriexec_file_entry *, u_char *);
int veriexec_fp_cmp(struct veriexec_fp_ops *, u_char *, u_char *);
struct veriexec_table_entry *veriexec_tblfind(struct vnode *);
struct veriexec_file_entry *veriexec_lookup(struct vnode *);
int veriexec_hashadd(struct vnode *, struct veriexec_file_entry *);
int veriexec_verify(struct lwp *, struct vnode *,
const u_char *, int, struct veriexec_file_entry **);
-int veriexec_page_verify(struct veriexec_file_entry *, struct vm_page *, size_t,
- struct lwp *);
-int veriexec_removechk(struct vnode *, const char *, struct lwp *l);
+int veriexec_page_verify(struct veriexec_file_entry *, struct vm_page *,
+ size_t, struct lwp *);
+int veriexec_removechk(struct vnode *, const char *, struct lwp *);
int veriexec_renamechk(struct vnode *, const char *, struct vnode *,
const char *, struct lwp *);
void veriexec_report(const u_char *, const u_char *, struct lwp *, int);
@@ -201,7 +201,8 @@
void veriexec_clear(void *, int);
void veriexec_purge(struct veriexec_file_entry *);
int veriexec_rawchk(struct vnode *vp);
-
+int veriexec_block_verify(struct vnode *vp, struct vm_page **pps,
+ voff_t offset, int *npages);
#endif /* _KERNEL */
#endif /* !_SYS_VERIFIED_EXEC_H_ */
Index: uvm/uvm_vnode.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_vnode.c,v
retrieving revision 1.72
diff -u -r1.72 uvm_vnode.c
--- uvm/uvm_vnode.c 22 Jul 2006 08:47:56 -0000 1.72
+++ uvm/uvm_vnode.c 10 Sep 2006 11:18:03 -0000
@@ -56,6 +56,7 @@
#include "opt_uvmhist.h"
#include "opt_readahead.h"
#include "opt_ddb.h"
+#include "veriexec.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -69,6 +70,9 @@
#include <sys/conf.h>
#include <sys/pool.h>
#include <sys/mount.h>
+#if NVERIEXEC > 0
+#include <sys/verified_exec.h>
+#endif
#include <miscfs/specfs/specdev.h>
@@ -315,6 +319,11 @@
error = VOP_GETPAGES(vp, offset, pps, npagesp, centeridx,
access_type, advice, flags);
+#if NVERIEXEC > 0
+ if (*npagesp > 0)
+ error = veriexec_block_verify(vp, pps, offset, npagesp);
+#endif /* NVERIEXEC > 0 */
+
#if defined(READAHEAD_STATS)
if (((flags & PGO_LOCKED) != 0 && *npagesp > 0) ||
((flags & (PGO_LOCKED|PGO_SYNCIO)) == PGO_SYNCIO && error == 0)) {
--x+6KMIRAuhnl3hBn--