okay, since there was no way to divide this to two commits, here it goes..

introduce fileassoc(9), a kernel interface for associating meta-data with
files using in-kernel memory. this is very similar to what we had in
veriexec till now, only abstracted so it can be used more easily by more
consumers.

this also prompted the redesign of the interface, making it work on vnodes
and mounts and not directly on devices and inodes. internally, we still
use file-id but that's gonna change soon... the interface will remain
consistent.

as a result, veriexec went under some heavy changes to conform to the new
interface. since we no longer use device numbers to identify file-systems,
the veriexec sysctl stuff changed too: kern.veriexec.count.dev_N is now
kern.veriexec.tableN.* where 'N' is NOT the device number but rather a
way to distinguish several mounts.

also worth noting is the plugging of unmount/delete operations
wrt/fileassoc and veriexec.

tons of input from yamt@, wrstuden@, martin@, and christos@.
This commit is contained in:
elad 2006-07-14 18:41:40 +00:00
parent a1c2fd0906
commit b5d09ef065
19 changed files with 1336 additions and 363 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: mi,v 1.898 2006/07/13 11:43:54 he Exp $
# $NetBSD: mi,v 1.899 2006/07/14 18:41:41 elad Exp $
./etc/mtree/set.comp comp-sys-root
./usr/bin/addr2line comp-debug-bin bfd
./usr/bin/ar comp-util-bin bfd
@ -5852,6 +5852,7 @@
./usr/share/man/cat9/ffree.0 comp-sys-catman .cat
./usr/share/man/cat9/fgetown.0 comp-sys-catman .cat
./usr/share/man/cat9/file.0 comp-sys-catman .cat
./usr/share/man/cat9/fileassoc.0 comp-sys-catman .cat
./usr/share/man/cat9/filedesc.0 comp-sys-catman .cat
./usr/share/man/cat9/firmload.0 comp-sys-catman .cat
./usr/share/man/cat9/firmware_close.0 comp-sys-catman .cat
@ -9980,6 +9981,7 @@
./usr/share/man/man9/ffree.9 comp-sys-man .man
./usr/share/man/man9/fgetown.9 comp-sys-man .man
./usr/share/man/man9/file.9 comp-sys-man .man
./usr/share/man/man9/fileassoc.9 comp-sys-man .man
./usr/share/man/man9/filedesc.9 comp-sys-man .man
./usr/share/man/man9/firmload.9 comp-sys-man .man
./usr/share/man/man9/firmware_close.9 comp-sys-man .man

View File

@ -1,4 +1,4 @@
.\" $NetBSD: sysctl.3,v 1.170 2006/05/16 00:08:24 elad Exp $
.\" $NetBSD: sysctl.3,v 1.171 2006/07/14 18:41:40 elad Exp $
.\"
.\" Copyright (c) 1993
.\" The Regents of the University of California. All rights reserved.
@ -29,7 +29,7 @@
.\"
.\" @(#)sysctl.3 8.4 (Berkeley) 5/9/95
.\"
.Dd April 26, 2006
.Dd July 14, 2006
.Dt SYSCTL 3
.Os
.Sh NAME
@ -953,16 +953,12 @@ The variables are as follows:
Returns a string with the supported algorithms in Verified Exec.
.It Li VERIEXEC_COUNT
Variables are added to this node as new hash tables are created to
contain Verified Exec data for a new device.
Each variable in the node
will have a name in the form of
.No dev_ Ns Aq id
where
.Aq id
is the device id.
For example, the variable for the root device may be dev_0.
The value of this
variable will be the amount of fingerprinted files on the device.
contain Veriexec data for a new mount.
Each mount will be under its own
.No tableN
node.
Under each node there will be three variables, indicating the mount
point, the file-system type, and the number of entries.
.It Li VERIEXEC_STRICT
Controls the strict level of Verified Exec.
The strict level defines how

View File

@ -1,4 +1,4 @@
.\" $NetBSD: sysctl.8,v 1.147 2006/05/29 19:35:31 liamjfoy Exp $
.\" $NetBSD: sysctl.8,v 1.148 2006/07/14 18:41:40 elad Exp $
.\"
.\" Copyright (c) 2004 The NetBSD Foundation, Inc.
.\" All rights reserved.
@ -61,7 +61,7 @@
.\"
.\" @(#)sysctl.8 8.1 (Berkeley) 6/6/93
.\"
.Dd May 29, 2006
.Dd July 14, 2006
.Dt SYSCTL 8
.Os
.Sh NAME
@ -371,7 +371,10 @@ privilege can change the value.
.It kern.veriexec.verbose integer yes
.It kern.veriexec.strict integer raise only
.It kern.veriexec.algorithms string no
.It kern.veriexec.count.dev_\*[Lt]id\*[Gt] quad no
.It kern.veriexec.count.table\*[Lt]N\*[Gt] quad no
.It kern.veriexec.count.table\*[Lt]N\*[Gt].mntpt string no
.It kern.veriexec.count.table\*[Lt]N\*[Gt].fstype string no
.It kern.veriexec.count.table\*[Lt]N\*[Gt].nentries quad no
.It kern.version string no
.It kern.vnode struct no
.It machdep.console_device dev_t no

View File

@ -1,4 +1,4 @@
/* $NetBSD: veriexecctl.c,v 1.20 2005/12/13 10:56:16 dsl Exp $ */
/* $NetBSD: veriexecctl.c,v 1.21 2006/07/14 18:41:40 elad Exp $ */
/*-
* Copyright 2005 Elad Efrat <elad@bsd.org.il>
@ -73,27 +73,29 @@ openlock(const char *path)
}
struct veriexec_up *
dev_lookup(dev_t d)
/* dev_lookup(dev_t d) */
dev_lookup(char *vfs)
{
struct veriexec_up *p;
CIRCLEQ_FOREACH(p, &params_list, vu_list)
if (p->vu_param.dev == d)
if (strcmp(p->vu_param.file, vfs) == 0)
return (p);
return NULL;
}
struct veriexec_up *
dev_add(dev_t d)
/* dev_add(dev_t d) */
dev_add(dev_t d, char *vfs)
{
struct veriexec_up *up;
if ((up = calloc((size_t)1, sizeof(*up))) == NULL)
err(1, "No memory");
up->vu_param.dev = d;
up->vu_param.hash_size = 1;
strlcpy(up->vu_param.file, vfs, sizeof(up->vu_param.file));
CIRCLEQ_INSERT_TAIL(&params_list, up, vu_list);
@ -115,13 +117,13 @@ phase1_preload(void)
if (ioctl(gfd, VERIEXEC_TABLESIZE, &(vup->vu_param)) == -1) {
if (errno != EEXIST)
err(1, "Error in phase 1: Can't "
"set hash table size for device %d",
vup->vu_param.dev);
"set hash table size for mount %s",
vup->vu_param.file);
}
if (verbose) {
printf(" => Hash table sizing successful for device "
"%d. (%zu entries)\n", vup->vu_param.dev,
printf(" => Hash table sizing successful for mount "
"%s. (%zu entries)\n", vup->vu_param.file,
vup->vu_param.hash_size);
}
@ -302,19 +304,15 @@ main(int argc, char **argv)
if (stat(argv[1], &sb) == -1)
err(1, "Can't stat `%s'", argv[1]);
strlcpy(dp.file, argv[1], sizeof(dp.file));
/*
* If it's a regular file, remove it. If it's a directory,
* remove the entire table. If it's neither, abort.
*/
if (S_ISDIR(sb.st_mode))
dp.ino = 0;
else if (S_ISREG(sb.st_mode))
dp.ino = sb.st_ino;
else
if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode))
errx(1, "`%s' is not a regular file or directory.", argv[1]);
dp.dev = sb.st_dev;
if (ioctl(gfd, VERIEXEC_DELETE, &dp) == -1)
err(1, "Error deleting `%s'", argv[1]);
} else if (argc == 2 && strcasecmp(argv[0], "query") == 0) {
@ -331,6 +329,8 @@ main(int argc, char **argv)
if (!S_ISREG(sb.st_mode))
errx(1, "`%s' is not a regular file.", argv[1]);
strlcpy(qp.file, argv[1], sizeof(qp.file));
qp.ino = sb.st_ino;
qp.dev = sb.st_dev;
memset(fp, 0, sizeof(fp));

View File

@ -1,4 +1,4 @@
/* $NetBSD: veriexecctl.h,v 1.5 2005/12/12 21:47:58 elad Exp $ */
/* $NetBSD: veriexecctl.h,v 1.6 2006/07/14 18:41:40 elad Exp $ */
/*-
* Copyright 2005 Elad Efrat <elad@bsd.org.il>
@ -56,8 +56,12 @@ int yywrap(void);
int yylex(void);
int yyparse(void);
void yyerror(const char *);
#if 0
struct veriexec_up *dev_lookup(dev_t);
struct veriexec_up *dev_add(dev_t);
#endif
struct veriexec_up *dev_lookup(char *);
struct veriexec_up *dev_add(dev_t, char *);
void phase2_load(void);
#endif /* _VERIEXECCTL_H_ */

View File

@ -1,5 +1,5 @@
%{
/* $NetBSD: veriexecctl_parse.y,v 1.13 2005/10/05 13:48:48 elad Exp $ */
/* $NetBSD: veriexecctl_parse.y,v 1.14 2006/07/14 18:41:40 elad Exp $ */
/*-
* Copyright 2005 Elad Efrat <elad@bsd.org.il>
@ -71,6 +71,7 @@ statement : /* empty */
goto phase_2_end;
}
#if 1
if (stat(params.file, &sb) == -1) {
warnx("Line %lu: Can't stat `%s'",
(unsigned long)line, params.file);
@ -83,8 +84,10 @@ statement : /* empty */
(unsigned long)line, params.file);
goto phase_2_end;
}
#endif
if ((p = dev_lookup(sb.st_dev)) != NULL) {
/* if ((p = dev_lookup(sb.st_dev)) != NULL) { */
if ((p = dev_lookup(params.file)) != NULL) {
(p->vu_param.hash_size)++;
goto phase_2_end;
}
@ -97,7 +100,7 @@ statement : /* empty */
(void)printf( " => Adding device ID %d. (%s)\n",
sb.st_dev, sf.f_mntonname);
}
dev_add(sb.st_dev);
dev_add(sb.st_dev, params.file);
phase_2_end:
(void)memset(&params, 0, sizeof(params));
}

View File

@ -1,4 +1,4 @@
.\" $NetBSD: options.4,v 1.324 2006/07/11 16:22:23 jschauma Exp $
.\" $NetBSD: options.4,v 1.325 2006/07/14 18:41:40 elad Exp $
.\"
.\" Copyright (c) 1996
.\" Perry E. Metzger. All rights reserved.
@ -30,7 +30,7 @@
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\"
.Dd May 19, 2006
.Dd July 14, 2006
.Os
.Dt OPTIONS 4
.Sh NAME
@ -1460,6 +1460,13 @@ A supplement to XSERVER that adds support for entering
.Xr ddb 4
while in
.Tn X11 .
.It Cd options FILEASSOC
Support for
.Xr fileassoc 9 .
.It Cd options FILEASSOC_NHOOKS=integer
Number of storage slots per file for
.Xr fileassoc 9 .
Default is 4.
.El
.Ss Networking Options
.Bl -ohang

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.204 2006/07/08 17:17:31 skrll Exp $
# $NetBSD: Makefile,v 1.205 2006/07/14 18:41:41 elad Exp $
# Makefile for section 9 (kernel function and variable) manual pages.
@ -15,7 +15,7 @@ MAN= altq.9 arc4random.9 arp.9 audio.9 autoconf.9 \
delay.9 disk.9 ddc.9 disklabel.9 dofileread.9 \
dopowerhooks.9 do_setresuid.9 doshutdownhooks.9 driver.9 \
edid.9 errno.9 ethersubr.9 evcnt.9 extattr.9 extent.9 \
fetch.9 file.9 filedesc.9 firmload.9 fork1.9 fsetown.9 \
fetch.9 file.9 fileassoc.9 filedesc.9 firmload.9 fork1.9 fsetown.9 \
getiobuf.9 \
hash.9 hashinit.9 hardclock.9 humanize_number.9 hz.9 \
ieee80211.9 ieee80211_crypto.9 ieee80211_input.9 ieee80211_ioctl.9 \

390
share/man/man9/fileassoc.9 Normal file
View File

@ -0,0 +1,390 @@
.\" $NetBSD: fileassoc.9,v 1.1 2006/07/14 18:41:41 elad Exp $
.\"
.\" Copyright (c) 2006 Elad Efrat <elad@NetBSD.org>
.\" All rights reserved.
.\"
.\" 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 Elad Efrat.
.\" 4. The name of the author may not be used to endorse or promote products
.\" derived from this software without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 July 14, 2006
.Dt FILEASSOC 9
.Os
.Sh NAME
.Nm fileassoc
.Nd in-kernel, file-system independent, file-meta data association
.Sh SYNOPSIS
.In sys/fileassoc.h
.Sh DESCRIPTION
The
.Nm
KPI allows association of meta-data with files independent of file-system
support for such elaborate meta-data.
.Pp
A system can have a maximum number of
.Dq FILEASSOC_NHOOKS
fileassocs associated with each file.
.Pp
When plugging a new fileassoc to the system, a developer can specify private
data to be associated with every file, as well as (potentially different)
private data to be associated with every file-system mount.
.Pp
For example, a developer might choose to associate a custom ACL with every
file, and a count of total files with ACLs with the mount.
.Ss Kernel Programming Interface
Designed with simplicity in mind, the
.Nm
KPI usually accepts four different types of parameters to the most commonly
used routines:
.Bl -tag -width "123456"
.It Ft struct mount * Ar mp
Describing a mount on which to take action.
.It Ft struct vnode * Ar vp
Describing a file on which to take action.
.It Ft fileassoc_t Ar id
Describing a id, as returned from a successful call to
.Fn fileassoc_register .
.It Ft void * Ar data
Describing a custom private data block, attached to either a file or a mount.
.El
.Pp
Before using the
.Nm
KPI it is important to keep in mind that the interface provides memory
management only for
.Nm
internal memory.
Any additional memory stored in the tables (such as private data-structures
used by custom fileassocs) should be allocated and freed by the developer.
.Pp
.Nm
provides the ability to specify a
.Dq cleanup
routine to
.Fn fileassoc_register
(see below)
to be called whenever an entry for a file or a mount is deleted.
.Pp
.Ss Fileassoc Registration and Deregistration Routines
These routines allow a developer to allocate a
.Nm
slot to be used for private data.
.Bl -tag -width "123456"
.It Ft int Fn fileassoc_register "const char *name" "fileassoc_cleanup_cb cleanup_cb"
Registers a new fileassoc as
.Ar name ,
and returns an
.Ft int
to be used as a metahhook-id in subsequent calls to the
.Nm
subsystem to identify the fileassoc, or -1 on failure.
.Pp
If
.Ar cleanup_cb
is not
.Dq NULL ,
it will be called during delete/clear operations (see routines below) with
indication whether the passed data is file- or mount-specific.
.Pp
.Ar cleanup_cb
should be a function receiving a
.Ft void *
and an
.Ft int ,
returning
.Ft void .
See EXAMPLES section for illustration.
.Pp
.It Ft int Fn fileassoc_deregister "fileassoc_t id"
Deregisters a
.Nm fileassoc
whose id is
.Ar id .
.Pp
Note that calling
.Fn fileassoc_deregister
only frees the associated slot in the
.Nm
subsystem.
It is up to the developer to take care of garbage collection.
.El
.Ss Lookup Routines
These routines allow lookup of
.Nm
mounts, files, and private data attached to them.
.Bl -tag -width "123456"
.It Ft struct fileassoc_table * Fn fileassoc_table_lookup "struct mount *mp"
Return the fileassoc table for
.Ar mp
or
.Dq NULL
if not found.
.Pp
.It Ft void * Fn fileassoc_tabledata_lookup "struct mount *mp" "fileassoc_t id"
Return table-wide private data in
.Ar mp
for
.Ar id .
.Pp
.It Ft struct fileassoc_hash_entry * Fn fileassoc_file_lookup "struct vnode *vp"
Return the fileassoc file entry for
.Ar vp
or
.Dq NULL
if not found.
.Pp
.It Ft void * Fn fileassoc_lookup "struct vnode *vp" "fileassoc_t id"
Returns the private data for the file/id combination
or
.Dq NULL
if not found.
.El
.Ss Mount-wide Routines
.Bl -tag -width "123456"
.It Ft int Fn fileassoc_table_add "struct mount *mp" "size_t size"
Creates a new fileassoc table for
.Ar mp
with at least
.Ar size
slots.
.Pp
.It Ft int Fn fileassoc_table_delete "struct mount *mp"
Deletes a fileassoc table for
.Ar mp .
.Pp
If specified, the fileassoc's
.Dq cleanup routine
will be called with a pointer to the private data-structure and indication of
.Dq FILEASSOC_CLEANUP_TABLE .
.Pp
.It Ft int Fn fileassoc_table_clear "struct mount *mp" "fileassoc_t id"
Clear all table entries for
.Ar fileassoc
from
.Ar mp .
.Pp
If specified, the fileassoc's
.Dq cleanup routine
will be called with a pointer to the private data-structure and indication of
either
.Dq FILEASSOC_CLEANUP_FILE
or
.Dq FILEASSOC_CLEANUP_TABLE
as appropriate.
.Pp
.It Ft int Fn fileassoc_tabledata_add "struct mount *mp" "fileassoc_t id" "void *data"
Add table-wide fileassoc-specific data in
.Ar data
to
.Ar mp
for
.Ar id .
.Pp
.It Ft int Fn fileassoc_tabledata_clear "struct mount *mp" "fileassoc_t id"
Clear table-wide fileassoc-specific data in
.Ar mp
for
.Ar id .
.El
.Ss File-specific Routines
.Bl -tag -width "123456"
.It Ft struct fileassoc_hash_entry * Fn fileassoc_file_add "struct vnode *vp"
Add a
.Nm
entry for the file.
All fileassoc-specific slots for the file are set to
.Dq NULL .
.Pp
.It Ft int Fn fileassoc_file_delete "struct vnode *vp"
Delete the fileassoc entry for
.Ar vp .
.Pp
If specified, the fileassoc's
.Dq cleanup routine
will be called with a pointer to the private data-structure and indication of
.Dq FILEASSOC_CLEANUP_FILE .
.El
.Ss Fileassoc-specific Routines
.Bl -tag -width "123456"
.It Ft int Fn fileassoc_add "struct vnode *vp" "fileassoc_t id" "void *data"
Add private data in
.Ar data
for
.Ar vp ,
for the fileassoc specified by
.Ar id .
.Pp
.It Ft int Fn fileassoc_clear "struct vnode *vp" "fileassoc_t id"
Clear the private data for
.Ar vp ,
for the fileassoc specified by
.Ar id .
.Pp
If specified, the fileassoc's
.Dq cleanup routine
will be called with a pointer to the private data-structure and indication of
.Dq FILEASSOC_CLEANUP_FILE .
.El
.Ss Misc. Routines
.Bl -tag -width "123456"
.It Ft void Fn fileassoc_init "void"
Initializes the
.Nm
subsystem.
.Fn fileassoc_init
is called once during system boot.
.El
.Sh EXAMPLES
The following code examples should give you a clue on using
.Nm
for your purposes.
.Pp
First, we'll begin with registering a new id.
We need to do that to save a slot for private data storage with each mount
and/or file:
.Bd -literal -offset indent
fileassoc_t myhook_id;
myhook_id = fileassoc_register("my_hook", myhook_cleanup);
if (myhook_id == -1)
...handle error...
.Ed
.Pp
In the above example we pass a
.Fn myhook_cleanup
routine.
It could look something like this:
.Bd -literal -offset indent
void
myhook_cleanup(void *data, int what)
{
if (what == FILEASSOC_CLEANUP_FILE) {
printf("Myhook: Removing entry for file.\n");
...handle file entry removal...
free(data, M_TEMP);
} else if (what == FILEASSOC_CLEANUP_TABLE) {
printf("Myhook: Removing entry for mount.\n");
...handle mount entry removal...
free(data, M_TEMP);
}
}
.Ed
.Pp
Another useful thing would be to add our private data to an file.
For example, let's assume we keep a custom ACL with each file:
.Bd -literal -offset indent
int
myhook_acl_add(struct vattr *va, struct myhook_acl *acl)
{
int error;
error = fileassoc_add(va->va_fsid, va->va_fileid, myhook_id, acl);
if (error) {
printf("Myhook: Could not add ACL.\n");
...handle error...
}
printf("Myhook: Added ACL.\n");
return (0);
}
.Ed
.Pp
Adding an entry will override any entry that previously exists.
.Pp
The above can fail, normally, when there is no table for the mount.
Creating a new table is simple:
.Bd -literal -offset indent
int error;
error = fileassoc_table_add(va->va_fsid, nentries);
if (error)
...handle error...
.Ed
.Pp
The second argument,
.Ar nentries ,
should be approximately the number of files it is predicted that will
have entries in the table, although you can provide a pseudo-safe constant
value. (like 128, for example)
.Pp
Whatever your plug is, eventually you'll want to access the private data you
store with each file.
To do that you can use the following:
.Bd -literal -offset indent
int
myhook_acl_access(struct vattr *va, int access_flags)
{
struct myhook_acl *acl;
acl = fileassoc_lookup(va->va_fsid, va->va_fileid, myhook_id);
if (acl == NULL)
return (0);
error = myhook_acl_eval(acl, access_flags);
if (error) {
printf("Myhook: Denying access based on ACL decision.\n");
return (error);
}
return (0);
}
.Ed
.Pp
And, in some cases, it may be desired to remove private data associated with
an file:
.Bd -literal -offset indent
int error;
error = fileassoc_clear(va->va_fsid, va->va_fileid, myhook_id);
if (error) {
printf("Myhook: Error occured during fileassoc removal.\n");
...handle error...
}
.Ed
.Pp
As mentioned previously, the call to
.Fn fileassoc_clear
will result in a call to the
.Dq cleanup routine
specified in the initial call to
.Fn fileassoc_register .
.Pp
The above should be enough to get you started.
.Pp
For example usage of
.Nm ,
see the Veriexec code.
.Sh CODE REFERENCES
.Pa src/sys/kern/kern_verifiedexec.c
.Sh HISTORY
The
.Nm
KPI first appeared in
.Nx 5.0 .
.Sh AUTHOR
.An Elad Efrat Aq elad@NetBSD.org
designed and implemented the
.Nm
KPI.

View File

@ -1,4 +1,4 @@
# $NetBSD: files,v 1.788 2006/07/13 22:56:02 gdamore Exp $
# $NetBSD: files,v 1.789 2006/07/14 18:41:40 elad Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@ -79,6 +79,9 @@ defflag opt_verified_exec.h VERIFIED_EXEC
defparam opt_pax.h PAX_MPROTECT
defflag opt_fileassoc.h FILEASSOC
defparam opt_fileassoc.h FILEASSOC_NHOOKS
# compatibility options
#
defflag opt_compat_netbsd.h COMPAT_30
@ -1133,8 +1136,8 @@ file dev/kttcp.c kttcp needs-flag
# Verified exec fingerprint loader pseudo-device
defpseudo veriexec
file kern/kern_verifiedexec.c veriexec needs-flag
file dev/verified_exec.c veriexec needs-flag
file kern/kern_verifiedexec.c veriexec & fileassoc needs-flag
file dev/verified_exec.c veriexec & fileassoc needs-flag
# isochronous pseudo device for IEEE 1394, i.LINK or FireWire
defpseudo fwiso: ieee1394
@ -1241,6 +1244,7 @@ file kern/kern_lock.c
file kern/kern_lwp.c
file kern/kern_malloc.c
file kern/kern_malloc_debug.c malloc_debug
file kern/kern_fileassoc.c fileassoc needs-flag
file kern/kern_ntptime.c
file kern/kern_pax.c pax_mprotect
file kern/kern_physio.c

View File

@ -1,4 +1,4 @@
/* $NetBSD: verified_exec.c,v 1.34 2006/05/25 11:24:00 blymn Exp $ */
/* $NetBSD: verified_exec.c,v 1.35 2006/07/14 18:41:40 elad Exp $ */
/*-
* Copyright 2005 Elad Efrat <elad@bsd.org.il>
@ -31,9 +31,9 @@
#include <sys/cdefs.h>
#if defined(__NetBSD__)
__KERNEL_RCSID(0, "$NetBSD: verified_exec.c,v 1.34 2006/05/25 11:24:00 blymn Exp $");
__KERNEL_RCSID(0, "$NetBSD: verified_exec.c,v 1.35 2006/07/14 18:41:40 elad Exp $");
#else
__RCSID("$Id: verified_exec.c,v 1.34 2006/05/25 11:24:00 blymn Exp $\n$NetBSD: verified_exec.c,v 1.34 2006/05/25 11:24:00 blymn Exp $");
__RCSID("$Id: verified_exec.c,v 1.35 2006/07/14 18:41:40 elad Exp $\n$NetBSD: verified_exec.c,v 1.35 2006/07/14 18:41:40 elad Exp $");
#endif
#include <sys/param.h>
@ -64,8 +64,11 @@ __RCSID("$Id: verified_exec.c,v 1.34 2006/05/25 11:24:00 blymn Exp $\n$NetBSD: v
#include <sys/verified_exec.h>
#include <sys/kauth.h>
#include <sys/fileassoc.h>
/* count of number of times device is open (we really only allow one open) */
static unsigned int veriexec_dev_usage;
static unsigned int veriexec_tablecount = 0;
struct veriexec_softc {
DEVPORT_DEVICE veriexec_dev;
@ -171,7 +174,7 @@ veriexecioctl(dev_t dev __unused, u_long cmd, caddr_t data,
switch (cmd) {
case VERIEXEC_TABLESIZE:
error = veriexec_newtable((struct veriexec_sizing_params *)
data);
data, l);
break;
case VERIEXEC_LOAD:
@ -179,11 +182,11 @@ veriexecioctl(dev_t dev __unused, u_long cmd, caddr_t data,
break;
case VERIEXEC_DELETE:
error = veriexec_delete((struct veriexec_delete_params *)data);
error = veriexec_delete((struct veriexec_delete_params *)data, l);
break;
case VERIEXEC_QUERY:
error = veriexec_query((struct veriexec_query_params *)data);
error = veriexec_query((struct veriexec_query_params *)data, l);
break;
default:
@ -207,44 +210,57 @@ veriexec_drvinit(void *unused __unused)
SYSINIT(veriexec, SI_SUB_PSEUDO, SI_ORDER_ANY, veriexec_drvinit, NULL);
#endif
/*
* Create a new Veriexec table.
*/
int
veriexec_newtable(struct veriexec_sizing_params *params)
veriexec_newtable(struct veriexec_sizing_params *params, struct lwp *l)
{
struct veriexec_hashtbl *tbl;
u_char node_name[16];
u_long hashmask;
struct veriexec_table_entry *vte;
struct nameidata nid;
u_char buf[16];
int error;
/* Check for existing table for device. */
if (veriexec_tblfind(params->dev) != NULL)
return (EEXIST);
NDINIT(&nid, LOOKUP, FOLLOW, UIO_SYSSPACE, params->file, l);
error = namei(&nid);
if (error)
return (error);
/* Allocate and initialize a Veriexec hash table. */
tbl = malloc(sizeof(*tbl), M_TEMP, M_WAITOK);
tbl->hash_size = params->hash_size;
tbl->hash_dev = params->dev;
tbl->hash_tbl = hashinit(params->hash_size, HASH_LIST, M_TEMP,
M_WAITOK, &hashmask);
tbl->hash_count = 0;
error = fileassoc_table_add(nid.ni_vp->v_mount, params->hash_size);
if (error && (error != EEXIST))
goto out;
LIST_INSERT_HEAD(&veriexec_tables, tbl, hash_list);
vte = malloc(sizeof(*vte), M_TEMP, M_WAITOK | M_ZERO);
error = fileassoc_tabledata_add(nid.ni_vp->v_mount, veriexec_hook, vte);
#ifdef DIAGNOSTIC
if (error)
panic("Fileassoc: Inconsistency after adding table");
#endif /* DIAGNOSTIC */
snprintf(node_name, sizeof(node_name), "dev_%u",
tbl->hash_dev);
snprintf(buf, sizeof(buf), "table%ud", veriexec_tablecount++);
sysctl_createv(NULL, 0, &veriexec_count_node, &vte->vte_node,
0, CTLTYPE_NODE, "table0", NULL, NULL, 0, NULL,
0, CTL_CREATE, CTL_EOL);
sysctl_createv(NULL, 0, &veriexec_count_node, NULL,
CTLFLAG_READONLY, CTLTYPE_QUAD, node_name,
NULL, NULL, 0, &tbl->hash_count, 0,
tbl->hash_dev, CTL_EOL);
sysctl_createv(NULL, 0, &vte->vte_node, NULL,
CTLFLAG_READONLY, CTLTYPE_STRING, "mntpt",
NULL, NULL, 0, nid.ni_vp->v_mount->mnt_stat.f_mntonname, 0, CTL_CREATE, CTL_EOL);
sysctl_createv(NULL, 0, &vte->vte_node, NULL,
CTLFLAG_READONLY, CTLTYPE_STRING, "fstype",
NULL, NULL, 0, nid.ni_vp->v_mount->mnt_stat.f_fstypename, 0, CTL_CREATE, CTL_EOL);
sysctl_createv(NULL, 0, &vte->vte_node, NULL,
CTLFLAG_READONLY, CTLTYPE_QUAD, "nentries",
NULL, NULL, 0, &vte->vte_count, 0, CTL_CREATE, CTL_EOL);
return (0);
out:
vrele(nid.ni_vp);
return (error);
}
int
veriexec_load(struct veriexec_params *params, struct lwp *l)
{
struct veriexec_hashtbl *tbl;
struct veriexec_hash_entry *hh;
struct veriexec_hash_entry *e;
struct veriexec_file_entry *hh, *e;
struct nameidata nid;
struct vattr va;
int error;
@ -262,7 +278,7 @@ veriexec_load(struct veriexec_params *params, struct lwp *l)
return (EINVAL);
}
/* Get attributes for device and inode. */
/* Get attributes for device and fileid. */
error = VOP_GETATTR(nid.ni_vp, &va, l->l_proc->p_cred, l);
if (error) {
vrele(nid.ni_vp);
@ -272,13 +288,7 @@ veriexec_load(struct veriexec_params *params, struct lwp *l)
/* Release our reference to the vnode. (namei) */
vrele(nid.ni_vp);
/* Get table for the device. */
tbl = veriexec_tblfind(va.va_fsid);
if (tbl == NULL) {
return (EINVAL);
}
hh = veriexec_lookup(va.va_fsid, va.va_fileid);
hh = veriexec_lookup(nid.ni_vp);
if (hh != NULL) {
/*
* Duplicate entry means something is wrong in
@ -302,7 +312,6 @@ veriexec_load(struct veriexec_params *params, struct lwp *l)
}
e = malloc(sizeof(*e), M_TEMP, M_WAITOK);
e->inode = va.va_fileid;
e->type = params->type;
e->status = FINGERPRINT_NOTEVAL;
e->page_fp = NULL;
@ -313,7 +322,7 @@ veriexec_load(struct veriexec_params *params, struct lwp *l)
free(e, M_TEMP);
printf("Veriexec: veriexecioctl: Invalid or unknown "
"fingerprint type \"%s\" for file \"%s\" "
"(dev=%ld, inode=%llu)\n", params->fp_type,
"(dev=%ld, fileid=%llu)\n", params->fp_type,
params->file, va.va_fsid,
(unsigned long long)va.va_fileid);
return(EINVAL);
@ -330,7 +339,7 @@ veriexec_load(struct veriexec_params *params, struct lwp *l)
if (e->ops->hash_len != params->size) {
printf("Veriexec: veriexecioctl: Inconsistent "
"fingerprint size for type \"%s\" for file "
"\"%s\" (dev=%ld, inode=%llu), size was %u "
"\"%s\" (dev=%ld, fileid=%llu), size was %u "
"was expecting %zu\n", params->fp_type,
params->file, va.va_fsid,
(unsigned long long)va.va_fileid,
@ -347,90 +356,109 @@ veriexec_load(struct veriexec_params *params, struct lwp *l)
REPORT_VERBOSE_HIGH, REPORT_NOALARM,
REPORT_NOPANIC);
error = veriexec_hashadd(tbl, e);
error = veriexec_hashadd(nid.ni_vp, e);
if (error)
return (error);
return (error);
}
int
veriexec_delete(struct veriexec_delete_params *params)
void
veriexec_clear(void *data, int file_specific)
{
struct veriexec_hashtbl *tbl;
struct veriexec_hash_entry *vhe;
if (file_specific) {
struct veriexec_file_entry *vfe = data;
/* Delete an entire table */
if (params->ino == 0) {
struct veriexec_hashhead *tbl_list;
u_long i;
tbl = veriexec_tblfind(params->dev);
if (tbl == NULL)
return (ENOENT);
/* Remove all entries from the table and lists */
tbl_list = tbl->hash_tbl;
for (i = 0; i < tbl->hash_size; i++) {
while (LIST_FIRST(&tbl_list[i]) != NULL) {
vhe = LIST_FIRST(&tbl_list[i]);
if (vhe->fp != NULL)
free(vhe->fp, M_TEMP);
if (vhe->page_fp != NULL)
free(vhe->page_fp, M_TEMP);
LIST_REMOVE(vhe, entries);
free(vhe, M_TEMP);
}
}
/* Remove hash table and sysctl node */
hashdone(tbl->hash_tbl, M_TEMP);
LIST_REMOVE(tbl, hash_list);
sysctl_destroyv(__UNCONST(veriexec_count_node), params->dev,
CTL_EOL);
if (vfe->fp != NULL)
free(vfe->fp, M_TEMP);
if (vfe->page_fp != NULL)
free(vfe->page_fp, M_TEMP);
free(vfe, M_TEMP);
} else {
tbl = veriexec_tblfind(params->dev);
if (tbl == NULL)
return (ENOENT);
struct veriexec_table_entry *vte = data;
vhe = veriexec_lookup(params->dev, params->ino);
if (vhe != NULL) {
if (vhe->fp != NULL)
free(vhe->fp, M_TEMP);
if (vhe->page_fp != NULL)
free(vhe->page_fp, M_TEMP);
LIST_REMOVE(vhe, entries);
free(vhe, M_TEMP);
tbl->hash_count--;
free(vte, M_TEMP);
}
}
return (0);
}
int
veriexec_query(struct veriexec_query_params *params)
veriexec_delete(struct veriexec_delete_params *params, struct lwp *l)
{
struct veriexec_hash_entry *vhe;
struct veriexec_table_entry *vte;
struct nameidata nid;
int error;
vhe = veriexec_lookup(params->dev, params->ino);
if (vhe == NULL)
return (ENOENT);
NDINIT(&nid, LOOKUP, FOLLOW, UIO_SYSSPACE, params->file, l);
error = namei(&nid);
if (error)
return (error);
params->type = vhe->type;
params->status = vhe->status;
params->hash_len = vhe->ops->hash_len;
strlcpy(params->fp_type, vhe->ops->type, sizeof(params->fp_type));
memcpy(params->fp_type, vhe->ops->type, sizeof(params->fp_type));
vte = veriexec_tblfind(nid.ni_vp);
if (vte == NULL) {
error = ENOENT;
goto out;
}
/* XXX this should either receive the filename to remove OR a mount point! */
/* Delete an entire table */
if (nid.ni_vp->v_type == VDIR) {
sysctl_free(__UNCONST(vte->vte_node));
veriexec_tablecount--;
error = fileassoc_table_clear(nid.ni_vp->v_mount, veriexec_hook);
if (error)
goto out;
} else if (nid.ni_vp->v_type == VREG) {
error = fileassoc_clear(nid.ni_vp, veriexec_hook);
if (error)
goto out;
vte->vte_count--;
}
out:
vrele(nid.ni_vp);
return (error);
}
int
veriexec_query(struct veriexec_query_params *params, struct lwp *l)
{
struct veriexec_file_entry *vfe;
struct nameidata nid;
int error;
NDINIT(&nid, LOOKUP, FOLLOW, UIO_SYSSPACE, params->file, l);
error = namei(&nid);
if (error)
return (error);
vfe = veriexec_lookup(nid.ni_vp);
if (vfe == NULL) {
error = ENOENT;
goto out;
}
params->type = vfe->type;
params->status = vfe->status;
params->hash_len = vfe->ops->hash_len;
strlcpy(params->fp_type, vfe->ops->type, sizeof(params->fp_type));
memcpy(params->fp_type, vfe->ops->type, sizeof(params->fp_type));
error = copyout(params, params->uaddr, sizeof(*params));
if (error)
return (error);
if (params->fp_bufsize >= vhe->ops->hash_len) {
error = copyout(vhe->fp, params->fp, vhe->ops->hash_len);
goto out;
if (params->fp_bufsize >= vfe->ops->hash_len) {
error = copyout(vfe->fp, params->fp, vfe->ops->hash_len);
if (error)
return (error);
goto out;
} else
error = ENOMEM;
out:
vrele(nid.ni_vp);
return (error);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: init_main.c,v 1.270 2006/07/01 05:41:10 kardel Exp $ */
/* $NetBSD: init_main.c,v 1.271 2006/07/14 18:41:40 elad Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1991, 1992, 1993
@ -71,7 +71,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: init_main.c,v 1.270 2006/07/01 05:41:10 kardel Exp $");
__KERNEL_RCSID(0, "$NetBSD: init_main.c,v 1.271 2006/07/14 18:41:40 elad Exp $");
#include "opt_ipsec.h"
#include "opt_kcont.h"
@ -82,6 +82,7 @@ __KERNEL_RCSID(0, "$NetBSD: init_main.c,v 1.270 2006/07/01 05:41:10 kardel Exp $
#include "opt_syscall_debug.h"
#include "opt_sysv.h"
#include "opt_verified_exec.h"
#include "opt_fileassoc.h"
#include "rnd.h"
@ -150,6 +151,10 @@ __KERNEL_RCSID(0, "$NetBSD: init_main.c,v 1.270 2006/07/01 05:41:10 kardel Exp $
#include <sys/sa.h>
#include <sys/syscallargs.h>
#ifdef FILEASSOC
#include <sys/fileassoc.h>
#endif /* FILEASSOC */
#include <ufs/ufs/quota.h>
#include <miscfs/genfs/genfs.h>
@ -330,6 +335,10 @@ main(void)
/* Initialize kauth. */
kauth_init();
#ifdef FILEASSOC
fileassoc_init();
#endif /* FILEASSOC */
#ifdef VERIFIED_EXEC
/*
* Initialise the fingerprint operations vectors before

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_exec.c,v 1.218 2006/05/14 21:15:11 elad Exp $ */
/* $NetBSD: kern_exec.c,v 1.219 2006/07/14 18:41:40 elad Exp $ */
/*-
* Copyright (C) 1993, 1994, 1996 Christopher G. Demetriou
@ -33,7 +33,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.218 2006/05/14 21:15:11 elad Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_exec.c,v 1.219 2006/07/14 18:41:40 elad Exp $");
#include "opt_ktrace.h"
#include "opt_syscall_debug.h"
@ -286,8 +286,8 @@ check_exec(struct lwp *l, struct exec_package *epp, int flag)
#ifdef VERIFIED_EXEC
if ((error = veriexec_verify(l, vp, epp->ep_vap, epp->ep_ndp->ni_dirp,
flag, NULL)) != 0)
if ((error = veriexec_verify(l, vp, epp->ep_ndp->ni_dirp, flag,
NULL)) != 0)
goto bad2;
#endif

435
sys/kern/kern_fileassoc.c Normal file
View File

@ -0,0 +1,435 @@
/* $NetBSD: kern_fileassoc.c,v 1.1 2006/07/14 18:41:40 elad Exp $ */
/*-
* Copyright (c) 2006 Elad Efrat <elad@NetBSD.org>
* All rights reserved.
*
* 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 Elad Efrat.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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/mount.h>
#include <sys/queue.h>
#include <sys/malloc.h>
#include <sys/vnode.h>
#include <sys/namei.h>
#include <sys/exec.h>
#include <sys/proc.h>
#include <sys/inttypes.h>
#include <sys/errno.h>
#include <sys/fileassoc.h>
struct fileassoc_hook fileassoc_hooks[FILEASSOC_NHOOKS];
int fileassoc_nhooks;
/* Global list of hash tables, one per device. */
LIST_HEAD(, fileassoc_table) fileassoc_tables;
/*
* Hashing function: Takes a number modulus the mask to give back
* an index into the hash table.
*/
#define FILEASSOC_HASH(tbl, fileid) \
(hash32_buf(&(fileid), sizeof((fileid)), HASH32_BUF_INIT) \
& ((tbl)->hash_mask))
/*
* Initialize the fileassoc subsystem.
*/
void
fileassoc_init(void)
{
memset(fileassoc_hooks, 0, sizeof(fileassoc_hooks));
fileassoc_nhooks = 0;
}
/*
* Register a new hook.
*/
fileassoc_t
fileassoc_register(const char *name, fileassoc_cleanup_cb_t cleanup_cb)
{
int i;
if (fileassoc_nhooks >= FILEASSOC_NHOOKS)
return (-1);
for (i = 0; i < FILEASSOC_NHOOKS; i++)
if (fileassoc_hooks[i].hook_name == NULL)
break;
fileassoc_hooks[i].hook_name = name;
fileassoc_hooks[i].hook_cleanup_cb = cleanup_cb;
fileassoc_nhooks++;
return (i);
}
/*
* Deregister a hook.
*/
int
fileassoc_deregister(fileassoc_t id)
{
if (id < 0 || id >= FILEASSOC_NHOOKS)
return (EINVAL);
fileassoc_hooks[id].hook_name = NULL;
fileassoc_hooks[id].hook_cleanup_cb = NULL;
fileassoc_nhooks--;
return (0);
}
/*
* Get the hash table for the specified device.
*/
struct fileassoc_table *
fileassoc_table_lookup(struct mount *mp)
{
struct fileassoc_table *tbl;
LIST_FOREACH(tbl, &fileassoc_tables, hash_list) {
if (tbl->tbl_mntpt == mp)
return (tbl);
}
return (NULL);
}
/*
* Perform a lookup on a hash table.
*/
struct fileassoc_hash_entry *
fileassoc_file_lookup(struct vnode *vp)
{
struct fileassoc_table *tbl;
struct fileassoc_hashhead *tble;
struct fileassoc_hash_entry *e;
struct vattr va;
size_t indx;
int error;
error = VOP_GETATTR(vp, &va, curlwp->l_proc->p_cred, curlwp);
if (error)
return (NULL);
tbl = fileassoc_table_lookup(vp->v_mount);
if (tbl == NULL)
return (NULL);
/*
* XXX: We should NOT rely on fileid here!
*/
indx = FILEASSOC_HASH(tbl, va.va_fileid);
tble = &(tbl->hash_tbl[indx & ((tbl)->hash_mask)]);
LIST_FOREACH(e, tble, entries) {
if ((e != NULL) && (e->fileid == va.va_fileid))
return (e);
}
return (NULL);
}
/*
* Return hook data associated with a vnode.
*/
void *
fileassoc_lookup(struct vnode *vp, fileassoc_t id)
{
struct fileassoc_hash_entry *mhe;
mhe = fileassoc_file_lookup(vp);
if (mhe == NULL)
return (NULL);
return (mhe->hooks[id]);
}
/*
* Create a new fileassoc table.
*/
int
fileassoc_table_add(struct mount *mp, size_t size)
{
struct fileassoc_table *tbl;
/* Check for existing table for device. */
if (fileassoc_table_lookup(mp) != NULL)
return (EEXIST);
/* Allocate and initialize a Veriexec hash table. */
tbl = malloc(sizeof(*tbl), M_TEMP, M_WAITOK | M_ZERO);
tbl->hash_size = size;
tbl->tbl_mntpt = mp;
tbl->hash_tbl = hashinit(size, HASH_LIST, M_TEMP,
M_WAITOK | M_ZERO, &tbl->hash_mask);
LIST_INSERT_HEAD(&fileassoc_tables, tbl, hash_list);
return (0);
}
/*
* Delete a table.
*/
int
fileassoc_table_delete(struct mount *mp)
{
struct fileassoc_table *tbl;
struct fileassoc_hashhead *hh;
u_long i;
int j;
tbl = fileassoc_table_lookup(mp);
if (tbl == NULL)
return (EEXIST);
/* Remove all entries from the table and lists */
hh = tbl->hash_tbl;
for (i = 0; i < tbl->hash_size; i++) {
struct fileassoc_hash_entry *mhe;
while (LIST_FIRST(&hh[i]) != NULL) {
mhe = LIST_FIRST(&hh[i]);
LIST_REMOVE(mhe, entries);
for (j = 0; j < fileassoc_nhooks; j++)
if (fileassoc_hooks[j].hook_cleanup_cb != NULL)
(fileassoc_hooks[j].hook_cleanup_cb)(mhe->hooks[j], 1);
free(mhe, M_TEMP);
}
}
for (j = 0; j < fileassoc_nhooks; j++)
if (fileassoc_hooks[j].hook_cleanup_cb != NULL)
(fileassoc_hooks[j].hook_cleanup_cb)(tbl->tables[j], 0);
/* Remove hash table and sysctl node */
hashdone(tbl->hash_tbl, M_TEMP);
LIST_REMOVE(tbl, hash_list);
return (0);
}
/*
* Clear a table for a given hook.
*/
int
fileassoc_table_clear(struct mount *mp, fileassoc_t id)
{
struct fileassoc_table *tbl;
struct fileassoc_hashhead *hh;
fileassoc_cleanup_cb_t cleanup_cb;
u_long i;
tbl = fileassoc_table_lookup(mp);
if (tbl == NULL)
return (EEXIST);
cleanup_cb = fileassoc_hooks[id].hook_cleanup_cb;
hh = tbl->hash_tbl;
for (i = 0; i < tbl->hash_size; i++) {
struct fileassoc_hash_entry *mhe;
LIST_FOREACH(mhe, &hh[i], entries) {
if ((mhe->hooks[id] != NULL) && cleanup_cb != NULL)
cleanup_cb(mhe->hooks[id], FILEASSOC_CLEANUP_FILE);
mhe->hooks[id] = NULL;
}
}
if ((tbl->tables[id] != NULL) && cleanup_cb != NULL)
cleanup_cb(tbl->tables[id], FILEASSOC_CLEANUP_TABLE);
tbl->tables[id] = NULL;
return (0);
}
/*
* Add hook-specific data on a fileassoc table.
*/
int
fileassoc_tabledata_add(struct mount *mp, fileassoc_t id, void *data)
{
struct fileassoc_table *tbl;
tbl = fileassoc_table_lookup(mp);
if (tbl == NULL)
return (EFAULT);
tbl->tables[id] = data;
return (0);
}
/*
* Clear hook-specific data on a fileassoc table.
*/
int
fileassoc_tabledata_clear(struct mount *mp, fileassoc_t id)
{
struct fileassoc_table *tbl;
tbl = fileassoc_table_lookup(mp);
if (tbl == NULL)
return (EFAULT);
tbl->tables[id] = NULL;
return (0);
}
/*
* Retrieve hook-specific data from a fileassoc table.
*/
void *
fileassoc_tabledata_lookup(struct mount *mp, fileassoc_t id)
{
struct fileassoc_table *tbl;
tbl = fileassoc_table_lookup(mp);
if (tbl == NULL)
return (NULL);
return (tbl->tables[id]);
}
/*
* Add a file entry to a table.
*/
struct fileassoc_hash_entry *
fileassoc_file_add(struct vnode *vp)
{
struct fileassoc_table *tbl;
struct fileassoc_hashhead *vhh;
struct fileassoc_hash_entry *e;
struct vattr va;
size_t indx;
int error;
error = VOP_GETATTR(vp, &va, curlwp->l_proc->p_cred, curlwp);
if (error)
return (NULL);
e = fileassoc_file_lookup(vp);
if (e != NULL)
return (e);
tbl = fileassoc_table_lookup(vp->v_mount);
if (tbl == NULL)
return (NULL);
/*
* XXX: We should NOT rely on fileid here!
*/
indx = FILEASSOC_HASH(tbl, va.va_fileid);
vhh = &(tbl->hash_tbl[indx & ((tbl)->hash_mask)]);
e = malloc(sizeof(*e), M_TEMP, M_WAITOK | M_ZERO);
e->fileid = va.va_fileid;
LIST_INSERT_HEAD(vhh, e, entries);
return (e);
}
/*
* Delete a file entry from a table.
*/
int
fileassoc_file_delete(struct vnode *vp)
{
struct fileassoc_hash_entry *mhe;
int i;
mhe = fileassoc_file_lookup(vp);
if (mhe == NULL)
return (ENOENT);
LIST_REMOVE(mhe, entries);
for (i = 0; i < fileassoc_nhooks; i++)
if (fileassoc_hooks[i].hook_cleanup_cb != NULL)
(fileassoc_hooks[i].hook_cleanup_cb)(mhe->hooks[i],
FILEASSOC_CLEANUP_FILE);
free(mhe, M_TEMP);
return (0);
}
/*
* Add a hook to a vnode.
*/
int
fileassoc_add(struct vnode *vp, fileassoc_t id, void *data)
{
struct fileassoc_hash_entry *e;
e = fileassoc_file_lookup(vp);
if (e == NULL) {
e = fileassoc_file_add(vp);
if (e == NULL)
return (ENOTDIR);
}
if (e->hooks[id] != NULL)
return (EEXIST);
e->hooks[id] = data;
return (0);
}
/*
* Clear a hook from a vnode.
*/
int
fileassoc_clear(struct vnode *vp, fileassoc_t id)
{
struct fileassoc_hash_entry *mhe;
fileassoc_cleanup_cb_t cleanup_cb;
mhe = fileassoc_file_lookup(vp);
if (mhe == NULL)
return (ENOENT);
cleanup_cb = fileassoc_hooks[id].hook_cleanup_cb;
if ((mhe->hooks[id] != NULL) && cleanup_cb != NULL)
cleanup_cb(mhe->hooks[id], FILEASSOC_CLEANUP_FILE);
mhe->hooks[id] = NULL;
return (0);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_verifiedexec.c,v 1.52 2006/07/09 10:13:53 blymn Exp $ */
/* $NetBSD: kern_verifiedexec.c,v 1.53 2006/07/14 18:41:40 elad Exp $ */
/*-
* Copyright 2005 Elad Efrat <elad@bsd.org.il>
@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_verifiedexec.c,v 1.52 2006/07/09 10:13:53 blymn Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_verifiedexec.c,v 1.53 2006/07/14 18:41:40 elad Exp $");
#include "opt_verified_exec.h"
@ -57,6 +57,7 @@ __KERNEL_RCSID(0, "$NetBSD: kern_verifiedexec.c,v 1.52 2006/07/09 10:13:53 blymn
#include <crypto/ripemd160/rmd160.h>
#include <sys/md5.h>
#include <uvm/uvm_extern.h>
#include <sys/fileassoc.h>
#include <sys/kauth.h>
int veriexec_verbose;
@ -67,6 +68,8 @@ size_t veriexec_name_max;
const struct sysctlnode *veriexec_count_node;
int veriexec_hook;
/* Veriexecs table of hash types and their associated information. */
LIST_HEAD(veriexec_ops_head, veriexec_fp_ops) veriexec_ops_list;
@ -134,6 +137,13 @@ veriexec_init_fp_ops(void)
{
struct veriexec_fp_ops *ops;
/* 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");
#endif /* DIAGNOSTIC */
LIST_INIT(&veriexec_ops_list);
veriexec_fp_names = NULL;
veriexec_name_max = 0;
@ -210,7 +220,7 @@ veriexec_find_ops(u_char *name)
*/
int
veriexec_fp_calc(struct lwp *l, struct vnode *vp,
struct veriexec_hash_entry *vhe, uint64_t size, u_char *fp)
struct veriexec_file_entry *vfe, uint64_t size, u_char *fp)
{
void *ctx, *page_ctx;
u_char *buf, *page_fp;
@ -218,19 +228,18 @@ veriexec_fp_calc(struct lwp *l, struct vnode *vp,
size_t resid, npages;
int error, do_perpage, pagen;
if (vhe->ops == NULL) {
panic("veriexec: Operations vector is NULL");
}
if (vfe->ops == NULL)
panic("Veriexec: Operations vector is NULL");
#if 0 /* XXX - for now */
if ((vhe->type & VERIEXEC_UNTRUSTED) &&
(vhe->page_fp_status == PAGE_FP_NONE))
if ((vfe->type & VERIEXEC_UNTRUSTED) &&
(vfe->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);
ctx = (void *) malloc(vfe->ops->context_size, M_TEMP, M_WAITOK);
buf = (u_char *) malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
page_ctx = NULL;
@ -238,14 +247,14 @@ veriexec_fp_calc(struct lwp *l, struct vnode *vp,
npages = 0;
if (do_perpage) {
npages = (size >> PAGE_SHIFT) + 1;
page_fp = (u_char *) malloc(vhe->ops->hash_len * npages,
page_fp = (u_char *) malloc(vfe->ops->hash_len * npages,
M_TEMP, M_WAITOK|M_ZERO);
vhe->page_fp = page_fp;
page_ctx = (void *) malloc(vhe->ops->context_size, M_TEMP,
vfe->page_fp = page_fp;
page_ctx = (void *) malloc(vfe->ops->context_size, M_TEMP,
M_WAITOK);
}
(vhe->ops->init)(ctx);
(vfe->ops->init)(ctx);
len = 0;
error = 0;
@ -265,30 +274,30 @@ veriexec_fp_calc(struct lwp *l, struct vnode *vp,
if (error) {
if (do_perpage) {
free(vhe->page_fp, M_TEMP);
vhe->page_fp = NULL;
free(vfe->page_fp, M_TEMP);
vfe->page_fp = NULL;
}
goto bad;
}
(vhe->ops->update)(ctx, buf, (unsigned int) len);
(vfe->ops->update)(ctx, buf, (unsigned int) len);
if (do_perpage) {
(vhe->ops->init)(page_ctx);
(vhe->ops->update)(page_ctx, buf, (unsigned int)len);
(vhe->ops->final)(page_fp, page_ctx);
(vfe->ops->init)(page_ctx);
(vfe->ops->update)(page_ctx, buf, (unsigned int)len);
(vfe->ops->final)(page_fp, page_ctx);
if (veriexec_verbose >= 2) {
int i;
printf("hash for page %d: ", pagen);
for (i = 0; i < vhe->ops->hash_len; i++)
for (i = 0; i < vfe->ops->hash_len; i++)
printf("%02x", page_fp[i]);
printf("\n");
}
page_fp += vhe->ops->hash_len;
page_fp += vfe->ops->hash_len;
pagen++;
}
@ -296,12 +305,12 @@ veriexec_fp_calc(struct lwp *l, struct vnode *vp,
break;
}
(vhe->ops->final)(fp, ctx);
(vfe->ops->final)(fp, ctx);
if (do_perpage) {
vhe->last_page_size = len;
vhe->page_fp_status = PAGE_FP_READY;
vhe->npages = npages;
vfe->last_page_size = len;
vfe->page_fp_status = PAGE_FP_READY;
vfe->npages = npages;
}
bad:
@ -335,41 +344,16 @@ veriexec_fp_cmp(struct veriexec_fp_ops *ops, u_char *fp1, u_char *fp2)
return (memcmp(fp1, fp2, ops->hash_len));
}
/* Get the hash table for the specified device. */
struct veriexec_hashtbl *
veriexec_tblfind(dev_t device) {
struct veriexec_hashtbl *tbl;
LIST_FOREACH(tbl, &veriexec_tables, hash_list) {
if (tbl->hash_dev == device)
return (tbl);
}
return (NULL);
}
/* Perform a lookup on a hash table. */
struct veriexec_hash_entry *
veriexec_lookup(dev_t device, ino_t inode)
struct veriexec_table_entry *
veriexec_tblfind(struct vnode *vp)
{
struct veriexec_hashtbl *tbl;
struct veriexec_hashhead *tble;
struct veriexec_hash_entry *e;
size_t indx;
tbl = veriexec_tblfind(device);
if (tbl == NULL)
return (NULL);
indx = VERIEXEC_HASH(tbl, inode);
tble = &(tbl->hash_tbl[indx & VERIEXEC_HASH_MASK(tbl)]);
LIST_FOREACH(e, tble, entries) {
if ((e != NULL) && (e->inode == inode))
return (e);
return (fileassoc_tabledata_lookup(vp->v_mount, veriexec_hook));
}
return (NULL);
struct veriexec_file_entry *
veriexec_lookup(struct vnode *vp)
{
return (fileassoc_lookup(vp, veriexec_hook));
}
/*
@ -377,23 +361,22 @@ veriexec_lookup(dev_t device, ino_t inode)
* The passed entry is allocated in kernel memory.
*/
int
veriexec_hashadd(struct veriexec_hashtbl *tbl, struct veriexec_hash_entry *e)
veriexec_hashadd(struct vnode *vp, struct veriexec_file_entry *vfe)
{
struct veriexec_hashhead *vhh;
size_t indx;
struct veriexec_table_entry *vte;
int error;
if (tbl == NULL)
return (EFAULT);
error = fileassoc_add(vp, veriexec_hook, vfe);
if (error)
return (error);
indx = VERIEXEC_HASH(tbl, e->inode);
vhh = &(tbl->hash_tbl[indx]);
vte = veriexec_tblfind(vp);
#ifdef DIAGNOSTIC
if (vte == NULL)
panic("Fileassoc: Inconsistency with table data");
#endif /* DIAGNOSTIC */
if (vhh == NULL)
panic("veriexec: veriexec_hashadd: vhh is NULL.");
LIST_INSERT_HEAD(vhh, e, entries);
tbl->hash_count++;
vte->vte_count++;
return (0);
}
@ -405,52 +388,57 @@ veriexec_hashadd(struct veriexec_hashtbl *tbl, struct veriexec_hash_entry *e)
* vn_open(), 'flag' will be VERIEXEC_FILE.
*/
int
veriexec_verify(struct lwp *l, struct vnode *vp, struct vattr *va,
const u_char *name, int flag, struct veriexec_hash_entry **ret)
veriexec_verify(struct lwp *l, struct vnode *vp, const u_char *name, int flag,
struct veriexec_file_entry **ret)
{
struct veriexec_hash_entry *vhe;
struct veriexec_file_entry *vfe;
struct vattr va;
u_char *digest;
int error;
if (vp->v_type != VREG)
return (0);
error = VOP_GETATTR(vp, &va, l->l_proc->p_cred, l);
if (error)
return (error);
/* Lookup veriexec table entry, save pointer if requested. */
vhe = veriexec_lookup((dev_t)va->va_fsid, (ino_t)va->va_fileid);
vfe = veriexec_lookup(vp);
if (ret != NULL)
*ret = vhe;
if (vhe == NULL)
*ret = vfe;
if (vfe == NULL)
goto out;
/* Evaluate fingerprint if needed. */
error = 0;
digest = NULL;
if ((vhe->status == FINGERPRINT_NOTEVAL) ||
(vhe->type & VERIEXEC_UNTRUSTED)) {
if ((vfe->status == FINGERPRINT_NOTEVAL) ||
(vfe->type & VERIEXEC_UNTRUSTED)) {
/* Calculate fingerprint for on-disk file. */
digest = (u_char *) malloc(vhe->ops->hash_len, M_TEMP,
digest = (u_char *) malloc(vfe->ops->hash_len, M_TEMP,
M_WAITOK);
error = veriexec_fp_calc(l, vp, vhe, va->va_size, digest);
error = veriexec_fp_calc(l, vp, vfe, va.va_size, digest);
if (error) {
veriexec_report("Fingerprint calculation error.",
name, va, NULL, REPORT_NOVERBOSE,
name, &va, NULL, REPORT_NOVERBOSE,
REPORT_NOALARM, REPORT_NOPANIC);
free(digest, M_TEMP);
return (error);
}
/* Compare fingerprint with loaded data. */
if (veriexec_fp_cmp(vhe->ops, vhe->fp, digest) == 0) {
vhe->status = FINGERPRINT_VALID;
if (veriexec_fp_cmp(vfe->ops, vfe->fp, digest) == 0) {
vfe->status = FINGERPRINT_VALID;
} else {
vhe->status = FINGERPRINT_NOMATCH;
vfe->status = FINGERPRINT_NOMATCH;
}
free(digest, M_TEMP);
}
if (!(vhe->type & flag)) {
veriexec_report("Incorrect access type.", name, va, l,
if (!(vfe->type & flag)) {
veriexec_report("Incorrect access type.", name, &va, l,
REPORT_NOVERBOSE, REPORT_ALARM,
REPORT_NOPANIC);
@ -461,8 +449,8 @@ veriexec_verify(struct lwp *l, struct vnode *vp, struct vattr *va,
out:
/* No entry in the veriexec tables. */
if (vhe == NULL) {
veriexec_report("veriexec_verify: No entry.", name, va,
if (vfe == NULL) {
veriexec_report("veriexec_verify: No entry.", name, &va,
l, REPORT_VERBOSE, REPORT_NOALARM, REPORT_NOPANIC);
/* Lockdown mode: Deny access to non-monitored files if
@ -477,23 +465,23 @@ out:
return (0);
}
switch (vhe->status) {
switch (vfe->status) {
case FINGERPRINT_NOTEVAL:
/* Should not happen. */
veriexec_report("veriexec_verify: Not-evaluated status "
"post evaluation; inconsistency detected.", name, va,
"post evaluation; inconsistency detected.", name, &va,
NULL, REPORT_NOVERBOSE, REPORT_NOALARM, REPORT_PANIC);
case FINGERPRINT_VALID:
/* Valid fingerprint. */
veriexec_report("veriexec_verify: Match.", name, va, NULL,
veriexec_report("veriexec_verify: Match.", name, &va, NULL,
REPORT_VERBOSE, REPORT_NOALARM, REPORT_NOPANIC);
break;
case FINGERPRINT_NOMATCH:
/* Fingerprint mismatch. */
veriexec_report("veriexec_verify: Mismatch.", name, va,
veriexec_report("veriexec_verify: Mismatch.", name, &va,
NULL, REPORT_NOVERBOSE, REPORT_ALARM, REPORT_NOPANIC);
/* IDS mode: Deny access on fingerprint mismatch. */
@ -503,12 +491,9 @@ out:
break;
default:
/*
* Should never happen.
* XXX: Print vnode/process?
*/
/* Should never happen. */
veriexec_report("veriexec_verify: Invalid status "
"post evaluation.", name, va, NULL, REPORT_NOVERBOSE,
"post evaluation.", name, &va, NULL, REPORT_NOVERBOSE,
REPORT_NOALARM, REPORT_PANIC);
}
@ -519,7 +504,7 @@ out:
* Evaluate per-page fingerprints.
*/
int
veriexec_page_verify(struct veriexec_hash_entry *vhe, struct vattr *va,
veriexec_page_verify(struct veriexec_file_entry *vfe, struct vattr *va,
struct vm_page *pg, size_t idx, struct lwp *l)
{
void *ctx;
@ -528,31 +513,31 @@ veriexec_page_verify(struct veriexec_hash_entry *vhe, struct vattr *va,
int error;
vaddr_t kva;
if (vhe->page_fp_status == PAGE_FP_NONE)
if (vfe->page_fp_status == PAGE_FP_NONE)
return (0);
if (vhe->page_fp_status == PAGE_FP_FAIL)
if (vfe->page_fp_status == PAGE_FP_FAIL)
return (EPERM);
if (idx >= vhe->npages)
if (idx >= vfe->npages)
return (0);
ctx = malloc(vhe->ops->context_size, M_TEMP, M_WAITOK);
fp = malloc(vhe->ops->hash_len, M_TEMP, M_WAITOK);
ctx = malloc(vfe->ops->context_size, M_TEMP, M_WAITOK);
fp = malloc(vfe->ops->hash_len, M_TEMP, M_WAITOK);
kva = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, UVM_KMF_VAONLY | UVM_KMF_WAITVA);
pmap_kenter_pa(kva, VM_PAGE_TO_PHYS(pg), VM_PROT_READ);
page_fp = (u_char *) vhe->page_fp + (vhe->ops->hash_len * idx);
(vhe->ops->init)(ctx);
(vhe->ops->update)(ctx, (void *) kva,
((vhe->npages - 1) == idx) ? vhe->last_page_size
page_fp = (u_char *) vfe->page_fp + (vfe->ops->hash_len * idx);
(vfe->ops->init)(ctx);
(vfe->ops->update)(ctx, (void *) kva,
((vfe->npages - 1) == idx) ? vfe->last_page_size
: PAGE_SIZE);
(vhe->ops->final)(fp, ctx);
(vfe->ops->final)(fp, ctx);
pmap_kremove(kva, PAGE_SIZE);
uvm_km_free(kernel_map, kva, PAGE_SIZE, UVM_KMF_VAONLY);
error = veriexec_fp_cmp(vhe->ops, page_fp, fp);
error = veriexec_fp_cmp(vfe->ops, page_fp, fp);
if (error) {
const char *msg;
@ -591,8 +576,8 @@ veriexec_page_verify(struct veriexec_hash_entry *vhe, struct vattr *va,
int
veriexec_removechk(struct lwp *l, struct vnode *vp, const char *pathbuf)
{
struct veriexec_hashtbl *tbl;
struct veriexec_hash_entry *vhe;
struct veriexec_file_entry *vfe;
struct veriexec_table_entry *vte;
struct vattr va;
int error;
@ -600,8 +585,8 @@ veriexec_removechk(struct lwp *l, struct vnode *vp, const char *pathbuf)
if (error)
return (error);
vhe = veriexec_lookup(va.va_fsid, va.va_fileid);
if (vhe == NULL) {
vfe = veriexec_lookup(vp);
if (vfe == NULL) {
/* Lockdown mode: Deny access to non-monitored files. */
if (veriexec_strict >= 3)
return (EPERM);
@ -616,21 +601,15 @@ veriexec_removechk(struct lwp *l, struct vnode *vp, const char *pathbuf)
if (veriexec_strict >= 2)
return (EPERM);
tbl = veriexec_tblfind(va.va_fsid);
if (tbl == NULL) {
veriexec_report("veriexec_removechk: Inconsistency "
"detected: Could not get table for file in lists.",
pathbuf, &va, NULL, REPORT_NOVERBOSE, REPORT_NOALARM,
REPORT_PANIC);
}
fileassoc_clear(vp, veriexec_hook);
LIST_REMOVE(vhe, entries);
if (vhe->fp != NULL)
free(vhe->fp, M_TEMP);
if (vhe->page_fp != NULL)
free(vhe->page_fp, M_TEMP);
free(vhe, M_TEMP);
tbl->hash_count--;
vte = veriexec_tblfind(vp);
#ifdef DIAGNOSTIC
if (vte == NULL)
panic("Fileassoc: Inconsistency during entry removel");
#endif /* DIAGNOSTIC */
vte->vte_count--;
return (error);
}
@ -642,7 +621,7 @@ int
veriexec_renamechk(struct vnode *vp, const char *from, const char *to,
struct lwp *l)
{
struct veriexec_hash_entry *vhe;
struct veriexec_file_entry *vfe;
struct vattr va;
int error;
@ -651,24 +630,22 @@ veriexec_renamechk(struct vnode *vp, const char *from, const char *to,
return (error);
if (veriexec_strict >= 3) {
printf("Veriexec: veriexec_renamechk: Preventing rename "
"of \"%s\" [%ld:%llu] to \"%s\", uid=%u, pid=%u: "
"Lockdown mode.\n", from, va.va_fsid,
(unsigned long long)va.va_fileid,
printf("Veriexec: Preventing rename of \"%s\" [%ld:%llu] to "
"\"%s\", uid=%u, pid=%u: Lockdown mode.\n", from,
va.va_fsid, (unsigned long long)va.va_fileid,
to, kauth_cred_geteuid(l->l_proc->p_cred),
l->l_proc->p_pid);
return (EPERM);
}
vhe = veriexec_lookup(va.va_fsid, va.va_fileid);
if (vhe != NULL) {
vfe = veriexec_lookup(vp);
if (vfe != NULL) {
if (veriexec_strict >= 2) {
printf("Veriexec: veriexec_renamechk: Preventing "
"rename of \"%s\" [%ld:%llu] to \"%s\", "
"uid=%u, pid=%u: IPS mode, file "
"monitored.\n", from, va.va_fsid,
(unsigned long long)va.va_fileid,
to, kauth_cred_geteuid(l->l_proc->p_cred),
printf("Veriexec: Preventing rename of \"%s\" "
"[%ld:%llu] to \"%s\", uid=%u, pid=%u: "
"IPS mode, file monitored.\n", from, va.va_fsid,
(unsigned long long)va.va_fileid, to,
kauth_cred_geteuid(l->l_proc->p_cred),
l->l_proc->p_pid);
return (EPERM);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: vfs_syscalls.c,v 1.250 2006/07/14 18:30:35 yamt Exp $ */
/* $NetBSD: vfs_syscalls.c,v 1.251 2006/07/14 18:41:40 elad Exp $ */
/*
* Copyright (c) 1989, 1993
@ -37,13 +37,14 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vfs_syscalls.c,v 1.250 2006/07/14 18:30:35 yamt Exp $");
__KERNEL_RCSID(0, "$NetBSD: vfs_syscalls.c,v 1.251 2006/07/14 18:41:40 elad Exp $");
#include "opt_compat_netbsd.h"
#include "opt_compat_43.h"
#include "opt_ktrace.h"
#include "opt_verified_exec.h"
#include "fss.h"
#include "opt_fileassoc.h"
#include <sys/param.h>
#include <sys/systm.h>
@ -65,6 +66,9 @@ __KERNEL_RCSID(0, "$NetBSD: vfs_syscalls.c,v 1.250 2006/07/14 18:30:35 yamt Exp
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
#ifdef FILEASSOC
#include <sys/fileassoc.h>
#endif /* FILEASSOC */
#ifdef VERIFIED_EXEC
#include <sys/verified_exec.h>
#endif /* VERIFIED_EXEC */
@ -546,6 +550,35 @@ dounmount(struct mount *mp, int flags, struct lwp *l)
int async;
int used_syncer;
#ifdef VERIFIED_EXEC
if (!doing_shutdown) {
if (veriexec_strict >= 3) {
printf("Veriexec: Lockdown mode, preventing unmount of"
" \"%s\". (uid=%u)\n", mp->mnt_stat.f_mntonname,
kauth_cred_getuid(l->l_proc->p_cred));
return (EPERM);
}
if (veriexec_strict == 2) {
struct veriexec_table_entry *vte;
/* Check if we have fingerprints on mount. */
vte = fileassoc_tabledata_lookup(mp, veriexec_hook);
if ((vte != NULL) && (vte->vte_count > 0)) {
printf("Veriexec: IPS mode, preventing unmount"
" of \"%s\" with monitored files. "
"(uid=%u)\n", mp->mnt_stat.f_mntonname,
kauth_cred_getuid(l->l_proc->p_cred));
return (EPERM);
}
}
}
#endif /* VERIFIED_EXEC */
#ifdef FILEASSOC
(void)fileassoc_table_delete(mp);
#endif /* FILEASSOC */
simple_lock(&mountlist_slock);
vfs_unbusy(mp);
used_syncer = (mp->mnt_syncer != NULL);
@ -2019,6 +2052,10 @@ restart:
VOP_LEASE(vp, l, p->p_cred, LEASE_WRITE);
error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
vn_finished_write(mp, 0);
#ifdef FILEASSOC
if (!error)
(void)fileassoc_file_delete(nd.ni_vp);
#endif /* FILEASSOC */
out:
return (error);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: vfs_vnops.c,v 1.112 2006/05/27 23:46:49 simonb Exp $ */
/* $NetBSD: vfs_vnops.c,v 1.113 2006/07/14 18:41:40 elad Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vfs_vnops.c,v 1.112 2006/05/27 23:46:49 simonb Exp $");
__KERNEL_RCSID(0, "$NetBSD: vfs_vnops.c,v 1.113 2006/07/14 18:41:40 elad Exp $");
#include "opt_verified_exec.h"
@ -105,7 +105,7 @@ vn_open(struct nameidata *ndp, int fmode, int cmode)
struct vattr va;
int error;
#ifdef VERIFIED_EXEC
struct veriexec_hash_entry *vhe = NULL;
struct veriexec_file_entry *vfe = NULL;
char pathbuf[MAXPATHLEN];
size_t pathlen;
int (*copyfun)(const void *, void *, size_t, size_t *) =
@ -208,8 +208,8 @@ restart:
if ((fmode & O_CREAT) == 0) {
#ifdef VERIFIED_EXEC
if ((error = veriexec_verify(l, vp, &va, pathbuf,
VERIEXEC_FILE, &vhe)) != 0)
if ((error = veriexec_verify(l, vp, pathbuf,
VERIEXEC_FILE, &vfe)) != 0)
goto bad;
#endif
@ -227,7 +227,7 @@ restart:
(error = VOP_ACCESS(vp, VWRITE, cred, l)) != 0)
goto bad;
#ifdef VERIFIED_EXEC
if (vhe != NULL) {
if (vfe != NULL) {
veriexec_report("Write access request.",
pathbuf, &va, l,
REPORT_NOVERBOSE,
@ -239,7 +239,7 @@ restart:
error = EPERM;
goto bad;
} else {
vhe->status = FINGERPRINT_NOTEVAL;
vfe->status = FINGERPRINT_NOTEVAL;
}
}
#endif

99
sys/sys/fileassoc.h Normal file
View File

@ -0,0 +1,99 @@
/* $NetBSD: fileassoc.h,v 1.1 2006/07/14 18:41:40 elad Exp $ */
/*-
* Copyright (c) 2006 Elad Efrat <elad@NetBSD.org>
* All rights reserved.
*
* 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 Elad Efrat.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*/
#ifndef _SYS_FILEASSOC_H_
#define _SYS_FILEASSOC_H_
#include "opt_fileassoc.h"
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/hash.h>
/* Max. number of hooks. */
#ifndef FILEASSOC_NHOOKS
#define FILEASSOC_NHOOKS 4
#endif /* !FILEASSOC_NHOOKS */
#define FILEASSOC_INVAL -1
typedef int fileassoc_t;
typedef void (*fileassoc_cleanup_cb_t)(void *, int);
/*
* Hook entry.
* Includes the hook name for identification and private hook clear callback.
*/
struct fileassoc_hook {
const char *hook_name; /* Hook name. */
fileassoc_cleanup_cb_t hook_cleanup_cb; /* Hook clear callback. */
};
/* An entry in the per-device hash table. */
struct fileassoc_hash_entry {
ino_t fileid; /* File id. */
void *hooks[FILEASSOC_NHOOKS]; /* Hooks. */
LIST_ENTRY(fileassoc_hash_entry) entries; /* List pointer. */
};
LIST_HEAD(fileassoc_hashhead, fileassoc_hash_entry);
struct fileassoc_table {
struct fileassoc_hashhead *hash_tbl;
size_t hash_size; /* Number of slots. */
struct mount *tbl_mntpt;
u_long hash_mask;
void *tables[FILEASSOC_NHOOKS];
LIST_ENTRY(fileassoc_table) hash_list; /* List pointer. */
};
#define FILEASSOC_CLEANUP_TABLE 0
#define FILEASSOC_CLEANUP_FILE 1
void fileassoc_init(void);
fileassoc_t fileassoc_register(const char *, fileassoc_cleanup_cb_t);
int fileassoc_deregister(fileassoc_t);
struct fileassoc_table *fileassoc_table_lookup(struct mount *);
void *fileassoc_tabledata_lookup(struct mount *, fileassoc_t);
struct fileassoc_hash_entry *fileassoc_file_lookup(struct vnode *);
void *fileassoc_lookup(struct vnode *, fileassoc_t);
int fileassoc_table_add(struct mount *, size_t);
int fileassoc_table_delete(struct mount *);
int fileassoc_table_clear(struct mount *, fileassoc_t);
int fileassoc_tabledata_add(struct mount *, fileassoc_t, void *);
int fileassoc_tabledata_clear(struct mount *, fileassoc_t);
struct fileassoc_hash_entry *fileassoc_file_add(struct vnode *);
int fileassoc_file_delete(struct vnode *);
int fileassoc_add(struct vnode *, fileassoc_t, void *);
int fileassoc_clear(struct vnode *, fileassoc_t);
#endif /* !_SYS_FILEASSOC_H_ */

View File

@ -1,4 +1,4 @@
/* $NetBSD: verified_exec.h,v 1.28 2005/12/12 21:47:58 elad Exp $ */
/* $NetBSD: verified_exec.h,v 1.29 2006/07/14 18:41:40 elad Exp $ */
/*-
* Copyright 2005 Elad Efrat <elad@bsd.org.il>
@ -51,19 +51,17 @@ struct veriexec_params {
};
struct veriexec_sizing_params {
dev_t dev;
size_t hash_size;
u_char file[MAXPATHLEN];
};
struct veriexec_delete_params {
dev_t dev;
ino_t ino;
u_char file[MAXPATHLEN];
};
struct veriexec_query_params {
u_char file[MAXPATHLEN];
unsigned char fp_type[VERIEXEC_TYPE_MAXLEN];
dev_t dev;
ino_t ino;
unsigned char type;
unsigned char status;
unsigned char *fp;
@ -114,6 +112,7 @@ extern int veriexec_strict;
#ifdef VERIEXEC_NEED_NODE
extern const struct sysctlnode *veriexec_count_node;
#endif /* VERIEXEC_NEED_NODE */
extern int veriexec_hook;
/*
* Operations vector for verified exec, this defines the characteristics
@ -134,37 +133,24 @@ struct veriexec_fp_ops {
LIST_ENTRY(veriexec_fp_ops) entries;
};
/* An entry in the per-device hash table. */
struct veriexec_hash_entry {
ino_t inode; /* Inode number. */
unsigned char type; /* Entry type. */
unsigned char status; /* Evaluation status. */
unsigned char page_fp_status; /* Per-page FP status. */
unsigned char *fp; /* Fingerprint. */
/* Veriexec per-file entry data. */
struct veriexec_file_entry {
u_char type; /* Entry type. */
u_char status; /* Evaluation status. */
u_char page_fp_status; /* Per-page FP status. */
u_char *fp; /* Fingerprint. */
void *page_fp; /* Per-page fingerprints */
size_t npages; /* Number of pages. */
size_t last_page_size; /* To support < PAGE_SIZE */
struct veriexec_fp_ops *ops; /* Fingerprint ops vector*/
LIST_ENTRY(veriexec_hash_entry) entries; /* List pointer. */
};
LIST_HEAD(veriexec_hashhead, veriexec_hash_entry);
/* Veriexec hash table information. */
struct veriexec_hashtbl {
struct veriexec_hashhead *hash_tbl;
size_t hash_size; /* Number of slots in the table. */
dev_t hash_dev; /* Device ID the hash table refers to. */
uint64_t hash_count; /* # of fingerprinted files in table. */
LIST_ENTRY(veriexec_hashtbl) hash_list;
/* Veriexec per-table data. */
struct veriexec_table_entry {
uint64_t vte_count; /* Number of Veriexec entries. */
const struct sysctlnode *vte_node;
};
/* Global list of hash tables, one per device. */
LIST_HEAD(, veriexec_hashtbl) veriexec_tables;
/* Mask to ensure bounded access to elements in the hash table. */
#define VERIEXEC_HASH_MASK(tbl) ((tbl)->hash_size - 1)
/* Readable values for veriexec_report(). */
#define REPORT_NOVERBOSE 0 /* Always print */
#define REPORT_VERBOSE 1 /* Print when verbose >= 1 */
@ -174,14 +160,6 @@ LIST_HEAD(, veriexec_hashtbl) veriexec_tables;
#define REPORT_NOALARM 0 /* Normal report */
#define REPORT_ALARM 1 /* Alarm - also print pid/uid/.. */
/*
* Hashing function: Takes an inode number modulus the mask to give back
* an index into the hash table.
*/
#define VERIEXEC_HASH(tbl, inode) \
(hash32_buf(&(inode), sizeof((inode)), HASH32_BUF_INIT) \
& VERIEXEC_HASH_MASK(tbl))
/* Initialize a fingerprint ops struct. */
#define VERIEXEC_OPINIT(ops, fp_type, hashlen, ctx_size, init_fn, \
update_fn, final_fn) \
@ -198,14 +176,14 @@ 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_hash_entry *, uint64_t, u_char *);
struct veriexec_file_entry *, uint64_t, u_char *);
int veriexec_fp_cmp(struct veriexec_fp_ops *, u_char *, u_char *);
struct veriexec_hashtbl *veriexec_tblfind(dev_t);
struct veriexec_hash_entry *veriexec_lookup(dev_t, ino_t);
int veriexec_hashadd(struct veriexec_hashtbl *, struct veriexec_hash_entry *);
int veriexec_verify(struct lwp *, struct vnode *, struct vattr *,
const u_char *, int, struct veriexec_hash_entry **);
int veriexec_page_verify(struct veriexec_hash_entry *, struct vattr *,
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 vattr *,
struct vm_page *, size_t, struct lwp *);
int veriexec_removechk(struct lwp *, struct vnode *, const char *);
int veriexec_renamechk(struct vnode *, const char *, const char *,
@ -213,10 +191,11 @@ int veriexec_renamechk(struct vnode *, const char *, const char *,
void veriexec_init_fp_ops(void);
void veriexec_report(const u_char *, const u_char *, struct vattr *,
struct lwp *, int, int, int);
int veriexec_newtable(struct veriexec_sizing_params *);
int veriexec_newtable(struct veriexec_sizing_params *, struct lwp *);
int veriexec_load(struct veriexec_params *, struct lwp *);
int veriexec_delete(struct veriexec_delete_params *);
int veriexec_query(struct veriexec_query_params *);
int veriexec_delete(struct veriexec_delete_params *, struct lwp *);
int veriexec_query(struct veriexec_query_params *, struct lwp *);
void veriexec_clear(void *, int);
#endif /* _KERNEL */