Subject: Additional features for veriexecgen(8)
To: None <tech-security@NetBSD.org, tech-userland@NetBSD.org>
From: M J Fleming <scs5mjf@comp.leeds.ac.uk>
List: tech-security
Date: 10/09/2006 12:09:02
--nFreZHaLTZJo0R7j
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Attached is a patch that implements a number of new features for veriexecgen.
 
The "-F" command-line option instructs veriexecgen(8) to "guess" which
flags should be written to the fingerprint file for certain files, based on 
characteristics of that file (its path, permissions, etc).
 
For instance, executing,
 
"veriexecgen -F"
 
instructs veriexecgen to search the default system paths for files.
This will cause all files that are on a local filesystem and are executable
to have the flag "PROGRAM" written to the fingerprint file. Any files that
are not executable will have "FILE" written to the fingerprint file. If any
of the files on the default system paths are on non-local filesystems,
the flag "UNTRUSTED" will be appended to the flags for that file.

Of course, a way to make even more intelligent decisions about the flags
that will be written to the fingerprint file is needed. This patch also provides
the user with a way to specify (in conjuection with F) that they want 
veriexecgen to use default values for common library paths, script suffixes
and interpreter paths (/bin/sh, /bin/ksh, etc).
 
These are wildcards for pathnames which are compared against the files, they
can be turned on with command-line options,

- A default list of interpreter paths (-I)
- A default list of library paths (-L)
- A default list of script suffixes  (-S)
 
These changes are intended to allow the user to type less and achieve more.
 
The command-line options -i, -s, -l also allow the user to specify custom paths
for interpreters, script suffixes and library paths, respectively. These 
options allow paths to be specified via globbing, which uses the globbing rules
based on the user's shell.

Examples:

"veriexecgen -i '/usr/pkg/bin/python2.4'" - labels the file 
					    /usr/pkg/bin/python2.4 as an
 					    interpreter.

"veriexecgen -l '/mnt/lib/*'" - labels all files in the directory /mnt/lib
				as libraries.

"veriexecgen -s '*.xxx'" -  Treats as files with a suffix of 'xxx' as scripts.

Thanks,
Matt


--nFreZHaLTZJo0R7j
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="veriexecgen.patch"

? usr.sbin/veriexecgen/.gdbinit
? usr.sbin/veriexecgen/veriexecgen
? usr.sbin/veriexecgen/veriexecgen.cat8
Index: usr.sbin/veriexecgen/veriexecgen.c
===================================================================
RCS file: /cvsroot/src/usr.sbin/veriexecgen/veriexecgen.c,v
retrieving revision 1.6
diff -u -r1.6 veriexecgen.c
--- usr.sbin/veriexecgen/veriexecgen.c	23 Sep 2006 19:08:48 -0000	1.6
+++ usr.sbin/veriexecgen/veriexecgen.c	7 Oct 2006 14:56:32 -0000
@@ -40,11 +40,13 @@
 #include <sys/types.h>
 #include <sys/queue.h>
 #include <sys/stat.h>
+#include <sys/statvfs.h>
 #include <sys/dirent.h>
 #include <sys/verified_exec.h>
 
 #include <err.h>
 #include <errno.h>
+#include <fnmatch.h>
 #include <fts.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -65,11 +67,21 @@
 #define DEFAULT_SYSPATHS { "/bin", "/sbin", "/usr/bin", "/usr/sbin", \
 			   "/lib", "/usr/lib", "/libexec", "/usr/libexec", \
 			   NULL }
+#define DEFAULT_INTERPRETERS { "/bin/sh", "/bin/csh", "/bin/ksh",	\
+			      "/usr/pkg/bin/perl", "/usr/pkg/bin/python2.4", \
+			      NULL }
+#define DEFAULT_LIBS { "/lib/*", "/usr/lib/*", "/usr/pkg/lib/*", NULL }
+#define DEFAULT_SCRIPT_SUFFIX { ".sh", ".py", ".pl", NULL }
+
+#define PROGRAM			VERIEXEC_DIRECT
+#define INTERPRETER		VERIEXEC_INDIRECT
+#define SCRIPT			(VERIEXEC_DIRECT | VERIEXEC_FILE)
+#define LIBRARY			0x20
 
 struct fentry {
 	char filename[MAXPATHLEN];
 	char *hash_val;
-	int flags;
+	char *flags;
 	TAILQ_ENTRY(fentry) f;
 };
 TAILQ_HEAD(, fentry) fehead;
@@ -87,13 +99,26 @@
 	{ NULL, NULL },
 };
 
-int Aflag, aflag, Dflag, Fflag, rflag, Sflag, vflag;
+struct flags {
+	int script;
+	int lib;
+	int intrprtr;
+	int exec;
+	int local;
+};
+
+int Aflag, aflag, Dflag, Fflag, Iflag, iflag, Lflag, lflag, rflag;
+int Sflag, sflag, vflag, Xflag;
+
+char **interpreter_patterns = NULL;
+char **lib_patterns = NULL;
+char **scripts = NULL;
 
 static void
 usage(void)
 {
 	(void)fprintf(stderr,
-	    "usage: %s [-AaDrSv] [-d dir] [-o fingerprintdb]"
+	    "usage: %s [-AaDFIiLlrSsvX] [-d dir] [-o fingerprintdb]"
 	    " [-t algorithm]\n", getprogname());
 }
 
@@ -130,20 +155,89 @@
 	return h->filefunc(filename, NULL);
 }
 
+static char * 
+weigh_flags(struct flags f)
+{
+	char *buf = "";
+
+	if (!(f.local))
+		easprintf(&buf, "%sUNTRUSTED", *buf ? ", " : "");
+
+	if (f.script)
+		easprintf(&buf, "%s%sSCRIPT", buf, *buf ? ", " : "");
+	else if (f.lib)
+		easprintf(&buf, "%s%sLIBRARY", buf, *buf ? ", " : "");
+	else if (f.intrprtr)
+		easprintf(&buf, "%s%sINTERPRETER", buf, *buf ? ", " : "");
+	else if (f.exec)
+		easprintf(&buf, "%s%sPROGRAM", buf, *buf ? ", " : "");
+	else 
+		easprintf(&buf, "%s%sFILE", buf, *buf ? ", " : "");
+
+	return buf;
+}
+
 static int
+wildcard(char **list, char *path)
+{
+	int i;
+	char **a = list;
+
+	for (i = 0; a[i] != NULL; i++) {
+		if (!fnmatch(a[i], path, 0))
+			return 1;
+	}
+	return 0;
+}
+
+static char * 
 figure_flags(char *path, mode_t mode)
 {
-#ifdef notyet
+	char *p;
+	struct statvfs st;
+	struct flags f;
+
+	memset(&f, 0, sizeof(struct flags));
+
 	if (Fflag) {
 		/* Try to figure out right flag(s). */
-		return VERIEXEC_DIRECT;
-}
-#endif /* notyet */
+		if (statvfs(path, &st) == -1)
+			err(1, "could not stat %s", path);
+
+		if ((st.f_flag & MNT_LOCAL))
+			f.local = 1;
 
-	if (!IS_EXEC(mode))
-		return VERIEXEC_FILE;
-	else
-		return 0;
+		if (IS_EXEC(mode))
+			f.exec = 1;
+
+		if (Lflag) {
+			char *a[] = DEFAULT_LIBS;
+			f.lib = wildcard(a, path);
+		}
+
+		if (lflag && *lib_patterns != NULL)
+			f.lib = wildcard(lib_patterns, path);
+
+		if (Iflag) {
+			char *a[] = DEFAULT_INTERPRETERS;
+			f.intrprtr = wildcard(a, path);
+		}
+
+		if (iflag && *interpreter_patterns != NULL)
+			f.intrprtr = wildcard(interpreter_patterns, path);
+
+		if (Sflag && ((p = strchr(path, '.')) != NULL)) {
+			char *a[] = DEFAULT_SCRIPT_SUFFIX;
+			f.script = wildcard(a, p);
+		}
+
+		if (sflag && *scripts != NULL)
+			f.script = wildcard(scripts, path);
+
+	} else if (IS_EXEC(mode))
+		f.exec = 1;
+
+	return weigh_flags(f);
 }
 
 static int
@@ -168,7 +262,7 @@
 
 	if (file->fts_info == FTS_SL) {
 		if (stat(file->fts_path, &sb) == -1)
-			err(1, "Cannot stat symlink");
+			err(1, "Cannot stat symlink %s", file->fts_path);
 	} else
 		sb = *file->fts_statp;
 
@@ -225,15 +319,6 @@
 	fts_close(fh);
 }
 
-static char *
-flags2str(int flags)
-{
-	if (flags != 0)
-		return "FILE";
-	else
-		return "";
-}
-
 static void
 store_entries(char *dbfile, struct hash *hash)
 {
@@ -274,7 +359,7 @@
 			(void)printf("Adding %s.\n", e->filename);
 
 		(void)fprintf(fp, "%s %s %s %s\n", e->filename,
-			hash->hashname, e->hash_val, flags2str(e->flags));
+			hash->hashname, e->hash_val, e->flags);
 	}
 
 	(void)fclose(fp);
@@ -293,14 +378,15 @@
 int
 main(int argc, char **argv)
 {
-	int ch, total = 0;
+	int ch, stotal = 0, itotal = 0, sutotal = 0, ltotal = 0;
 	char *dbfile = NULL;
 	char **search_path = NULL;
 	struct hash *hash = NULL;
 
-	Aflag = aflag = Dflag = Fflag = rflag = Sflag = vflag = 0;
+	Aflag = aflag = Dflag = Fflag = Iflag = iflag = Lflag = lflag;
+	rflag = Sflag = sflag = vflag = Xflag = 0;
 
-	while ((ch = getopt(argc, argv, "AaDd:ho:rSt:v")) != -1) {
+	while ((ch = getopt(argc, argv, "AaDd:FhIiLl::o:rSs:t:vX")) != -1) {
 		switch (ch) {
 		case 'A':
 			Aflag = 1;
@@ -313,18 +399,34 @@
 			break;
 		case 'd':
 			search_path = erealloc(search_path, sizeof(char *) *
-			    (total + 1));
-			search_path[total] = optarg;
-			search_path[++total] = NULL;
+			    (stotal + 1));
+			search_path[stotal] = optarg;
+			search_path[++stotal] = NULL;
 			break;
-#ifdef notyet
 		case 'F':
 			Fflag = 1;
 			break;
-#endif /* notyet */
 		case 'h':
 			usage();
 			return 0;
+		case 'I':
+			Iflag = 1;
+			break;
+		case 'i':
+			iflag = 1;
+			interpreter_patterns = erealloc(interpreter_patterns,
+						sizeof(char *) * (itotal + 1));
+			interpreter_patterns[itotal++] = optarg;
+			break;
+		case 'L':
+			Lflag = 1;
+			break;
+		case 'l':
+			lflag = 1;
+			lib_patterns = erealloc(lib_patterns, sizeof(char *) *
+						(ltotal + 1));
+			lib_patterns[ltotal++] = optarg;
+			break;
 		case 'o':
 			dbfile = optarg;
 			break;
@@ -334,18 +436,31 @@
 		case 'S':
 			Sflag = 1;
 			break;
+		case 's':
+			sflag = 1;
+			scripts = erealloc(scripts,
+						sizeof(char *) * (sutotal + 1));
+			scripts[sutotal++] = optarg;
+			break;
 		case 't':
 			hash = find_hash(optarg);
 			break;
 		case 'v':
 			vflag = 1;
 			break;
+		case 'X':
+			Xflag = 1;
+			break;
 		default:
 			usage();
 			return 1;
 		}
 	}
 
+	/* -F implies -I and -S */
+	if (Fflag)
+		Iflag = Lflag = Sflag = 1;
+
 	if (dbfile == NULL)
 		dbfile = DEFAULT_DBFILE;
 
@@ -375,7 +490,7 @@
 
 	store_entries(dbfile, hash);
 
-	if (Sflag && chflags(dbfile, SF_IMMUTABLE) != 0)
+	if (Xflag && chflags(dbfile, SF_IMMUTABLE) != 0)
 		err(1, "Can't set immutable flag");
 
 	return 0;

--nFreZHaLTZJo0R7j--