This file is indexed.

/usr/share/qpsmtpd/plugins/virus/aveclient 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
178
179
180
#!/usr/bin/perl -w
=head1 NAME

aveclient

=head1 DESCRIPTION

This qpsmtpd plugin uses the aveclient of a kaspersky 5.x server-suite. The original kaspersky
aveclient is called within this plugin to connect to the local socket of the aveserver. 
The aveserver runs as a daemon with all virusdefinitions already loaded, what makes scanning veeery
quick and performant without much load.

When a virus is detected, the mail is blocked and the connection is denied! Further configuration 
is simple to be added.

=head1 INSTALL AND CONFIG

Place this plugin in the default plugin directory of your qpsmtpd installation. Normaly you can use 
it with default options (nothing specified):

=over 4

=item B<aveclient>

Optional you may set the path to original aveclient and/or the socket:

=over 4

=item avclient_bin I</path/to/ave/binary>

Set the path to the original aveclient of kaspersky 5.x server-suite.
Default: /opt/kav/bin/aveclient

=item avdaemon_sock I</path/to/socket>

Set the path to the unix socket of the original aveserver of kaspersky 5.x server-suite.
Default: /var/run/aveserver

=item blockonerror I<(1|0)>

Whether to block mails on scanning errors or to accept connections.
Default: 0 (No)

=back

=back

=head1 EXIT CODES OF aveclient (taken from man aveclient)

When launched with the -s option, aveclient returns one of the following codes (if several files to be scanned are indicated  in  the
command line, the return code corresponds to the results of scanning the last file):

0      no viruses have been detected.

1      unable to connect to aveserver.

2      objects with an unknown viral code have been found.

3      suspicious objects have been found.

4      infected objects have been detected.

5      all infected objects have been disinfected.

6      scan results are unavailable: encrypted or password protected file.

7      system error launching the application (file not found, unable to read the file).

8      scan results are unavailable: file is corrupted or input/output error.

9      some of the required parameters are missing from the command line.

=head1 VERSION

0.1rc first proof of concept.
How is load and performance on larger systems? This is tested whith aprox. 900 Clients
on a small RH-System (AMD, 768 Mhz, 512 MB) MAXCLIENTS set to 40.

=head1 AUTHOR

Adopted by Marcus Spiegel <aveclient@uscreen.de> from kavscanner plugin of Hanno Hecker.

THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

=cut

use File::Temp qw(tempfile);
use Mail::Address;
 
sub register {
	my ($self, $qp, @args) = @_;
	
	# defaults to be used
	$self->{_avclient_bin} 	= "/opt/kav/bin/aveclient";
	$self->{_avdaemon_sock} = "/var/run/aveserver";
	$self->{_blockonerror} 	= 0;
	
	# parse optional arguments
	my %args = @args;
	foreach my $key (keys %args) {
		my $arg = $key;
		$key =~ s/^/_/;
		$self->{$key} = $args{$arg};
	}

	# Untaint client location
	# socket will be tested during scan (response-code)
	if (exists $self->{_avclient_bin} && $self->{_avclient_bin} =~ /^(\/[\/\-\_\.a-z0-9A-Z]*)$/) {
		$self->{_avclient_bin} = $1;
	} else {
		$self->log(LOGALERT, "FATAL ERROR: No binary aveclient found: '".$self->{_avclient_bin}."'");
		exit 3;
	}
}
 
sub hook_data_post {
	my ($self, $transaction) = @_;
	my ($temp_fh, $filename) = tempfile();
	my $description = 'clean';
	
	# a temporary file is needed to be scanned
	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 scan this file
	my $cmd = $self->{_avclient_bin}." -p ".$self->{_avdaemon_sock}." -s $filename 2>&1";

	my @output = `$cmd`;
	chomp(@output);
	
	my $result = ($? >> 8);
	my $signal = ($? & 127);
	
	# tidy up a bit
	unlink($filename);
	close $temp_fh;
	
	# check if something went wrong
	if ($signal) {
		$self->log(LOGERROR, "kavscanner exited with signal: $signal");
		return (DECLINED);
	}
	
	# either we found a virus or something went wrong
	if ($result > 0) {
		if ($result =~ /^(2|3|4|6|8)$/) {
			
			# ok a somewhat virus was found
			shift @output;
			$description = "REPORT: ".join(", ",@output);
			$self->log(LOGWARN, "Virus found! ($description)");
			
			# we don't want to be disturbed be these, so block mail and DENY connection
			return(DENY, "Virus found: $description");
			
		} else {
			$self->log(LOGCRIT, "aveserver: no viruses have been detected.") if($result =~ /^0$/);
			$self->log(LOGCRIT, "aveserver: system error launching the application (file not found, unable to read the file).") if($result =~ /^0$/);
			$self->log(LOGCRIT, "aveserver: some of the required parameters are missing from the command line.") if($result =~ /^9$/);
			return(DENY, "Unable to scan for virus, please contact admin of ".$self->qp->config("me").", if you feel this is an error!") if $self->{_blockonerror};
		}
	}
	
	$self->log(LOGINFO, "kavscanner results: $description");
	$transaction->header->add('X-Virus-Checked', 'Checked by Kaspersky on '.$self->qp->config("me"));
	return (DECLINED);
}