[svn:qpsmtpd] rev 448 - in trunk: . plugins/virus

[email protected] 22 Jun 2005 20:06:54 -0000
Newsgroups perl.cvs.qpsmtpd
Message-ID <[email protected]>
Author: jpeacock
Date: Wed Jun 22 13:06:54 2005
New Revision: 448

Added:
   trunk/plugins/virus/sophie
Modified:
   trunk/   (props changed)
Log:
 New AV plugin.  Uses SOPHOS Antivirus via Sophie resident daemon.


Added: trunk/plugins/virus/sophie
==============================================================================
--- (empty file)
+++ trunk/plugins/virus/sophie	Wed Jun 22 13:06:54 2005
@@ -0,0 +1,199 @@
+#!/usr/bin/perl -w
+use IO::Socket;
+
+sub register {
+    my ( $self, $qp, @args ) = @_;
+    $self->register_hook( "data_post", "sophiescan" );
+
+    %{ $self->{"_sophie"} } = @args;
+
+    # Set some sensible defaults
+    $self->{"_sophie"}->{"sophie_socket"} ||= "/var/run/sophie";
+    $self->{"_sophie"}->{"deny_viruses"}  ||= "yes";
+    $self->{"_sophie"}->{"max_size"}      ||= 128;
+}
+
+sub sophiescan {
+    my ( $self, $transaction ) = @_;
+    $DB::single = 1;
+
+    if ( $transaction->body_size > $self->{"_sophie"}->{"max_size"} * 1024 ) {
+        $self->log( LOGNOTICE, "Declining due to body_size" );
+        return (DECLINED);
+    }
+
+    # Ignore non-multipart emails
+    my $content_type = $transaction->header->get('Content-Type');
+    $content_type =~ s/\s/ /g if defined $content_type;
+    unless ( $content_type
+        && $content_type =~ m!\bmultipart/.*\bboundary="?([^"]+)!i )
+    {
+        $self->log( LOGWARN, "non-multipart mail - skipping" );
+        return DECLINED;
+    }
+
+    my $filename = $transaction->body_filename;
+    unless ($filename) {
+        $self->log( LOGWARN, "Cannot process due to lack of filename" );
+        return (DECLINED);    # unless $filename;
+    }
+
+    my $mode = ( stat( $self->spool_dir() ) )[2];
+    if ( $mode & 07077 ) {   # must be sharing spool directory with external app
+        $self->log( LOGWARN,
+            "Changing permissions on file to permit scanner access" );
+        chmod $mode, $filename;
+    }
+
+    my ($SOPHIE, $response);
+    socket(\*SOPHIE, AF_UNIX, SOCK_STREAM, 0)
+    	|| die "Couldn't create socket ($!)\n";
+
+    connect(\*SOPHIE, pack_sockaddr_un $self->{"_sophie"}->{"sophie_socket"})
+	|| die "Couldn't connect() to the socket ($!)\n";
+
+    syswrite(\*SOPHIE, $filename."\n", length($filename)+1);
+    sysread(\*SOPHIE, $response, 256);    
+    close (\*SOPHIE);
+
+    my $virus;
+
+    if ( ($virus) = ( $response =~ m/^1:?(.*)?$/ ) ) {
+        $self->log( LOGERROR, "One or more virus(es) found: $virus" );
+
+        if ( lc( $self->{"_sophie"}->{"deny_viruses"} ) eq "yes" ) {
+            return ( DENY,
+                    "Virus"
+                  . ( $virus =~ /,/ ? "es " : " " )
+                  . "Found: $virus" );
+        }
+        else {
+            $transaction->header->add( 'X-Virus-Found',   'Yes' );
+            $transaction->header->add( 'X-Virus-Details', $virus );
+            return (DECLINED);
+        }
+    }
+
+    $transaction->header->add( 'X-Virus-Checked',
+        "Checked by SOPHIE on " . $self->qp->config("me") );
+
+    return (DECLINED);
+}
+
+=head1 NAME
+
+sophie scanner
+
+=head1 DESCRIPTION
+
+A qpsmtpd plugin for virus scanning using the SOPHOS scan daemon, Sophie.
+
+=head1 RESTRICTIONS
+
+The Sophie scan daemon must have at least read access to the qpsmtpd spool
+directory in order to sucessfully scan the messages.  You can ensure this
+by running Sophie as the same user as qpsmtpd does (by far the easiest
+method) or by doing the following: 
+
+=over 4
+
+=item * Change the group ownership of the spool directory to be a group 
+of which the Sophie user is a member or add the Sophie user to the same group
+as the qpsmtpd user.
+
+=item * Change the permissions of the qpsmtpd spool directory to 0750 (this 
+will emit a warning when the qpsmtpd service starts up, but can be safely
+ignored).
+
+=item * Make sure that all directories above the spool directory (to the
+root) are g+x so that the group has directory traversal rights; it is not
+necessary for the group to have any read rights except to the spool
+directory itself.
+
+=back
+
+It may be helpful to temporary grant the Sophie user a shell and test to
+make sure you can cd into the spool directory and read files located there.
+Remember to remove the shell from the Sophieav user when you are done
+testing.
+
+Note also that the contents of config/spool_dir must be the full path to the
+spool directory (not a relative path) in order for the scanner to locate the
+file.
+
+=head1 INSTALL AND CONFIG
+
+Place this plugin in the plugin/virus directory beneath the standard
+qpsmtpd installation.  If you installed Sophie with the default path, you
+can use this plugin with default options (nothing specified):
+
+=over 4
+
+=item B<Sophie_socket>
+
+Full path to the Sophie socket defaults to /var/run/Sophie.
+
+=item B<deny_viruses>
+
+Whether the scanner will automatically delete messages which have viruses.
+Takes either 'yes' or 'no' (defaults to 'yes').  If set to 'no' it will add
+a header to the message with the virus results.
+
+=item B<max_size>
+
+The maximum size, in kilobytes, of messages to scan; defaults to 128k.
+
+=back
+
+=head1 REQUIREMENTS
+
+This module requires the Sophie daemon, available here:
+
+L<http://www.clanfield.info/sophie/>
+
+which in turn requires the libsavi.so library (available with the Sophos
+Anti-Virus for Linux or Unix).
+
+The following changes to F</etc/sophie.cfg> B<should> be made:
+
+=over 4
+
+=item user: qmaild
+
+Change the "user" parameter to match the qpsmtpd user.
+
+=item group: nofiles
+
+Change the "group" parameter to match the qpsmtpd group.
+
+=item umask: 0001
+
+If you don't change the umask, only the above user/group will be able to scan.
+
+=back
+
+The following changes to F</etc/sophie.savi> B<must> be made:
+
+=over 4
+
+=item Mime: 1
+
+This option will permit the SAVI engine to directly scan e-mail messages.
+
+=back
+
+=head1 AUTHOR
+
+John Peacock <[email protected]>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (c) 2005 John Peacock
+
+Based heavily on the clamav plugin
+
+This plugin is licensed under the same terms as the qpsmtpd package itself.
+Please see the LICENSE file included with qpsmtpd for details.
+
+=cut
+