Add a C version of Veriexec's fingerprint generator, written by Matt

Fleming.

This one has some nice options -- for example, an admin can run right
after installing a system:

        fpgen -D

and it will fingerprint a set of "common" system directories to the
default loaction. See the man-page for more stuff.

Performance-wise, here are results for both fpgen.sh (old) and this
new tool:

	474.599u 574.335s 13:53.05 125.9%       0+0k 0+307io 0pf+0w

	0.424u 0.131s 0:00.56 98.2%     0+0k 0+2io 0pf+0w

...guess which is which? (that's ~1500 times *faster*)
This commit is contained in:
elad 2006-09-16 20:54:42 +00:00
parent 177426845d
commit ade08c91dc
6 changed files with 510 additions and 4 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: mi,v 1.651 2006/09/16 08:38:20 tnozaki Exp $
# $NetBSD: mi,v 1.652 2006/09/16 20:54:43 elad Exp $
. base-sys-root
./altroot base-sys-root
./bin base-sys-root
@ -1029,6 +1029,7 @@
./usr/sbin/faithd base-router-bin inet6
./usr/sbin/fixmount base-nfsclient-bin
./usr/sbin/flush base-obsolete obsolete
./usr/sbin/veriexecgen base-sysutil-bin
./usr/sbin/fsinfo base-sysutil-bin
./usr/sbin/fssconfig base-sysutil-bin
./usr/sbin/fwctl base-sysutil-bin

View File

@ -1,4 +1,4 @@
# $NetBSD: mi,v 1.930 2006/09/10 15:45:55 plunky Exp $
# $NetBSD: mi,v 1.931 2006/09/16 20:54:43 elad Exp $
./etc/mtree/set.man man-sys-root
./usr/share/info/am-utils.info man-amd-info info
./usr/share/info/as.info man-computil-info bfd,info
@ -1854,6 +1854,7 @@
./usr/share/man/cat8/fingerd.0 man-sysutil-catman .cat
./usr/share/man/cat8/fixmount.0 man-sysutil-catman .cat
./usr/share/man/cat8/flush.0 man-postfix-catman postfix,.cat
./usr/share/man/cat8/veriexecgen.0 man-sysutil-catman .cat
./usr/share/man/cat8/fsck.0 man-sysutil-catman .cat
./usr/share/man/cat8/fsck_ext2fs.0 man-ext2fs-catman .cat
./usr/share/man/cat8/fsck_ffs.0 man-sysutil-catman .cat
@ -4226,6 +4227,7 @@
./usr/share/man/man8/fingerd.8 man-sysutil-man .man
./usr/share/man/man8/fixmount.8 man-sysutil-man .man
./usr/share/man/man8/flush.8 man-postfix-man postfix,.man
./usr/share/man/man8/veriexecgen.8 man-sysutil-man .man
./usr/share/man/man8/fsck.8 man-sysutil-man .man
./usr/share/man/man8/fsck_ext2fs.8 man-ext2fs-man .man
./usr/share/man/man8/fsck_ffs.8 man-sysutil-man .man

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.219 2006/09/07 00:50:45 ad Exp $
# $NetBSD: Makefile,v 1.220 2006/09/16 20:54:43 elad Exp $
# from: @(#)Makefile 5.20 (Berkeley) 6/12/93
.include <bsd.own.mk>
@ -21,7 +21,7 @@ SUBDIR= ac accton altq amd apm apmd arp bad144 bind bootp \
sliplogin sntp \
spray sti sunlabel sup syslogd tadpolectl tcpdchk \
tcpdmatch tcpdump timed tpctl traceroute trpt unlink \
usbdevs user videomode vipw vnconfig wiconfig wlanctl wpa \
usbdevs user videomode vipw veriexecgen vnconfig wiconfig wlanctl wpa \
wsconscfg wsfontload wsmoused wsmuxctl zdump zic
.if (${MKISCSI} != "no")

View File

@ -0,0 +1,11 @@
# $NetBSD: Makefile,v 1.1 2006/09/16 20:54:42 elad Exp $
.include <bsd.own.mk>
PROG= veriexecgen
MAN= veriexecgen.8
LDADD+=-lutil
DPADD+=${LIBUTIL}
CFLAGS+=-g
.include <bsd.prog.mk>

View File

@ -0,0 +1,117 @@
.\" $NetBSD: veriexecgen.8,v 1.1 2006/09/16 20:54:42 elad Exp $
.\"
.\" Copyright (c) 2006 The NetBSD Foundation, Inc.
.\" All rights reserved.
.\"
.\" This code is derived from software contributed to The NetBSD Foundation
.\" by Matt Fleming.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed by the NetBSD
.\" Foundation, Inc. and its contributors.
.\" 4. Neither the name of The NetBSD Foundation nor the names of its
.\" contributors may be used to endorse or promote products derived
.\" from this software without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd September 16, 2006
.Dt VERIEXECGEN 8
.Sh NAME
.Nm veriexecgen
.Nd generate fingerprints for Veriexec
.Sh SYNOPSIS
.Nm
.Op Fl Aarv
.Op Fl d Pa dir
.Op Fl o Pa fingerprintdb
.Op Fl t Ar algorithm
.Nm
.Op Fl h
.Sh DESCRIPTION
.Nm
can be used to create a fingerprint database for use with Veriexec.
.Pp
By default,
.Nm
will scan the current working directory for executable file, fingerprint them
using
.Dq sha256 ,
then write the data to
.Pa /etc/signatures .
.Pp
If the output file already exists,
.Nm
will save a backup copy in the same file only with a
.Dq .old
suffix.
.Pp
The following options are available:
.Bl -tag
.It Fl A
Append to the output file, don't overwrite it.
.It Fl a
Add fingerprints for non-executable files as well.
.It Fl D
Search system directories,
.Pa /bin ,
.Pa /sbin ,
.Pa /usr/bin ,
.Pa /usr/sbin ,
and
.Pa /usr/lib .
.Fl d
flag.
.It Fl d Ar dir
Scan for files in
.Ar dir .
Multiple uses of this flag can specify more than one directory.
.\" .It Fl F
.\" Try to guess the correct flags for every file.
.It Fl h
Display the help screen.
.It Fl o Ar fingerprintdb
Save the generated fingerprint database to
.Ar fingerprintdb .
.It Fl r
Scan recursively.
.It Fl t Ar algorithm
Use
.Ar algorithm
for the fingerprints.
Must be one of
.Dq md5 ,
.Dq sha1 ,
.Dq sha256 ,
.Dq sha384 ,
.Dq sha512 ,
or
.Dq rmd160 .
.It Fl v
Verbose mode.
Print messages describing what operations are being done.
.El
.\" .Sh EXAMPLES
.\" Foo.
.Sh SEE ALSO
.Xr veriexec 4 ,
.Xr veriexectl 8

View File

@ -0,0 +1,375 @@
/* $NetBSD: veriexecgen.c,v 1.1 2006/09/16 20:54:42 elad Exp $ */
/*-
* Copyright (c) 2006 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Matt Fleming.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/dirent.h>
#include <sys/verified_exec.h>
#include <err.h>
#include <errno.h>
#include <fts.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <util.h>
#include <md5.h>
#include <crypto/sha1.h>
#include <crypto/sha2.h>
#include <crypto/rmd160.h>
#define IS_EXEC(mode) ((mode) & (S_IXUSR | S_IXGRP | S_IXOTH))
#define DEFAULT_DBFILE "/etc/signatures"
#define DEFAULT_HASH "sha256"
#define DEFAULT_SYSPATHS { "/bin", "/sbin", "/usr/bin", "/usr/sbin", \
"/usr/lib", NULL }
struct fentry {
char filename[MAXPATHLEN];
char *hash_val;
int flags;
TAILQ_ENTRY(fentry) f;
};
TAILQ_HEAD(, fentry) fehead;
struct hash {
const char *hashname;
char *(*filefunc) (const char *, char *);
} hashes[] = {
{ "MD5", MD5File },
{ "SHA1", SHA1File },
{ "SHA256", SHA256_File },
{ "SHA384", SHA384_File },
{ "SHA512", SHA512_File },
{ "RMD160", RMD160File },
{ NULL, NULL },
};
int Aflag, aflag, Dflag, Fflag, rflag, vflag;
static void
usage(void)
{
(void)fprintf(stderr,
"usage: %s [-Aarv] [-D] [-d dir] [-o fingerprintdb]"
" [-t algorithm]\n", getprogname());
}
static void
banner(struct hash *hash_type, char **search_path)
{
int j;
(void)printf("Fingerprinting ");
for (j = 0; search_path[j] != NULL; j++)
(void)printf("%s ", search_path[j]);
(void)printf("(%s) (%s) using %s\n",
aflag ? "all files" : "executables only",
rflag ? "recursive" : "non-recursive",
hash_type->hashname);
}
static struct hash *
find_hash(char *hash_type)
{
struct hash *hash;
for (hash = hashes; hash->hashname != NULL; hash++)
if (strcasecmp(hash_type, hash->hashname) == 0)
return hash;
return NULL;
}
static char *
do_hash(char *filename, struct hash * h)
{
return h->filefunc(filename, NULL);
}
static int
figure_flags(char *path, mode_t mode)
{
#ifdef notyet
if (Fflag) {
/* Try to figure out right flag(s). */
return VERIEXEC_DIRECT;
}
#endif /* notyet */
if (!IS_EXEC(mode))
return VERIEXEC_FILE;
else
return 0;
}
static int
check_dup(char *filename)
{
struct fentry *lwalk;
TAILQ_FOREACH(lwalk, &fehead, f) {
if (strncmp(lwalk->filename, filename,
(unsigned long) MAXPATHLEN) == 0)
return 1;
}
return 0;
}
static void
add_new_entry(FTSENT *file, struct hash *hash)
{
struct fentry *e;
struct stat sb;
if (file->fts_info == FTS_SL) {
if (stat(file->fts_path, &sb) == -1)
err(1, "Cannot stat symlink");
} else
sb = *file->fts_statp;
if (!aflag && !Dflag && IS_EXEC(sb.st_mode))
return;
e = ecalloc(1UL, sizeof(*e));
if (realpath(file->fts_accpath, e->filename) == NULL)
err(1, "Cannot find absolute path");
if (check_dup(e->filename)) {
free(e);
return;
}
if ((e->hash_val = do_hash(e->filename, hash)) == NULL)
errx(1, "Cannot calculate hash");
e->flags = figure_flags(e->filename, sb.st_mode);
TAILQ_INSERT_TAIL(&fehead, e, f);
}
static void
walk_dir(char **search_path, struct hash *hash)
{
FTS *fh;
FTSENT *file;
if ((fh = fts_open(search_path, FTS_PHYSICAL, NULL)) == NULL)
err(1, "fts_open");
while ((file = fts_read(fh)) != NULL) {
if (!rflag && file->fts_level > 1) {
fts_set(fh, file, FTS_SKIP);
continue;
}
switch (file->fts_info) {
case FTS_D:
case FTS_DC:
case FTS_DP:
continue;
default:
break;
}
if (file->fts_errno) {
errx(1, "%s: %s", file->fts_path,
strerror(file->fts_errno));
}
add_new_entry(file, hash);
}
fts_close(fh);
}
static char *
flags2str(int flags)
{
if (flags != 0)
return "FILE";
else
return "";
}
static void
store_entries(char *dbfile, struct hash *hash)
{
FILE *fp;
int move = 1;
char old_dbfile[MAXPATHLEN];
time_t ct;
struct stat sb;
struct fentry *e;
if (stat(dbfile, &sb) != 0) {
if (errno == ENOENT)
move = 0;
else
err(1, "could not stat %s", dbfile);
}
if (move && !Aflag) {
if (vflag)
(void)printf("\nBacking up existing fingerprint file "
"to \"%s.old\"\n\n", dbfile);
if (snprintf(old_dbfile, MAXPATHLEN, "%s.old", dbfile) <
strlen(dbfile) + 4) {
err(1, "%s", old_dbfile);
}
if (rename(dbfile, old_dbfile) == -1)
err(1, "could not rename file");
}
fp = efopen(dbfile, Aflag ? "a" : "w+");
time(&ct);
(void)fprintf(fp, "# Generated by %s, %.24s\n",
getlogin(), ctime(&ct));
TAILQ_FOREACH(e, &fehead, f) {
if (vflag)
(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));
}
(void)fclose(fp);
if (!vflag)
return;
(void)printf("\n\n"
"#############################################################\n"
" PLEASE VERIFY CONTENTS OF %s AND FINE-TUNE THE\n"
" FLAGS WHERE APPROPRIATE AFTER READING veriexecctl(8)\n"
"#############################################################\n",
dbfile);
}
int
main(int argc, char **argv)
{
int ch, total = 0;
char *dbfile = NULL;
char **search_path = NULL;
struct hash *hash = NULL;
Aflag = aflag = Dflag = Fflag = rflag = vflag = 0;
while ((ch = getopt(argc, argv, "AaDd:ho:rt:v")) != -1) {
switch (ch) {
case 'A':
Aflag = 1;
break;
case 'a':
aflag = 1;
break;
case 'D':
Dflag = 1;
break;
case 'd':
search_path = erealloc(search_path, sizeof(char *) *
(total + 1));
search_path[total] = optarg;
search_path[++total] = NULL;
break;
#ifdef notyet
case 'F':
Fflag = 1;
break;
#endif /* notyet */
case 'h':
usage();
return 0;
case 'o':
dbfile = optarg;
break;
case 'r':
rflag = 1;
break;
case 't':
hash = find_hash(optarg);
break;
case 'v':
vflag = 1;
break;
default:
usage();
return 1;
}
}
if (dbfile == NULL)
dbfile = DEFAULT_DBFILE;
if (hash == NULL) {
if ((hash = find_hash(DEFAULT_HASH)) == NULL)
errx(1, "No hash algorithm");
}
TAILQ_INIT(&fehead);
if (search_path == NULL)
Dflag = 1;
if (Dflag) {
char *sys_paths[] = DEFAULT_SYSPATHS;
if (vflag)
banner(hash, sys_paths);
walk_dir(sys_paths, hash);
}
if (search_path != NULL) {
if (vflag)
banner(hash, search_path);
walk_dir(search_path, hash);
}
store_entries(dbfile, hash);
return 0;
}