Messages in this thread Patch in this message | | | From | Eric Paris <> | Subject | [RFC 1/5] [TALPA] Hooking points and kernel interception | Date | Mon, 4 Aug 2008 17:00:36 -0400 |
| |
Core of the functionality is to insert hooking points at appropriate places and pass events for vetting.
Vetting works via three chain of filters. First an interception is passed to a evaluation chain whose purpose is to decide whether the access should be allowed or denied. Depending on the outcome either allow or deny filter chain is run next.
Signed-off-by: Eric Paris <eparis@redhat.com> ---
Documentation/talpa/design.txt | 266 +++++++++++++++++++++++++++++++ fs/open.c | 10 ++ include/linux/talpa.h | 88 ++++++++++ security/Kconfig | 1 + security/Makefile | 2 + security/talpa/Kconfig | 9 + security/talpa/Makefile | 7 + security/talpa/talpa.h | 64 ++++++++ security/talpa/talpa_allow_calls.h | 5 + security/talpa/talpa_deny_calls.h | 5 + security/talpa/talpa_evaluation_calls.h | 6 + security/talpa/talpa_interceptor.c | 116 ++++++++++++++ 12 files changed, 579 insertions(+), 0 deletions(-)
diff --git a/Documentation/talpa/design.txt b/Documentation/talpa/design.txt new file mode 100644 index 0000000..5df07bf --- /dev/null +++ b/Documentation/talpa/design.txt @@ -0,0 +1,266 @@ + +Background +++++++++++ +There is a consensus in the security industry that protecting against malicious +files (viruses, root kits, spyware, ad-ware, ...) by the way of so-called +on-access scanning is usable and reasonable approach. Currently the Linux kernel +does not offer a completely suitable interface to implement such security solutions. +Present solutions involve overwriting function pointers in the LSM, in filesystem +operations, in the sycall table, and other fragile hacks. The purpose of this +project is to create a fast, clean interface for userspace programs to look for +malware when files are accessed. This malware may be ultimately intended for +this or some other Linux machine or may be malware intended to attack a host +running a different operating system and is merely in transit across the Linux +server. Since there are almost an infinite number of ways in which information +can enter and exit a server it is not seen as reasonable to move these checks to +all the applications at the boundary (MTA, NFS, CIFS, SSH, rsync, et al.) to look +for such malware on at the border. + +Speed is of particular interest for those who have it compiled into the kernel +but have no userspace client. There must be no measurable performance hit to +just compiling this into the kernel. + +Security vendors, Linux distributors and other interested parties have come together +on the malware-list mailing list to discuss this problem and see if they can work +together to propose a solution. During these talks couple of requirement sets were +posted with the aim of fleshing out common needs as a prerequisite of creating an +interface prototype. + +Collated requirements ++++++++++++++++++++++ + 1. Intercept file opens (exec also) for vetting (block until decision is made) and allow some userspace black magic to make decisions. + 2. Intercept file closes for scanning post access + 3. Cache scan results so the same file is not scanned on each and every access + 4. Ability to flush the cache and cause all files to be re-scanned when accessed + 5. Define which filesystems are cacheable and which are not + 6. Scan files directly not relying on path. Avoid races and problems with namespaces, chroot, containers, etc. + 7. Report other relevant file, process and user information associated with each interception + 8. Report file pathnames to userspace (relative to process root, current working directory) + 9. Mark a processes as exempt from on access scanning + 10. Exclude sub-trees from scanning based on filesystem (exclude procfs, sysfs, devfs) + 11. Exclude sub-trees from scanning based on filesystem path + 12. Include only certain sub-trees from scanning based on filesystem path + 13. Register more than one userspace client in which case behavior is restrictive + + +Discussion of requirements +++++++++++++++++++++++++++ +The initial patch set with NOT meet all of these 'requirements.' Some will be +implemented at a later time and some will never be implemented. Specifics are +detailed below. There is no intention to (abu)use the LSM for this purpose. +The LSM provides complete internal kernel mandatory access controls. It is not +intended for userspace scanning and detection. Users should not be forced to +choose between an in kernel mandatory access control policy and this additional +userspace file access. LSM stacking is NOT as option as has been demonstrated +repeatedly. + +1., 2. Basic interception +------------------------- +Core requirement is to intercept access to files and prevent it if malicious +content is detected. This is done on open, not on read. It may be possible +to do read time checking with minimal performance impact although not currently +implemented. This means that the following race is possible + + Process1 Process2 + - open file RD + - open file WR + - write virus data (1) + - read virus data + +*note that any open after (1) will get properly vetted. At this time the +likelyhood of this being a problem vs the performance impact of scanning on +read and the increased complexity of the code means this is left out. This +should not be a problem for local executables as writes to files opened to be run +typically return ETXTBSY. + +To accomplish that two hooks were inserted, on file open in __dentry_open and in +filp_close on file close. In both cases the file object in question is passed as a +parameter for further processing. In case of an open the operation can actually be +blocked, while closes are always immediately successful and will not cause additional +blocking. Results of a close are returned to the kernel asynchronously and may be +used to cache answers to speed up a future open. + +Interception processing is done by way of three chains of filters. Access requests +are first send to the "evaluation" chain. Depending on the results of the evaluation +the decision is then send to either the allow chain or the deny chain. + +There are three basic responses each filter can make - to be indifferent or either +allow or deny access to the file. The filter may also allow or deny access to a +file while not caching that result. + +One of the most important filters in the evaluation chain implements an interface +through which an userspace process can register and receive vetting requests. +Userspace process opens a misc character device to express its interest and then +receives binary structures from that device describing basic interception information. +After file contents have been scanned a vetting response is sent by writing a different +binary structure back to the device and the intercepted process continues its execution. +These are not done over network sockets and no endian conversions are done. The client +and the kernel must have the same endian configuration. + +3., 4. Caching +--------------- +To avoid scanning unchanged files on every access which would be very bad for +performance some sort of caching is needed. Although possible to implement a +cache in userspace having two context switches required for every open is clearly +not fast. We implemented it per inode object as a serial number compared with +a single global monotonically increasing system serial number. + +The cache filter is inserted into the evaluation chain before the userspace +client filter and if the inode serial number is equal to the system one it allows +access to the file. + +If the file is seen for the first time, has been modified, or for any other reason +has a serial number less than the system one the cache filter will be 'indifferent' +and processing of the given vetting request will continue down the evaluation chain. +When some filter (only Userspace in the first patch set) allows access to a file its +inode serial number is set to the system global which effectively makes it cached. +Also, when a write access is gained for a file the serial number will automatically +be reset as well as when any process actually writes to that file. + +Cache flushing is possible by simply increasing the global system serial number. + +Both positive and negative vetting results are cached by the means of positive and +negative serial numbers. + +This method of caching has minimal impact on system resources while providing maximal +effectiveness and simple implementation. + +5. Fine-grained caching +----------------------- +It is necessary to select which filesystems can be safely cached and which must +not be. For example it is not a good idea to allow caching of network filesystems +because their content can be changed invisibly. Disk based and some virtual +filesystems can be cached safely on the other hand. + +This first proposal only partially implements this requirement. Only block device +backed filesystems will be cached while there is no way to enable caching for +things like tmpfs. Improving this is left out of the initial prototype. Although +there may be additional work to implement caching for certain FS types there is +no plan to greatly increase the scope of the cache granularity. There is no +plan to cache based on the operation or things of that nature. Caching of +this nature can be implemented in userspace if the vendor so chooses. We +include only a minimal safe cache for performance reasons. + +6. Direct access to file content +-------------------------------- +When an userspace daemon receives a vetting request, it also receives a new RO +file descriptor which provides direct access to the inode in question. This is +to enable access to the file regardless of it accessibility from the scanner +environment (consider process namespaces, chroot's, NFS). The userspace client +is responsible for closing this file when it is finished scanning. + +7. Other reporting +------------------ +Along with the fd being installed in the scanning process the process gets a +binary structure of data including: + ++ uint32_t version; ++ uint32_t type; ++ int32_t fd; ++ uint32_t operation; ++ uint32_t flags; ++ uint32_t mode; ++ uint32_t uid; ++ uint32_t gid; ++ uint32_t tgid; ++ uint32_t pid; + +8. Path name reporting +---------------------- +When a malicious content is detected in a file it is important to be able to +report its location so the user or system administrator can take appropriate actions. + +This is implemented in a amazingly simple way which will hopefully avoid the +controversy of some other solutions. Path name is only needed for reporting purposes +and it is obtained by reading the symlink of the given file descriptor in /proc. Its as +simple as userspace calling: + +snprintf(link, sizeof(link), "/proc/self/fd/%d", details.fd); +ret = readlink(link, buf, sizeof(buf)-1); + +9. Process exclusion +-------------------- +Sometimes it is necessary to exclude certain processes from being intercepted. For +example it might be a userspace root kit scanner which would not be able to find +root kits if access to them was blocked by the on-access scanner. + +To facilitate that we have created a special file a process can open and register +itself as excluded. A flag is then put into its kernel structure (task_struct) +which makes it excluded from scanning. + +This implementation is very simple and provides greatest performance. In the proposed +implementation access to the exclusion device is controlled though permissions on +the device node which are not sufficient. An LSM call will need to be made for this +type or access in a later patch. + +10. Filesystem exclusions +------------------------- +One pretty important optimization is not to scan things like /proc, /sys or similar. +Basically all filesystems where user can not store arbitrary, potentially malicious, +content could and should be excluded from scanning. + +This interface prototype implements it as a run-time configurable list of filesystem +names. Again it is a filter in the evaluation chain which can allow access before +the request gets routed to the userspace client. + +This will not be implemented in the first patch set but should be soon to follow. +It is done by simply comparing strings between those supplied and the s_type->name +field in an associated superblock. + +11. Path exclusions +------------------- +The need for exclusions can be demonstrated with an example of a MySQL server. It's +data files are frequently modified which means they would need to be constantly +rescanned which is very bad for performance. Also, it is most often not even +possible to reasonably scan them. Therefore the best solution is not to scan +its database store which can simply be implemented by excluding the store subdirectory. + +It is a relatively simple implementation which allows run-time configuration of +a list of sub directories or files to exclude. Exclusion paths are relative to +each process root. So for example if we want to exclude /var/lib/mysql/ and we +have a mysql running in a chroot where from the outside that directory actually +lives in /chroot/mysql/var/lib/mysql, /var/lib/mysql should actually be added +to the exclusion list. + +This is also not included in the initial patch set but will be coming shortly after. + +12. Path Inclusions +------------------- + +Path-based inclusions are not implemented due to concerns with hard-linked files +both inside and outside the included directories. It is too easy to fall into +a sense of false security with path inclusions since the pathname is almost +meaningless. If a vendor feels this is particularly important for them they +will have to implement it in userspace by use of a judicious list of exclusion +filters. + + +13. Multiple client registration with restrictive behavior +----------------------------------------------------------- +This is currently not implemented. Multiple clients can register but they will be +used for (crappy) load balancing only. Not all will be called for a single interception. +Only one of the registered clients will process a single interception. Desire here +is to enable multiple clients servicing interceptions in parallel for performance +and reliability reasons. + +Requirement for serial and restrictive behavior would be slightly more complicated +to implement because we would want to keep the current behavior as well. Or in other +words we would need to have groups of multiple clients, where each interception +would go through one client from each group with the desired restrictive behavior. + +This may be left for a future implementation for simplicity reasons but I find it +unlikely. If a vendor needs to send requests to multiple scanners they should be +able to implement that serialization in userspace. I see no need for an in kernel +event dispatcher. Note that the audit system had this same need and has done iti +as a userspace event dispatcher. We have also seen in the LSM that restrictive +access stacking is not as easy as it sounds and has been abandoned. + +Closing remarks +--------------- +Although some may argue some of the filters are not necessary or may better be +implemented in userspace, we think it is better to have them in kernel primarily +for performance reasons. Secondly, it is all simple code not introducing much +baggage or risk into the kernel itself. The most complex filter and the only +one with locking ramifications is the userspace client vetting which calls into +dentry_open() on both open and close operations. There is no locking around +caching or process exclusions or other work. diff --git a/fs/open.c b/fs/open.c index 07da935..1133005 100644 --- a/fs/open.c +++ b/fs/open.c @@ -29,6 +29,7 @@ #include <linux/rcupdate.h> #include <linux/audit.h> #include <linux/falloc.h> +#include <linux/talpa.h> int vfs_statfs(struct dentry *dentry, struct kstatfs *buf) { @@ -842,6 +843,13 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, } } + if (!IS_ERR(f)) { + error = talpa_vet_file_open(f, flags); + if (error) { + fput(f); + f = ERR_PTR(error); + } + } return f; cleanup_all: @@ -1082,6 +1090,8 @@ int filp_close(struct file *filp, fl_owner_t id) return 0; } + talpa_vet_file_close(filp); + if (filp->f_op && filp->f_op->flush) retval = filp->f_op->flush(filp, id); diff --git a/include/linux/talpa.h b/include/linux/talpa.h new file mode 100644 index 0000000..4ae05ba --- /dev/null +++ b/include/linux/talpa.h @@ -0,0 +1,88 @@ +/* + * Copyright 2008 Sophos Plc + * Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __LINUX_TALPA_H__ +#define __LINUX_TALPA_H__ + +#ifndef __KERNEL__ +#include <stdint.h> +#else +#include <linux/types.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * enum talpa_operation - type of intercepted operation + * @TALPA_OPEN:open of a filesystem object + * @TALPA_CLOSE:closing of a filesystem object + */ +enum talpa_operation { + TALPA_OPEN = 0, + TALPA_CLOSE = 1, +}; + +#ifdef __cplusplus +} +#endif + +#ifdef __KERNEL__ + +#include <linux/fs.h> + +#ifdef CONFIG_TALPA + +/* Internal interface. */ + +/** + * talpa_vet_file_open - called to vet an open operation + * @file:file which is being opened + * @flags:flags passed when opening + * + * This is to be called from the appropriate place in the VFS layer + * to catch all filesystem operations which provide access to + * file objects. + */ +extern int talpa_vet_file_open(struct file *file, int flags); + +/** + * talpa_vet_file_close - called to vet files on close + * @file:file which is being closed + * + * This is to be called from the appropriate place in the VFS layer + * to inspect file content just before they will be closed. + */ +extern void talpa_vet_file_close(struct file *file); + +#else /* !CONFIG_TALPA*/ + +static inline int talpa_vet_file_open(struct file *file, int flags) +{ + return 0; +} + +static inline void talpa_vet_file_close(struct file *file) +{ +} + +#endif /* !CONFIG_TALPA */ + +#endif /* __KERNEL__ */ +#endif /* __LINUX_TALPA_H__ */ diff --git a/security/Kconfig b/security/Kconfig index 5592939..cb32796 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -117,6 +117,7 @@ config SECURITY_DEFAULT_MMAP_MIN_ADDR source security/selinux/Kconfig source security/smack/Kconfig +source security/talpa/Kconfig endmenu diff --git a/security/Makefile b/security/Makefile index f654260..2d5d3a8 100644 --- a/security/Makefile +++ b/security/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_KEYS) += keys/ subdir-$(CONFIG_SECURITY_SELINUX) += selinux subdir-$(CONFIG_SECURITY_SMACK) += smack +subdir-$(CONFIG_TALPA) += talpa # always enable default capabilities obj-y += commoncap.o @@ -16,3 +17,4 @@ obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o +obj-$(CONFIG_TALPA) += talpa/built-in.o diff --git a/security/talpa/Kconfig b/security/talpa/Kconfig new file mode 100644 index 0000000..0b8449e --- /dev/null +++ b/security/talpa/Kconfig @@ -0,0 +1,9 @@ +config TALPA + bool "File content vetting interface" + default n + help + Talpa is a high-performance filesystem access interception + package providing facilities for user-mode daemons/programs + to 'vet' filesystem operations before they are executed. + + If you are unsure how to answer this question, answer Y. diff --git a/security/talpa/Makefile b/security/talpa/Makefile new file mode 100644 index 0000000..676fc90 --- /dev/null +++ b/security/talpa/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for Talpa +# + +obj-$(CONFIG_TALPA) := talpa.o + +talpa-y := talpa_interceptor.o diff --git a/security/talpa/talpa.h b/security/talpa/talpa.h new file mode 100644 index 0000000..2c4fb6f --- /dev/null +++ b/security/talpa/talpa.h @@ -0,0 +1,64 @@ +/* + * Copyright 2008 Sophos Plc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef __TALPA_H__ +#define __TALPA_H__ + +#include <linux/fs.h> +#include <linux/talpa.h> + +/* Talpa filter interface definitions. */ + +/** + * enum talpa_action - filter response + * @TALPA_NEXT: do not care or do not know what to do + * @TALPA_ALLOW: operation should be allowed + * @TALPA_DENY: operation should be denied + * @TALPA_TIMEOUT: decision could not be made in a reasonable time + * @TALPA_ERROR: decision could not be made due to an error + * + * Each filters returns it's response from one of these values signalling + * it's opinion on the vetting operation in progress. + */ +enum talpa_action { + TALPA_NEXT = 0, + TALPA_ALLOW = 1, + TALPA_DENY = 2, + TALPA_TIMEOUT = 3, + TALPA_ERROR = 4, +}; + +/** + * struct talpa_file_vetting - object passed to filters for vetting + * @operation: type of operation being vetted + * @file: file pointer for the vetted object + * @flags: as passed to open(2) + * @authoritative: set to non-zero when decision has been made based on file content + * @code: error code if applicable + * + * This object is created by the interceptor and passed to all filters + * to do the vetting. + */ +struct talpa_file_vetting { + enum talpa_operation operation; + struct file *file; + int flags; + unsigned int authoritative; + int code; +}; + +#endif /* __TALPA_H__ */ diff --git a/security/talpa/talpa_allow_calls.h b/security/talpa/talpa_allow_calls.h new file mode 100644 index 0000000..eb24482 --- /dev/null +++ b/security/talpa/talpa_allow_calls.h @@ -0,0 +1,5 @@ +#include "talpa.h" + +static inline void talpa_allow_calls(struct talpa_file_vetting *tfv) +{ +} diff --git a/security/talpa/talpa_deny_calls.h b/security/talpa/talpa_deny_calls.h new file mode 100644 index 0000000..011d5c2 --- /dev/null +++ b/security/talpa/talpa_deny_calls.h @@ -0,0 +1,5 @@ +#include "talpa.h" + +static inline void talpa_deny_calls(struct talpa_file_vetting *tfv) +{ +} diff --git a/security/talpa/talpa_evaluation_calls.h b/security/talpa/talpa_evaluation_calls.h new file mode 100644 index 0000000..367a149 --- /dev/null +++ b/security/talpa/talpa_evaluation_calls.h @@ -0,0 +1,6 @@ +#include "talpa.h" + +static inline int talpa_evaluation_calls(struct talpa_file_vetting *tfv) +{ + return TALPA_NEXT; +} diff --git a/security/talpa/talpa_interceptor.c b/security/talpa/talpa_interceptor.c new file mode 100644 index 0000000..bde8a59 --- /dev/null +++ b/security/talpa/talpa_interceptor.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2008 Sophos Plc + * Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/errno.h> +#include <linux/talpa.h> + +#include "talpa_evaluation_calls.h" +#include "talpa_allow_calls.h" +#include "talpa_deny_calls.h" + +#include "talpa.h" + +static struct kmem_cache *tfv_cache; /* Cache for the above structure */ + +/* Forward declare implementation functions. */ +static int talpa_vet_file(struct file *file, int flags, enum talpa_operation op); + +/* Externally visible interface. */ +int talpa_vet_file_open(struct file *file, int flags) +{ + return talpa_vet_file(file, flags, TALPA_OPEN); +} + +void talpa_vet_file_close(struct file *file) +{ + talpa_vet_file(file, file->f_flags, TALPA_CLOSE); +} + +/* Main work function */ +static int talpa_vet_file(struct file *file, int flags, enum talpa_operation op) +{ + struct talpa_file_vetting *tfv = NULL; + enum talpa_action action = TALPA_NEXT; + int ret = -ENOSYS; + + /* Try to allocate vetting details or block access. + Cache will not be available before initcalls are run so + allow all access until then. */ + if (unlikely(!tfv_cache)) + return 0; + + tfv = kmem_cache_zalloc(tfv_cache, GFP_KERNEL); + if (!tfv) + return -ENOMEM; + + tfv->operation = op; + tfv->file = file; + tfv->flags = flags; + + action = talpa_evaluation_calls(tfv); + + /* Response can never be authoritative if response was indifferent. */ + if (action == TALPA_NEXT) + tfv->authoritative = 0; + + /* call post-eval function depedning on result */ + if (action == TALPA_ALLOW || action == TALPA_NEXT) + talpa_allow_calls(tfv); + else + talpa_deny_calls(tfv); + + /* Choose return code depending on the outcome. */ + switch (action) { + case TALPA_ALLOW: + case TALPA_NEXT: + ret = 0; + break; + case TALPA_DENY: + ret = -EACCES; + break; + case TALPA_TIMEOUT: + ret = -ETIME; + break; + case TALPA_ERROR: + if (tfv->code) + ret = tfv->code; + else + ret = -EACCES; + break; + } + + /* Free stuff we have (or might have) allocated. */ + kmem_cache_free(tfv_cache, tfv); + + return ret; +} + +static __init int talpa_interceptor_init(void) +{ + /* No point in running with a security subsystem which does + not work so we will panic if cannot allocate the cache here. */ + tfv_cache = kmem_cache_create("talpa_file_vetting", sizeof(struct talpa_file_vetting), + SLAB_PANIC, 0, NULL); + return 0; +} +__initcall(talpa_interceptor_init); -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |