diff --git a/distrib/sets/lists/comp/mi b/distrib/sets/lists/comp/mi index d48302284e43..66a96417f82b 100644 --- a/distrib/sets/lists/comp/mi +++ b/distrib/sets/lists/comp/mi @@ -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 diff --git a/lib/libc/gen/sysctl.3 b/lib/libc/gen/sysctl.3 index c2302b2b3ddc..531fc60d9550 100644 --- a/lib/libc/gen/sysctl.3 +++ b/lib/libc/gen/sysctl.3 @@ -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 diff --git a/sbin/sysctl/sysctl.8 b/sbin/sysctl/sysctl.8 index 4412eddbf6d4..da145510eeff 100644 --- a/sbin/sysctl/sysctl.8 +++ b/sbin/sysctl/sysctl.8 @@ -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 diff --git a/sbin/veriexecctl/veriexecctl.c b/sbin/veriexecctl/veriexecctl.c index 88481ad263c7..7b5c7071920d 100644 --- a/sbin/veriexecctl/veriexecctl.c +++ b/sbin/veriexecctl/veriexecctl.c @@ -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 @@ -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, ¶ms_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(¶ms_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)); diff --git a/sbin/veriexecctl/veriexecctl.h b/sbin/veriexecctl/veriexecctl.h index 3c644001cfa2..ff3e72ea8d73 100644 --- a/sbin/veriexecctl/veriexecctl.h +++ b/sbin/veriexecctl/veriexecctl.h @@ -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 @@ -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_ */ diff --git a/sbin/veriexecctl/veriexecctl_parse.y b/sbin/veriexecctl/veriexecctl_parse.y index 0f2e3049256e..5602f1db3e73 100644 --- a/sbin/veriexecctl/veriexecctl_parse.y +++ b/sbin/veriexecctl/veriexecctl_parse.y @@ -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 @@ -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(¶ms, 0, sizeof(params)); } diff --git a/share/man/man4/options.4 b/share/man/man4/options.4 index d985ab201f07..968ba41a535f 100644 --- a/share/man/man4/options.4 +++ b/share/man/man4/options.4 @@ -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 diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile index 15f48213d6a7..5e1c3ecbc31c 100644 --- a/share/man/man9/Makefile +++ b/share/man/man9/Makefile @@ -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 \ diff --git a/share/man/man9/fileassoc.9 b/share/man/man9/fileassoc.9 new file mode 100644 index 000000000000..c188b6b5a571 --- /dev/null +++ b/share/man/man9/fileassoc.9 @@ -0,0 +1,390 @@ +.\" $NetBSD: fileassoc.9,v 1.1 2006/07/14 18:41:41 elad Exp $ +.\" +.\" Copyright (c) 2006 Elad Efrat +.\" 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. diff --git a/sys/conf/files b/sys/conf/files index 6871a9112721..d6d91b6c26a6 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -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 diff --git a/sys/dev/verified_exec.c b/sys/dev/verified_exec.c index 8a6e1d22b781..de51bf6c00d9 100644 --- a/sys/dev/verified_exec.c +++ b/sys/dev/verified_exec.c @@ -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 @@ -31,9 +31,9 @@ #include #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 @@ -64,8 +64,11 @@ __RCSID("$Id: verified_exec.c,v 1.34 2006/05/25 11:24:00 blymn Exp $\n$NetBSD: v #include #include +#include + /* 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); +} + +void +veriexec_clear(void *data, int file_specific) +{ + if (file_specific) { + struct veriexec_file_entry *vfe = data; + + 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 { + struct veriexec_table_entry *vte = data; + + free(vte, M_TEMP); + } +} + +int +veriexec_delete(struct veriexec_delete_params *params, struct lwp *l) +{ + struct veriexec_table_entry *vte; + struct nameidata nid; + int error; + + NDINIT(&nid, LOOKUP, FOLLOW, UIO_SYSSPACE, params->file, l); + error = namei(&nid); + if (error) + return (error); + + 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_delete(struct veriexec_delete_params *params) +veriexec_query(struct veriexec_query_params *params, struct lwp *l) { - struct veriexec_hashtbl *tbl; - struct veriexec_hash_entry *vhe; - - /* 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); - } else { - tbl = veriexec_tblfind(params->dev); - if (tbl == NULL) - return (ENOENT); - - 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--; - } - } - - return (0); -} - -int -veriexec_query(struct veriexec_query_params *params) -{ - struct veriexec_hash_entry *vhe; + struct veriexec_file_entry *vfe; + struct nameidata nid; int error; - vhe = veriexec_lookup(params->dev, params->ino); - if (vhe == NULL) - return (ENOENT); - - 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)); - error = copyout(params, params->uaddr, sizeof(*params)); + NDINIT(&nid, LOOKUP, FOLLOW, UIO_SYSSPACE, params->file, l); + error = namei(&nid); if (error) return (error); - if (params->fp_bufsize >= vhe->ops->hash_len) { - error = copyout(vhe->fp, params->fp, vhe->ops->hash_len); + + 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) + 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); } diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index 528477cbab98..a5d6ded82275 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -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 -__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 #include +#ifdef FILEASSOC +#include +#endif /* FILEASSOC */ + #include #include @@ -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 diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index 3e664d60b897..4f130d30a15b 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -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 -__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 diff --git a/sys/kern/kern_fileassoc.c b/sys/kern/kern_fileassoc.c new file mode 100644 index 000000000000..023f9ae78e7d --- /dev/null +++ b/sys/kern/kern_fileassoc.c @@ -0,0 +1,435 @@ +/* $NetBSD: kern_fileassoc.c,v 1.1 2006/07/14 18:41:40 elad Exp $ */ + +/*- + * Copyright (c) 2006 Elad Efrat + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); +} diff --git a/sys/kern/kern_verifiedexec.c b/sys/kern/kern_verifiedexec.c index 9618836e1290..cc70bc668709 100644 --- a/sys/kern/kern_verifiedexec.c +++ b/sys/kern/kern_verifiedexec.c @@ -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 @@ -30,7 +30,7 @@ */ #include -__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 #include #include +#include #include 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); +struct veriexec_table_entry * +veriexec_tblfind(struct vnode *vp) +{ + return (fileassoc_tabledata_lookup(vp->v_mount, veriexec_hook)); } -/* Perform a lookup on a hash table. */ -struct veriexec_hash_entry * -veriexec_lookup(dev_t device, ino_t inode) +struct veriexec_file_entry * +veriexec_lookup(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 (NULL); + 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); @@ -459,10 +447,10 @@ veriexec_verify(struct lwp *l, struct vnode *vp, struct vattr *va, return (EPERM); } -out: + 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); } diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index b8936dce129a..619b37502775 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -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 -__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 #include @@ -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 #endif +#ifdef FILEASSOC +#include +#endif /* FILEASSOC */ #ifdef VERIFIED_EXEC #include #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); } diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c index fcd7f7b8a84a..1b37de926500 100644 --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -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 -__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 diff --git a/sys/sys/fileassoc.h b/sys/sys/fileassoc.h new file mode 100644 index 000000000000..c62a171df3bc --- /dev/null +++ b/sys/sys/fileassoc.h @@ -0,0 +1,99 @@ +/* $NetBSD: fileassoc.h,v 1.1 2006/07/14 18:41:40 elad Exp $ */ + +/*- + * Copyright (c) 2006 Elad Efrat + * 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 +#include +#include + +/* 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_ */ diff --git a/sys/sys/verified_exec.h b/sys/sys/verified_exec.h index 50cff558cc36..0286b87f7a65 100644 --- a/sys/sys/verified_exec.h +++ b/sys/sys/verified_exec.h @@ -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 @@ -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. */ - 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. */ +/* 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_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 */