This file is indexed.

/usr/share/qpsmtpd/plugins/virus/kavscanner is in qpsmtpd 0.84-11.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#!/usr/bin/perl -w
# Kasperski-AV plugin.

=head1 NAME

kavscanner - plugin for qpsmtpd which calls the Kasperski anti virus scanner

=head1 DESCRIPTION

Check a mail with the B<kavscanner> and deny if it matches a configured virus
list.

=head1 VERSION

this is B<kavscanner> version 1.0

=head1 CONFIGURATION

Add (perl-)regexps to the F<kav_deny> configuration file, one per line for the
virii you want to block, e.g.:

  I-Worm\.Sober\..*
  I-Worm\.NetSky\..*

NOTE: untested and disabled currently, need volunteers :-)

If this list does not match the virus found in the mail, you may set 
I<bcc_virusadmin viradm@your.company.com> in the plugin config to send a 
B<Bcc:> to the given mail address, i.e. the line 

  kavscanner bcc_virusadmin viradm@your.company.com 

in the F<config/plugin> file instead of just

  kavscanner

Set the location of the binary with 

  kavscanner kavscanner_bin /path/to/kavscanner

(default: F</opt/AVP/kavscanner>), NOTE: this may be broken, you want to
set B<kavscanner_bin> explicitly ;-)

=head1 NOTES

This is a merge of the clam_av plugin for qpsmtpd and qmail-scanner-queue.pl
L<http://qmail-scanner.sourceforge.net/> with my own improvements ;-)
Only tested with kavscanner 4.0.x, and bcc_virusadmin untested, as we have no
use for it currently. I wait for an official change in Qpsmtpd::Transaction
(reset/set the RCPT TO list) to activate and test the currently disabled 
B<to_virusadmin> option.

=cut

use File::Temp qw(tempfile);
use Mail::Address;
 
sub register {
  my ($self, $qp, @args) = @_;

  if (@args % 2) {
    $self->log(LOGWARN, "kavscanner: Wrong number of arguments");
    $self->{_kavscanner_bin} = "/opt/AVP/kavscanner";
  } else {
    my %args = @args;
    foreach my $key (keys %args) {
      my $arg = $key;
      $key =~ s/^/_/;
      $self->{$key} = $args{$arg};
    }
    # Untaint scanner location
    if (exists $self->{_kavscanner_bin} && 
        $self->{_kavscanner_bin} =~ /^(\/[\/\-\_\.a-z0-9A-Z]*)$/) {
      $self->{_kavscanner_bin} = $1;
    } else {
      $self->log(LOGALERT, "FATAL ERROR: Unexpected characters in kavscanner argument");
      exit 3;
    }
  } 
}
 
sub hook_data_post {
  my ($self, $transaction) = @_;
 
  my ($temp_fh, $filename) = tempfile();
  print $temp_fh $transaction->header->as_string;
  print $temp_fh "\n";
  $transaction->body_resetpos;
  while (my $line = $transaction->body_getline) {
    print $temp_fh $line;
  }
  seek($temp_fh, 0, 0);
 
  # Now do the actual scanning!
  my $cmd = $self->{_kavscanner_bin}." -Y -P -B -MP -MD -* $filename 2>&1";
  $self->log(LOGNOTICE, "Running: $cmd");
  my @output = `$cmd`;
  chomp(@output);
 
  my $result = ($? >> 8);
  my $signal = ($? & 127);
 
  unlink($filename);
  close $temp_fh;

  if ($signal) {
    $self->log(LOGWARN, "kavscanner exited with signal: $signal");
    return (DECLINED);
  }

  my $description = 'clean';
  my @infected    = ();
  my @suspicious  = ();
  if ($result > 0) {
    if ($result =~ /^(2|3|4|8)$/) {
      foreach (@output) {
        if (/^.* infected: (.*)$/) {
          # This covers the specific
          push @infected, $1;
        } elsif (/^\s*.* suspicion: (.*)$/) {
          # This covers the potential viruses
          push @suspicious, $1;
        }
      }
      $description = "infected by: ".join(", ",@infected)."; "
                    ."suspicions: ".join(", ", @suspicious);
      # else we may get a veeeery long X-Virus-Details: line or log entry
      $description = substr($description,0,60); 
      $self->log(LOGWARN, "There be a virus! ($description)");
      ### Untested by now, need volunteers ;-)
      #if ($self->qp->config("kav_deny")) {
      #  foreach my $d (keys %{$self->qp->config("kav_deny", "map")}) {
      #    foreach my $v (@infected) {
      #      return(DENY, "Virus found: $description")
      #        if ($v =~ /^$d$/i); 
      #    }
      #    foreach my $s (@suspicious) {
      #      return(DENY, "Virus found: $description")
      #        if ($s =~ /^$d$/i); 
      #    }
      #  }
      #}
      $transaction->header->add('X-Virus-Found', 'Yes');
      $transaction->header->add('X-Virus-Details', $description);
      ### maybe the spamassassin plugin can skip this mail if a virus
      ### was found (and $transaction->notes('virus_flag') exists :))
      ### ...ok, works with our spamassassin plugin version 
      ###   -- hah
      $transaction->notes('virus', $description);
      $transaction->notes('virus_flag', 'Yes');

      #### requires modification of Qpsmtpd/Transaction.pm:
      # if ($self->{_to_virusadmin}) {
      #   my @addrs = ();
      #   foreach (@{$transaction->recipients}) {
      #     push @addr, $_->address;
      #   }
      #   $transaction->header->add('X-Virus-Orig-RcptTo', join(", ", @addrs));
      #   $transaction->set_recipients(@{ Mail::Address->parse($self->{_to_virusadmin}) });
      # } elsif ($self->{_bcc_virusadmin}) {
      if ($self->{_bcc_virusadmin}) {
        foreach ( @{ Mail::Address->parse($self->{_bcc_virusadmin}) } ) {
          $transaction->add_recipient($_);
        }
      }
    } else {
      $self->log(LOGEMERG, "corrupt or unknown Kaspersky scanner/resource problems - exit status $result");
    }
  }
  
  $self->log(LOGINFO, "kavscanner results: $description");
 
  $transaction->header->add('X-Virus-Checked', 'Checked by '.$self->qp->config("me"));
  return (DECLINED);
} 

# vim: ts=2 sw=2 expandtab