Tuesday, March 29, 2016

Configuring On-Access Scanning in ClamAV

A Brief History

With the release of ClamAV 0.99, users saw a slew of new features aimed at enhancing ClamAV’s core functionalities. Included in these features were the improvements to ClamAV’s long-neglected on-access scanning capabilities. Ages ago, ClamAV supported on-access scanning via the now-defunct Dazuko kernel module. When development on Dazuko ended in 2011, the ClamAV team began searching for a suitable replacement. The team soon landed on the recently stable fanotify API that first shipped with the 2.6.37 Linux kernel. By November of 2011 a barebones fanotify-based on-access scanner had been completed, but it wouldn’t see public release until ClamAV 0.98.

Fast forward to ClamAV version 0.99four years after the creation of that first prototypeand users found themselves presented with a completely reworked on-access scanning system that now leveraged not only fanotify, but inotify as well. While ClamAV’s documentation had been updated to help navigate users through the transition, a number of people reported problems they were having with ClamAV’s on-access scanner. Almost invariably, these issues came down to either improper configuration or system-level limitations. This guide seeks to provide users interested in ClamAV’s on-access scanning with a definitive resource on the topicstraight from the horse’s mouth, so to speak.

System Requirements

fanotify
On-access scanning, requires a system running a Linux kernel (version >= 3.8) with fanotify compiled in. Check the kernel configuration for fanotify’s presence by running:
$ cat /boot/config-<kernel_version> | grep FANOTIFY

Results if fanotify is available:
CONFIG_FANOTIFY=y

Results if fanotify can prevent malicious file access attempts:
CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y

If this is seen:
CONFIG_FANOTIFY=y
# CONFIG_FANOTIFY_ACCESS_PERMISSIONS is not set

Then on-access scanning will be constrained to notify-only mode and will be unable to prevent access to malicious files, since fanotify lacks the ability to block events on the system.

inotify
To use the dynamic directory determination (DDD) system introduced in 0.99, ensure inotify is configured with enough watchpoints. By default, inotify can watch for changes in 8192 directories at any one time. If more directories than that need protection, run the following commandreplacing <NUM> with the maximum number of directories to be watched:
$ echo <NUM> | sudo tee -a /proc/sys/fs/inotify/max_user_watches

Overview of Configuration Options

The clamd.conf configuration file contains a number of options which modify the behaviour of ClamAV’s on-access scanner. Each configuration option is listed below alongside its intended purpose and any caveats of which users should remain cognizant.

ScanOnAccess
Purpose: Enables on-access scanning under clamd if set to “yes”. If set to ”no”, all other on-access configurations options will be ignored.

Caveats: clamd must be run as root.

OnAccessMountPath
Purpose: Watches the entirety of one or more specified mount points and notifies the user of any malicious events caught by fanotify.

Caveats: Users must specify a valid mount point. If this option is enabled, clamd will disable fanotify prevention to keep users from accidentally locking up their system. Enabling this option will also disable the DDD system and all configuration options which rely on that system.

OnAccessMaxFileSize
Purpose: Allows the user to stop clamd from scanning files larger than the specified size. If set to “0”, clamd will scan all files regardless of size.

Caveats: On some kernel versions and architectures, fanotify is known to fail when blocking access to files larger than 4GB. ClamAV will alert users if this problem occurs.

OnAccessIncludePath
Purpose: The files within the any specified path(s) will be monitored for access attempts. If the DDD system is enabled, clamd will watch this path recursively, i.e., the specified directory and all subdirectories will be monitored. If the DDD system is disabled, only the topmost directory will be watched and all subdirectories will be ignored.

Caveats: This option will be ignored if OnAccessMountPath is enabled. As an additional precaution, if DDD is enabled, clamd will prevent users from specifying “/” as an include path.

OnAccessExcludePath
Purpose: Recursively removes the given directory from the set of directories monitored by clamd.

Caveats: If the DDD system is disabled, this option is ignored.

OnAccessExcludeUID
Purpose: When fanotify catches an event, clamd will check given UIDs against the captured pid. If a match is found, the event is ignored and no scanning is performed.

Caveats: There aren't any known caveats with this option.

OnAccessDisableDDD
Purpose: This option allows users to turn off the DDD system if they prefer the legacy on-access scanning behaviour that was available in 0.98. If DDD is disabled OnAccessIncludePath will not recursively watch paths, making OnAccessExcludePath a redundant option.

Caveats: If OnAccessMountPath is enabled, clamd will act as though DDD is disabled.

OnAccessPrevention
Purpose: Flags fanotify to block any triggered events on monitored files, which allows ClamAV to scan affected files to determine if those events should be allowed to proceed.

Caveats: The kernel must be compiled with “CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y” for this option to function. As a precaution, if OnAccessMountPath is enabled, clamd automatically disables this option to stop the user from accidentally locking up their system. Only access and open events can be blocked by fanotify.

OnAccessExtraScanning
Purpose: Provides DDD users with inotify event coverage, which allows for scans on newly created, moved, or renamed files.

Caveats: Since this option relies on the inotify framework maintained by the DDD system, it is ignored if OnAccessDisableDDD is “yes”.

Configuration Examples

This guide ends with a few simple clamd.conf configuration examples which will satisfy a number of general use cases. Though there are only four, these examples can readily be expanded upon or adapted to fit more targeted use cases.

Watch entire filesystem
ScanOnAccess yes
OnAccessMountPath /
OnAccessExcludeUID 0

Details: This will set fanotify to watch the entire filesystem in real-time and trigger ClamAV to run scans on any files opened, accessed, or closed except by the root user. While clamd will report any viruses found during this scanning, fanotify will not perform any blocking or prevention.

Watching select users’ home directories
ScanOnAccess yes
OnAccessIncludePath /home
OnAccessExcludePath /home/user2
OnAccessExcludePath /home/user4
OnAccessExtraScanning yes

Details: Like the previous example, fanotify is not flagged to prevent events which occur on malicious files. However, further scanning coverage is provided thanks to OnAccessExtraScanning leveraging the inotify events caught by the DDD system.

Protecting a directory recursively
ScanOnAccess yes
OnAccessIncludePath /home/user/Downloads
OnAccessPrevention yes
OnAccessExtraScanning yes

Details: Here fanotify will block any triggered events, allowing only clean files to be opened, or otherwise accessed. This configuration will also immediately alert the user if a malicious file is inadvertently downloaded.

Protecting multiple dirs non-recursively
ScanOnAccess yes
OnAccessIncludePath /home/user/dir1
OnAccessIncludePath /home/user/dir2
OnAccessPrevention yes
OnAccessDisableDDD yes

Details: Similarly to legacy behaviour, only the top level of dir1 and dir2 will be protected in this case. The contents of all subdirectories will be ignored and extra scanning via inotify events is not possible since DDD is disabled.

9 comments :

  1. If I read your post correctly, on-access scanning won't work for any other *nix OS (e.g. FreeBSD, Solaris) but Linux distributions with a kernel version greater than 3.8? If so, are there plans to implement this feature in other *nixes?

    ReplyDelete
  2. We'd love to make OnAccess Scanning available on as many platforms as possible! However, we've been unable to find a full-featured, cross-platform, actively supported api that enables us to expand beyond fanotify configured Linux distros. If you can point us towards a viable option, we'd be excited to start investigating expanding OnAccess Scanning to additional *nix platforms in a future ClamAV release.

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. Thanks for this. One issue, clamd runs, parses the config and then exits with this:
    ScanOnAccess: onas_fan_exit(), signal 10
    Found this function on git (at https://git.devuan.org/dev1fanboy/clamav/blob/1606455f66c538380f9b67235fc250232c553c0d/clamd/onaccess_fan.c) which makes it look like a problem with DDD?
    :
    static void onas_fan_exit(int sig)
    {
    logg("*ScanOnAccess: onas_fan_exit(), signal %d\n", sig);

    close(onas_fan_fd);

    if (ddd_pid > 0) {
    pthread_kill(ddd_pid, SIGUSR1);
    pthread_join(ddd_pid, NULL);
    }

    pthread_exit(NULL);
    logg("ScanOnAccess: stopped\n");
    }

    ReplyDelete
  5. Getting this error shortly after clamd service start:
    ScanOnAccess: onas_fan_exit(), signal 10

    the kernel parameters are set, but it is on a VM
    CONFIG_FANOTIFY=y
    CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y
    Have you seen this before?

    ReplyDelete
  6. 2 small typo's;
    OnAccessExlcudePath
    OnAccessExlcudeUID

    ofcourse this should be
    OnAccessExcludePath
    OnAccessExcludeUID

    ReplyDelete
  7. Hello,
    It seems that ScanOnAcces is not working for me in this case.

    /etc/clamav/clamd.conf
    ScanOnAccess true
    CrossFilesystems true
    VirusEvent /etc/clamav/virusevent.d/block
    OnAccessIncludePath /users/home
    OnAccessIncludePath /tmp
    ExcludePath /proc

    /var/log/clamav/clamav.log
    Mon Nov 7 11:06:13 2016 -> Stopping on-access scan
    Mon Nov 7 11:06:14 2016 -> Pid file removed.
    Mon Nov 7 11:06:14 2016 -> --- Stopped at Mon Nov 7 11:06:14 2016
    Mon Nov 7 11:06:14 2016 -> Socket file removed.
    Mon Nov 7 11:13:56 2016 -> +++ Started at Mon Nov 7 11:13:56 2016
    Mon Nov 7 11:13:56 2016 -> Received 0 file descriptor(s) from systemd.
    Mon Nov 7 11:13:56 2016 -> clamd daemon 0.99.2 (OS: linux-gnu, ARCH: x86_64, CPU: x86_64)
    Mon Nov 7 11:13:56 2016 -> Running as user root (UID 0, GID 0)
    Mon Nov 7 11:13:56 2016 -> Log file size limited to 4294967295 bytes.
    Mon Nov 7 11:13:56 2016 -> Reading databases from /var/lib/clamav
    Mon Nov 7 11:13:56 2016 -> Not loading PUA signatures.
    Mon Nov 7 11:13:56 2016 -> Bytecode: Security mode set to "TrustSigned".
    Mon Nov 7 11:14:03 2016 -> Loaded 5051511 signatures.
    Mon Nov 7 11:14:04 2016 -> LOCAL: Unix socket file /var/run/clamav/clamd.ctl
    Mon Nov 7 11:14:04 2016 -> LOCAL: Setting connection queue length to 15
    Mon Nov 7 11:14:04 2016 -> Limits: Global size limit set to 104857600 bytes.
    Mon Nov 7 11:14:04 2016 -> Limits: File size limit set to 26214400 bytes.
    Mon Nov 7 11:14:04 2016 -> Limits: Recursion level limit set to 16.
    Mon Nov 7 11:14:04 2016 -> Limits: Files limit set to 10000.
    Mon Nov 7 11:14:04 2016 -> Limits: MaxEmbeddedPE limit set to 10485760 bytes.
    Mon Nov 7 11:14:04 2016 -> Limits: MaxHTMLNormalize limit set to 10485760 bytes.
    Mon Nov 7 11:14:04 2016 -> Limits: MaxHTMLNoTags limit set to 2097152 bytes.
    Mon Nov 7 11:14:04 2016 -> Limits: MaxScriptNormalize limit set to 5242880 bytes.
    Mon Nov 7 11:14:04 2016 -> Limits: MaxZipTypeRcg limit set to 1048576 bytes.
    Mon Nov 7 11:14:04 2016 -> Limits: MaxPartitions limit set to 50.
    Mon Nov 7 11:14:04 2016 -> Limits: MaxIconsPE limit set to 100.
    Mon Nov 7 11:14:04 2016 -> Limits: MaxRecHWP3 limit set to 16.
    Mon Nov 7 11:14:04 2016 -> Limits: PCREMatchLimit limit set to 10000.
    Mon Nov 7 11:14:04 2016 -> Limits: PCRERecMatchLimit limit set to 5000.
    Mon Nov 7 11:14:04 2016 -> Limits: PCREMaxFileSize limit set to 26214400.
    Mon Nov 7 11:14:04 2016 -> Archive support enabled.
    Mon Nov 7 11:14:04 2016 -> Algorithmic detection enabled.
    Mon Nov 7 11:14:04 2016 -> Portable Executable support enabled.
    Mon Nov 7 11:14:04 2016 -> ELF support enabled.
    Mon Nov 7 11:14:04 2016 -> Mail files support enabled.
    Mon Nov 7 11:14:04 2016 -> OLE2 support enabled.
    Mon Nov 7 11:14:04 2016 -> PDF support enabled.
    Mon Nov 7 11:14:04 2016 -> SWF support enabled.
    Mon Nov 7 11:14:04 2016 -> HTML support enabled.
    Mon Nov 7 11:14:04 2016 -> XMLDOCS support enabled.
    Mon Nov 7 11:14:04 2016 -> HWP3 support enabled.
    Mon Nov 7 11:14:04 2016 -> Self checking every 3600 seconds.
    Mon Nov 7 11:14:04 2016 -> ScanOnAccess: notifying only for access attempts.
    Mon Nov 7 11:14:04 2016 -> ScanOnAccess: Max file size limited to 5242880 bytes
    Mon Nov 7 11:14:05 2016 -> ScanOnAccess: Protecting directory '/users/home' (and all sub-directories)
    Mon Nov 7 11:14:05 2016 -> ScanOnAccess: Protecting directory '/tmp' (and all sub-directories)
    Mon Nov 7 11:14:05 2016 -> ERROR: ScanOnAccess: Could not watch path '/users/home', Success


    ReplyDelete
  8. @Olivier COSTES it has been fixed in :

    commit 4edbeea04c9f9fd7efb4a22914becdac1d4df1b9
    Author: Mickey Sola
    Date: Thu Jul 21 15:36:11 2016 -0400

    bb11602 - fixing case where on access scanning would erroneously classify sockets and other objects as directories

    ReplyDelete