cbpolicyd 21.2 KB
Newer Older
1
#!/usr/bin/perl
Nigel Kukard's avatar
Nigel Kukard committed
2
# Cluebringer policy daemon
Wepongo's avatar
Wepongo committed
3
# Copyright (C) 2009-2015, AllWorldIT
Nigel Kukard's avatar
Nigel Kukard committed
4
# Copyright (C) 2008, LinuxRulz
Nigel Kukard's avatar
Nigel Kukard committed
5
# Copyright (C) 2007, Nigel Kukard  <nkukard@lbsd.net>
Nigel Kukard's avatar
Nigel Kukard committed
6
#
Nigel Kukard's avatar
Nigel Kukard committed
7
8
9
10
# 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 of the License, or
# (at your option) any later version.
Nigel Kukard's avatar
Nigel Kukard committed
11
#
Nigel Kukard's avatar
Nigel Kukard committed
12
13
14
15
# 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.
Nigel Kukard's avatar
Nigel Kukard committed
16
#
Nigel Kukard's avatar
Nigel Kukard committed
17
18
19
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21
22
23
24


use strict;
use warnings;

25
26
use lib('/usr/local/lib/cbpolicyd-2.1','/usr/lib/cbpolicyd-2.1',
		'/usr/lib64/cbpolicyd-2.1','awitpt');
27
28
29
30
31

package cbp;


use base qw(Net::Server::PreFork);
32
use Config::IniFiles;
33
use Getopt::Long;
34
use Sys::Syslog;
Nigel Kukard's avatar
Nigel Kukard committed
35

36
use cbp::version;
37

Nigel Kukard's avatar
Nigel Kukard committed
38
use cbp::config;
39
use cbp::logging;
Nigel Kukard's avatar
Nigel Kukard committed
40
41
use awitpt::db::dbilayer;
use awitpt::cache;
42
use cbp::tracking;
43
44
use cbp::protocols;

45

46

47

48
49
50
# Override configuration
sub configure {
	my $self = shift;
51
52
53
54
55
56
	my $server = $self->{'server'};
	my $cfg;
	my $cmdline;
	my $inifile;


57
58
59
	# We're being called from somewhere else, maybe a protocol?
	return if (@_);

60
	# Set defaults
61
	$cfg->{'config_file'} = "/etc/cbpolicyd/cluebringer.conf";
62
	$cfg->{'cache_file'} = '/var/run/cbpolicyd/cache';
63
	$cfg->{'track_sessions'} = 0;
64

Nigel Kukard's avatar
Nigel Kukard committed
65
66
	$server->{'timeout_idle'} = 1015;
	$server->{'timeout_busy'} = 115;
67
	$server->{'background'} = "yes";
68
	$server->{'pid_file'} = "/var/run/cbpolicyd/cbpolicyd.pid";
69
	$server->{'log_level'} = 2;
70
	$server->{'log_file'} = "/var/log/cbpolicyd/cbpolicyd.log";
71

72
	$server->{'proto'} = "tcp";
73
74
	$server->{'host'} = "*";
	$server->{'port'} = 10031;
Nigel Kukard's avatar
Nigel Kukard committed
75

76
77
78
79
80
	$server->{'min_servers'} = 4;
	$server->{'min_spare_servers'} = 4;
	$server->{'max_spare_servers'} = 12;
	$server->{'max_servers'} = 25;
	$server->{'max_requests'} = 1000;
81
82
83
84
85
86
87
88

	# Parse command line params
	%{$cmdline} = ();
	GetOptions(
			\%{$cmdline},
			"help",
			"config:s",
			"debug",
89
			"fg",
90
	);
91

92
93
94
95
96
97
98
99
	# Check for some args
	if ($cmdline->{'help'}) {
		$self->displayHelp();
		exit 0;
	}
	if (defined($cmdline->{'config'}) && $cmdline->{'config'} ne "") {
		$cfg->{'config_file'} = $cmdline->{'config'};
	}
100

101
102
103
104
105
	# Check config file exists
	if (! -f $cfg->{'config_file'}) {
		print(STDERR "ERROR: No configuration file '".$cfg->{'config_file'}."' found!\n");
		exit 1;
	}
Nigel Kukard's avatar
Nigel Kukard committed
106

107
	# Use config file, ignore case
Nigel Kukard's avatar
Nigel Kukard committed
108
	tie my %inifile, 'Config::IniFiles', (
109
			-file => $cfg->{'config_file'},
110
			-nocase => 1
111
	) or die "Failed to open config file '".$cfg->{'config_file'}."': ".join("\n",@Config::IniFiles::errors);
Nigel Kukard's avatar
Nigel Kukard committed
112
113
	# Copy config
	my %config = %inifile;
114
115
	# FIXME: This now generates a warning as Config::Inifiles doesn't implement UNTIE
	# untie(%inifile);
Nigel Kukard's avatar
Nigel Kukard committed
116

117
	# Pull in params for the server
Nigel Kukard's avatar
Nigel Kukard committed
118
	my @server_params = (
119
			'log_level','log_file',
120
			'proto', 'host', 'port',
121
			'cidr_allow', 'cidr_deny',
Nigel Kukard's avatar
Nigel Kukard committed
122
			'pid_file',
123
			'user', 'group',
Nigel Kukard's avatar
Nigel Kukard committed
124
			'timeout_idle', 'timeout_busy',
125
			'background',
Nigel Kukard's avatar
Nigel Kukard committed
126
			'min_servers',
127
128
129
130
			'min_spare_servers',
			'max_spare_servers',
			'max_servers',
			'max_requests',
131
	);
Nigel Kukard's avatar
Nigel Kukard committed
132
133
	foreach my $param (@server_params) {
		$server->{$param} = $config{'server'}{$param} if (defined($config{'server'}{$param}));
134
	}
135
136
137

	# Fix up these ...
	if (defined($server->{'cidr_allow'})) {
138
		my @lst = split(/[,\s;]+/,$server->{'cidr_allow'});
139
140
141
		$server->{'cidr_allow'} = \@lst;
	}
	if (defined($server->{'cidr_deny'})) {
142
		my @lst = split(/[,\s;]+/,$server->{'cidr_deny'});
143
144
145
		$server->{'cidr_deny'} = \@lst;
	}

Nigel Kukard's avatar
Nigel Kukard committed
146
147
	# Split off modules
	if (!defined($config{'server'}{'modules'})) {
148
149
150
151
		die "Server configuration error: 'modules' not found";
	}
	if (!defined($config{'server'}{'protocols'})) {
		die "Server configuration error: 'protocols' not found";
Nigel Kukard's avatar
Nigel Kukard committed
152
	}
153
	# Split off modules
154
155
156
	if (ref($config{'server'}{'modules'}) eq "ARRAY") {
		foreach my $module (@{$config{'server'}{'modules'}}) {
			$module =~ s/\s+//g;
Nigel Kukard's avatar
Nigel Kukard committed
157
158
159
			# Skip comments
			next if ($module =~ /^#/);
			$module = "modules/$module";
160
161
162
163
164
165
166
167
168
169
			push(@{$cfg->{'module_list'}},$module);
		}
	} else {
		my @moduleList = split(/\s+/,$config{'server'}{'modules'});
		foreach my $module (@moduleList) {
			# Skip comments
			next if ($module =~ /^#/);
			$module = "modules/$module";
			push(@{$cfg->{'module_list'}},$module);
		}
Nigel Kukard's avatar
Nigel Kukard committed
170
	}
171
	# Split off protocols
172
173
174
	if (ref($config{'server'}{'protocols'}) eq "ARRAY") {
		foreach my $module (@{$config{'server'}{'protocols'}}) {
			$module =~ s/\s+//g;
Nigel Kukard's avatar
Nigel Kukard committed
175
176
177
			# Skip comments
			next if ($module =~ /^#/);
			$module = "protocols/$module";
178
			push(@{$cfg->{'module_list'}},$module);
Nigel Kukard's avatar
Nigel Kukard committed
179
		}
180
181
182
183
184
185
186
187
	} else {
		my @protocolList = split(/\s+/,$config{'server'}{'protocols'});
		foreach my $module (@protocolList) {
			# Skip comments
			next if ($module =~ /^#/);
			$module = "protocols/$module";
			push(@{$cfg->{'module_list'}},$module);
		}
Nigel Kukard's avatar
Nigel Kukard committed
188
	}
189

190
191
192
193
194
195
196
197
198
	# Override
	if ($cmdline->{'debug'}) {
		$server->{'log_level'} = 4;
		$cfg->{'debug'} = 1;
	}

	# If we set on commandline for foreground, keep in foreground
	if ($cmdline->{'fg'} || (defined($config{'server'}{'background'}) && $config{'server'}{'background'} eq "no" )) {
		$server->{'background'} = undef;
199
		$server->{'log_file'} = undef;
200
201
202
	} else {
		$server->{'setsid'} = 1;
	}
203
204
205
206
207
208
209
210
211

	# Loop with logging detail
	if (defined($config{'server'}{'log_detail'})) {
		# Lets see what we have to enable
		foreach my $detail (split(/[,\s;]/,$config{'server'}{'log_detail'})) {
			$cfg->{'logging'}{$detail} = 1;
		}
	}

212
	# Check log_mail
213
214
215
216
217
218
219
	if (defined($config{'server'}{'log_mail'}) && $config{'server'}{'log_mail'} ne "main") {
		# COMPATIBILITY
		if ($config{'server'}{'log_mail'} eq "maillog") {
			$cfg->{'log_mail'} = 'mail@syslog:native';
		} else {
			$cfg->{'log_mail'} = $config{'server'}{'log_mail'};
		}
220
	}
221
222
223
224
225
226
227

	# Check if the user specified a cache_file in the config
	if (defined($config{'server'}{'cache_file'})) {
		$cfg->{'cache_file'} = $config{'server'}{'cache_file'};
	}


228
229
230
	# Save our config and stuff
	$self->{'config'} = $cfg;
	$self->{'cmdline'} = $cmdline;
Nigel Kukard's avatar
Nigel Kukard committed
231
	$self->{'inifile'} = \%config;
232
233
234
235
}



236
237
238
# Run straight after ->run
sub post_configure_hook {
	my $self = shift;
239
	my $log_mail = $self->{'config'}{'log_mail'};
240

Nigel Kukard's avatar
Nigel Kukard committed
241

Nigel Kukard's avatar
Nigel Kukard committed
242
	$self->log(LOG_NOTICE,"[CBPOLICYD] PolicyD v2 / Cluebringer - v".VERSION);
243

244
	$self->log(LOG_NOTICE,"[CBPOLICYD] Initializing system modules.");
245
	# Init config
Nigel Kukard's avatar
Nigel Kukard committed
246
247
	cbp::config::Init($self);
	# Init caching engine
248
249
250
251
252
	awitpt::cache::Init($self,{
		'cache_file' => $self->{'config'}{'cache_file'},
		'cache_file_user' => $self->{'server'}->{'user'},
		'cache_file_group' => $self->{'server'}->{'group'}
	});
253
	$self->log(LOG_NOTICE,"[CBPOLICYD] System modules initialized.");
Nigel Kukard's avatar
Nigel Kukard committed
254

255
	$self->log(LOG_NOTICE,"[CBPOLICYD] Module load started...");
Nigel Kukard's avatar
Nigel Kukard committed
256
257
	# Load modules
	foreach my $module (@{$self->{'config'}{'module_list'}}) {
258
259
260
261
		# Split off dir and mod name
		$module =~ /^(\w+)\/(\w+)$/;
		my ($mod_dir,$mod_name) = ($1,$2);

Nigel Kukard's avatar
Nigel Kukard committed
262
263
		# Load module
		my $res = eval("
264
265
			use cbp::${mod_dir}::${mod_name};
			plugin_register(\$self,\"${mod_name}\",\$cbp::${mod_dir}::${mod_name}::pluginInfo);
Nigel Kukard's avatar
Nigel Kukard committed
266
267
		");
		if ($@ || (defined($res) && $res != 0)) {
268
			$self->log(LOG_WARN,"[CBPOLICYD] Error loading plugin $module ($@)");
269
270
		}
	}
271
	$self->log(LOG_NOTICE,"[CBPOLICYD] Module load done.");
Nigel Kukard's avatar
Nigel Kukard committed
272

273
274
275
276
277
278
	# Report if session tracking is on
	if ($self->{'config'}{'track_sessions'}) {
		$self->log(LOG_NOTICE,"[CBPOLICYD] Session tracking is ENABLED.");
	} else {
		$self->log(LOG_NOTICE,"[CBPOLICYD] Session tracking is DISABLED.");
	}
279

280
281
282
283
284
285
286
	# Check if we have some custom logging...
	if (defined($log_mail)) {
		# More flexible method to configure logging
		if ($log_mail =~ /^([^@]+)(?:@([^:]+))?(?:\:(\S+))?$/) {
			my $facility = $1;
			my $method = defined($2) ? $2 : 'syslog';
			my $destination = $3;
Nigel Kukard's avatar
Nigel Kukard committed
287

288
289
290
291
			# Check method of logging
			if ($method eq "syslog") {
				$destination = 'native' if (!defined($destination));
				$facility = 'mail' if (!defined($facility));
Nigel Kukard's avatar
Nigel Kukard committed
292

293
294
295
296
297
298
299
300
301
				$self->log(LOG_DEBUG,"[CBPOLICYD] Opening syslog, destination = '$destination', facility = '$facility'.");
				# We may have some args to pass to setlogsock
				my @syslogArgs = split(/,/,$destination);
				if (!Sys::Syslog::setlogsock(@syslogArgs)) {
					$self->log(LOG_ERR,"[CBPOLICYD] Failed to set log socket: $!");
				}
				if (!Sys::Syslog::openlog("cbpolicyd",'pid|ndelay',$facility)) {
					$self->log(LOG_ERR,"[CBPOLICYD] Failed to open syslog socket: $!");
				}
Nigel Kukard's avatar
Nigel Kukard committed
302

303
304
305
			} else {
				$self->log(LOG_WARN,"[CBPOLICYD] Value of 'log_mail' not understood. Method '$method' is invalid.");
			}
Nigel Kukard's avatar
Nigel Kukard committed
306

307
308
309
		} else {
			$self->log(LOG_WARN,"[CBPOLICYD] Value '$log_mail' of 'log_mail' not understood.");
		}
310
	}
Nigel Kukard's avatar
Nigel Kukard committed
311
312
313
314
315
316
317
318
319
320
}


# Register plugin info
sub plugin_register {
	my ($self,$module,$info) = @_;


	# If no info, return
	if (!defined($info)) {
321
		$self->log(LOG_WARN,"[CBPOLICYD] Plugin info not found for module => $module");
Nigel Kukard's avatar
Nigel Kukard committed
322
		return -1;
323
	}
Nigel Kukard's avatar
Nigel Kukard committed
324

Nigel Kukard's avatar
Nigel Kukard committed
325
326
327
328
329
330
	# Set real module name & save
	$info->{'Module'} = $module;
	push(@{$self->{'modules'}},$info);

	# If we should, init the module
	if (defined($info->{'init'})) {
Nigel Kukard's avatar
Nigel Kukard committed
331
		$info->{'init'}($self);
Nigel Kukard's avatar
Nigel Kukard committed
332
333
334
	}

	return 0;
335
336
337
}


Nigel Kukard's avatar
Nigel Kukard committed
338
339
340
# Initialize child
sub child_init_hook
{
Nigel Kukard's avatar
Nigel Kukard committed
341
342
	my $self = shift;

Nigel Kukard's avatar
Nigel Kukard committed
343

Nigel Kukard's avatar
Nigel Kukard committed
344
	$self->SUPER::child_init_hook();
Nigel Kukard's avatar
Nigel Kukard committed
345

346
	$self->log(LOG_DEBUG,"[CBPOLICYD] Starting up caching engine");
Nigel Kukard's avatar
Nigel Kukard committed
347
	awitpt::cache::connect($self);
Nigel Kukard's avatar
Nigel Kukard committed
348

349
350
351
352
	# This is the database connection timestamp, if we connect, it resets to 0
	# if not its used to check if we must kill the child and try a reconnect
	$self->{'client'}->{'dbh_status'} = time();

Nigel Kukard's avatar
Nigel Kukard committed
353
	# Init system stuff
Nigel Kukard's avatar
Nigel Kukard committed
354
	$self->{'client'}->{'dbh'} = awitpt::db::dbilayer::Init($self,'cbp');
355
356
357
358
	if (defined($self->{'client'}->{'dbh'})) {
		# Check if we succeeded
		if (!($self->{'client'}->{'dbh'}->connect())) {
			# If we succeeded, record OK
359
			$self->{'client'}->{'dbh_status'} = 0;
360
		} else {
361
			$self->log(LOG_WARN,"[CBPOLICYD] Failed to connect to database: ".$self->{'client'}->{'dbh'}->Error()." ($$)");
362
363
		}
	} else {
Nigel Kukard's avatar
Nigel Kukard committed
364
		$self->log(LOG_WARN,"[CBPOLICYD] Failed to Initialize: ".awitpt::db::dbilayer::internalError()." ($$)");
Nigel Kukard's avatar
Nigel Kukard committed
365
366
367
368
	}
}


Nigel Kukard's avatar
Nigel Kukard committed
369
370


Nigel Kukard's avatar
Nigel Kukard committed
371
372
373
374
# Destroy the child
sub child_finish_hook {
	my $self = shift;
	my $server = $self->{'server'};
375
	my $log_cache = defined($self->{'config'}{'logging'}{'cache'});
Nigel Kukard's avatar
Nigel Kukard committed
376

377
	$self->SUPER::child_finish_hook();
Nigel Kukard's avatar
Nigel Kukard committed
378

379
380
	$self->log(LOG_DEBUG,"[CBPOLICYD] Caching engine: hits = ".awitpt::cache::getCacheHits().", misses = ".
			awitpt::cache::getCacheMisses()) if ($log_cache);
381
	$self->log(LOG_DEBUG,"[CBPOLICYD] Shutting down caching engine ($$)");
Nigel Kukard's avatar
Nigel Kukard committed
382
	awitpt::cache::disconnect($self);
Nigel Kukard's avatar
Nigel Kukard committed
383
384
385
}


386
387
388
# Process requests we get
sub process_request {
	my $self = shift;
389
	my $server = $self->{'server'};
390
	my $log = defined($self->{'config'}{'logging'}{'modules'});
391

Robert Anderson's avatar
Robert Anderson committed
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
	# Check for unix/tcp peer and set peer_type
	my $sock = $self->{'server'}->{'client'};
	if ($sock->NS_proto eq 'UNIX') {
		$server->{'peer_type'} = "UNIX";

		# Some defaults for debugging, these are undef if UNIX
		$server->{'peeraddr'} = "";
		$server->{'peerport'} = $sock->NS_unix_path;
		$server->{'sockaddr'} = "";
		$server->{'sockport'} = "";
	} elsif ($sock->NS_proto eq 'TCP') {
		$server->{'peer_type'} = "TCP";
	} else {
		$self->log(LOG_WARN,"[CBPOLICYD] Unknown peer type, expected UNIX / TCP. Rejecting.");
		return;
	}
408

Nigel Kukard's avatar
Nigel Kukard committed
409
410
411
	# How many times did we pipeline...
	my $policyRequests = 0;

412
413
414
	#
	# Loop till we fill up the buffer
	#
Nigel Kukard's avatar
Nigel Kukard committed
415
416
417
418
419
420

	# Beginning label, we do pipelining ...
CONN_READ:

	# Found module, set to 1 if found, 0 if otherwize
	my $found = 0;
Nigel Kukard's avatar
Nigel Kukard committed
421

422
423
	# Buffer
	my $buf = "";
Nigel Kukard's avatar
Nigel Kukard committed
424

425
426
427
428
	# Create an FDSET for use in select()
	my $fdset = "";
	vec($fdset, fileno(STDIN), 1) = 1;
	while (1) {
429
430
		# Loop with modules
		foreach my $module ( sort { $b->{'priority'} <=> $a->{'priority'} }  @{$self->{'modules'}} ) {
431

432
433
			# Skip over if we don't have a check...
			next if (!defined($module->{'protocol_check'}));
434

435
436
437
438
			# Check protocol
			my $res = $module->{'protocol_check'}($self,$buf);
			if (defined($res) && $res == 1) {
				$found = $module;
439
			}
440
441
		}

442
443
444
		# Last if found
		last if ($found);

Nigel Kukard's avatar
Nigel Kukard committed
445
446
447
		# We need to store this cause we use it below a few times
		my $bufLen = length($buf);

448
		# Again ... too large
Nigel Kukard's avatar
Nigel Kukard committed
449
		if ($bufLen > 16*1024) {
450
			$self->log(LOG_WARN,"[CBPOLICYD] Request too large from => Peer: ".$server->{'peeraddr'}.":".$server->{'peerport'}.", Local: ".
451
					$server->{'sockaddr'}.":".$server->{'sockport'});
452
453
			return;
		}
Nigel Kukard's avatar
Nigel Kukard committed
454
455
456
457
458
459
460
461
462
463

		# Setup timeout
		my $timeout;
		# If buffer length > 0, its a busy connection
		if ($bufLen > 0) {
			$timeout = $server->{'timeout_busy'};
		# Else its idle
		} else {
			$timeout = $server->{'timeout_idle'};
		}
Nigel Kukard's avatar
Nigel Kukard committed
464

465
		# Check for timeout....
Nigel Kukard's avatar
Nigel Kukard committed
466
		my $n = select($fdset,undef,undef,$timeout);
467
		if (!$n) {
Nigel Kukard's avatar
Nigel Kukard committed
468
			$self->log(LOG_NOTICE,"[CBPOLICYD] Timed out after ".$timeout."s from => Peer: ".$server->{'peeraddr'}.":".
Nigel Kukard's avatar
Nigel Kukard committed
469
					$server->{'peerport'}.", Local: ".$server->{'sockaddr'}.":".$server->{'sockport'});
470
471
			return;
		}
Nigel Kukard's avatar
Nigel Kukard committed
472

473
		# Read in 8kb
Nigel Kukard's avatar
Nigel Kukard committed
474
		$n = sysread(STDIN,$buf,8192,$bufLen);
475
476
		if (!$n) {
			my $reason = defined($n) ? "Client closed connection" : "sysread[$!]";
Nigel Kukard's avatar
Nigel Kukard committed
477
			$self->log(LOG_DEBUG,"[CBPOLICYD] $reason => Peer: ".$server->{'peeraddr'}.":".$server->{'peerport'}.", Local: ".
478
479
480
481
					$server->{'sockaddr'}.":".$server->{'sockport'});
			return;
		}
	}
482
483
	# Check if a protocol handler wasn't found...
	if (!$found) {
Nigel Kukard's avatar
Nigel Kukard committed
484
		$self->log(LOG_WARN,"[CBPOLICYD] Request not understood => Peer: ".$server->{'peeraddr'}.":".$server->{'peerport'}.", Local: ".
485
486
487
488
			$server->{'sockaddr'}.":".$server->{'sockport'});
		return;
	}

489
490
	# Set protocol handler
	$server->{'_protocol_handler'} = $found;
491
492
493

	# If we have a init function, call it before processing...
	$server->{'_protocol_handler'}->{'protocol_init'}($self) if (defined($server->{'_protocol_handler'}->{'protocol_init'}));
Nigel Kukard's avatar
Nigel Kukard committed
494

495
	# Process buffer
496
	my $request = $server->{'_protocol_handler'}->{'protocol_parse'}($self,$buf);
497

498
499
	# Check data is ok...
	if ((my $res = $server->{'_protocol_handler'}->{'protocol_validate'}($self,$request))) {
Nigel Kukard's avatar
Nigel Kukard committed
500
		$self->log(LOG_WARN,"[CBPOLICYD] Protocol data validation error, $res");
501
502
503
504
505
506
507
		$self->protocol_response(PROTO_ERROR);
		print($self->protocol_getresponse());
		return;
	}

	# Data mangling...
	$request->{'sender'} = lc($request->{'sender'});
508
	$request->{'recipient'} = lc($request->{'recipient'}) if (defined($request->{'recipient'}));
509
	$request->{'sasl_username'} = lc($request->{'sasl_username'}) if (defined($request->{'sasl_username'}));
510

511
512
	# Internal data
	$request->{'_timestamp'} = time();
Robert Anderson's avatar
Robert Anderson committed
513
514
515
516
517

	# If this is a TCP peer type then it has a peer address
	if ($server->{'peer_type'} eq "TCP") {
		$request->{'_peer_address'} = $server->{'peeraddr'};
	}
Nigel Kukard's avatar
Nigel Kukard committed
518
519


520
	# Check if we got connected, if not ... bypass
521
	if ($self->{'client'}->{'dbh_status'} > 0) {
522
523
		my $action;

524
		$self->log(LOG_WARN,"[CBPOLICYD] Client in BYPASS mode due to DB connection failure!");
525
		# Check bypass mode
526
		if (!defined($self->{'inifile'}{'database'}{'bypass_mode'})) {
527
			$self->log(LOG_ERR,"[CBPOLICYD] No bypass_mode specified for failed database connections, defaulting to tempfail");
528
529
530
			$self->protocol_response(PROTO_DB_ERROR);
			$action = "tempfail";
		# Check for "tempfail"
531
		} elsif (lc($self->{'inifile'}{'database'}{'bypass_mode'}) eq "tempfail") {
Nigel Kukard's avatar
Nigel Kukard committed
532
			$self->protocol_response(PROTO_DB_ERROR);
533
534
			$action = "tempfail";
		# And for "bypass"
535
		} elsif (lc($self->{'inifile'}{'database'}{'bypass_mode'}) eq "pass") {
Nigel Kukard's avatar
Nigel Kukard committed
536
			$self->protocol_response(PROTO_PASS);
537
			$action = "pass";
Nigel Kukard's avatar
Nigel Kukard committed
538
539
540
541
542
		# Lasty for invalid
		} else {
			$self->log(LOG_ERR,"[CBPOLICYD] bypass_mode is invalid, defaulting to tempfail");
			$self->protocol_response(PROTO_DB_ERROR);
			$action = "tempfail";
543
		}
Nigel Kukard's avatar
Nigel Kukard committed
544

545
546
547
548
549
550
551
		$self->maillog("module=Core, action=$action, host=%s, from=%s, to=%s, reason=db_failure_bypass",
				$request->{'client_address'} ? $request->{'client_address'} : "unknown",
				$request->{'helo_name'} ? $request->{'helo_name'} : "",
				$request->{'sender'} ? $request->{'sender'} : "unknown",
				$request->{'recipient'} ? $request->{'recipient'} : "unknown");

		print($self->protocol_getresponse());
552
553

		# Check if we need to reconnect or not
554
		my $timeout = $self->{'inifile'}{'database'}{'bypass_timeout'};
555
556
557
558
559
560
561
562
563
564
565
566
567
568
		if (!defined($timeout)) {
			$self->log(LOG_ERR,"[CBPOLICYD] No bypass_timeout specified for failed database connections, defaulting to 120s");
			$timeout = 120;
		}
		# Get time left
		my $timepassed = $request->{'_timestamp'} - $self->{'client'}->{'dbh_status'};
		# Then check...
		if ($timepassed >= $timeout) {
			$self->log(LOG_NOTICE,"[CBPOLICYD] Client BYPASS timeout exceeded, reconnecting...");
			exit 0;
		} else {
			$self->log(LOG_NOTICE,"[CBPOLICYD] Client still in BYPASS mode, ".( $timeout - $timepassed )."s left till next reconnect");
			return;
		}
569
570
	}

571
	# Setup database handle
Nigel Kukard's avatar
Nigel Kukard committed
572
	awitpt::db::dblayer::setHandle($self->{'client'}->{'dbh'});
573

574
575
576
577
	# Grab session data
	my $sessionData = getSessionDataFromRequest($self,$request);
	if (ref $sessionData ne "HASH") {
		$self->log(LOG_DEBUG,"[CBPOLICYD:$$] Error getting session data");
578
579
		$self->protocol_response(PROTO_ERROR);
		print($self->protocol_getresponse());
580
		return;
581
	}
582

Nigel Kukard's avatar
Nigel Kukard committed
583
584
585
586
	# Increment counter
	$policyRequests++;

	$self->log(LOG_INFO,"[CBPOLICYD] Got request #$policyRequests" . ($policyRequests > 1 ? " (pipelined)" : ""));
587

Nigel Kukard's avatar
Nigel Kukard committed
588
	# Loop with modules
589
	foreach my $module ( sort { $b->{'priority'} <=> $a->{'priority'} }  @{$self->{'modules'}} ) {
590

591
		# Skip over if we don't have a check...
592
593
		next if (!defined($module->{'request_process'}));

594
		$self->log(LOG_DEBUG,"[CBPOLICYD] Running module: ".$module->{'name'}) if ($log);
Nigel Kukard's avatar
Nigel Kukard committed
595

596
597
		# Run request in eval
		my $res;
598
		eval {
599
			$res = $module->{'request_process'}($self,$sessionData);
600
601
602
		};
		# Check results
		if ($@) {
603
			$self->log(LOG_ERR,"[CBPOLICYD] Error running module request_process(): $@");
604
			$res = $self->protocol_response(PROTO_ERROR);
605
		}
Nigel Kukard's avatar
Nigel Kukard committed
606

607
608
609
610
		# Check responses
		if (!defined($res)) {
			$res = $self->protocol_response(PROTO_ERROR);
			last;
Nigel Kukard's avatar
Nigel Kukard committed
611

612
		} elsif ($res == CBP_SKIP) {
Nigel Kukard's avatar
Nigel Kukard committed
613
			$self->log(LOG_DEBUG,"[CBPOLICYD] Module '".$module->{'name'}."' returned CBP_SKIP") if ($log);
614
			next;
615

616
		} elsif ($res == CBP_CONTINUE) {
Nigel Kukard's avatar
Nigel Kukard committed
617
			$self->log(LOG_DEBUG,"[CBPOLICYD] Module '".$module->{'name'}."' returned CBP_CONTINUE") if ($log);
618
			next;
Nigel Kukard's avatar
Nigel Kukard committed
619

620
		} elsif ($res == CBP_STOP) {
Nigel Kukard's avatar
Nigel Kukard committed
621
			$self->log(LOG_DEBUG,"[CBPOLICYD] Module '".$module->{'name'}."' returned CBP_STOP") if ($log);
622
			last;
Nigel Kukard's avatar
Nigel Kukard committed
623

624
625
626
627
628
		} elsif ($res == CBP_ERROR) {
			$self->log(LOG_ERR,"[CBPOLICYD] Error returned from module '".$module->{'name'}."'");
			last;
		}
	}
Nigel Kukard's avatar
Nigel Kukard committed
629

630
	$self->log(LOG_DEBUG,"[CBPOLICYD] Done with modules") if ($log);
Nigel Kukard's avatar
Nigel Kukard committed
631

632
633
634
	# Update session data
	my $res = updateSessionData($self,$sessionData);
	if ($res) {
635
		$self->log(LOG_ERR,"[CBPOLICYD] Error updating session data");
636
637
638
		$self->protocol_response(PROTO_ERROR);
	}

639
640
	# Grab and return response
	my $response = $self->protocol_getresponse();
641
	print($response);
Nigel Kukard's avatar
Nigel Kukard committed
642
643
644

	# Carry on with pipelining?
	goto CONN_READ;
645
646
647
}


Nigel Kukard's avatar
Nigel Kukard committed
648
649
650
651
# Initialize child
sub server_exit
{
	my $self = shift;
652
	my $log_mail = $self->{'config'}{'log_mail'};
Nigel Kukard's avatar
Nigel Kukard committed
653

Nigel Kukard's avatar
Nigel Kukard committed
654

655
	$self->log(LOG_DEBUG,"Destroying system modules.");
Nigel Kukard's avatar
Nigel Kukard committed
656
	# Destroy cache
Nigel Kukard's avatar
Nigel Kukard committed
657
	awitpt::cache::Destroy($self);
658
659
	$self->log(LOG_DEBUG,"System modules destroyed.");

660
	# Check if we using syslog
661
	if (defined($log_mail)) {
662
663
664
665
		$self->log(LOG_DEBUG,"Closing syslog.");
		Sys::Syslog::closelog();
		$self->log(LOG_DEBUG,"Syslog closed.");
	};
Nigel Kukard's avatar
Nigel Kukard committed
666

667
	# Parent exit
Nigel Kukard's avatar
Nigel Kukard committed
668
669
670
671
672
673
674
675
676
	$self->SUPER::server_exit();
}



# Slightly better logging
sub log
{
	my ($self,$level,$msg,@args) = @_;
677
678
679
680
681
682
683
684
685
686
687
688
689

	# Check log level and set text
	my $logtxt = "UNKNOWN";
	if ($level == LOG_DEBUG) {
		$logtxt = "DEBUG";
	} elsif ($level == LOG_INFO) {
		$logtxt = "INFO";
	} elsif ($level == LOG_NOTICE) {
		$logtxt = "NOTICE";
	} elsif ($level == LOG_WARN) {
		$logtxt = "WARNING";
	} elsif ($level == LOG_ERR) {
		$logtxt = "ERROR";
Nigel Kukard's avatar
Nigel Kukard committed
690
	}
691
692
693
694
695
696
697
698

	# Parse message nicely
	if ($msg =~ /^(\[[^\]]+\]) (.*)/s) {
		$msg = "$1 $logtxt: $2";
	} else {
		$msg = "[CORE] $logtxt: $msg";
	}

699
700
701
702
703
	# If we have args, this is more than likely a format string & args
	if (@args > 0) {
		$msg = sprintf($msg,@args);
	}
	$self->SUPER::log($level,"[".$self->log_time." - $$] $msg");
Nigel Kukard's avatar
Nigel Kukard committed
704
705
706
}


707
708
709
710
# Syslog logging
sub maillog
{
	my ($self,$msg,@args) = @_;
711
	my $log_mail = $self->{'config'}{'log_mail'};
712

713
714

	# Log to syslog
715
	if (defined($log_mail)) {
716
717
718
719
720
721
722
723
		# If we have args use printf style
		if (@args) {
			Sys::Syslog::syslog('info',$msg,@args);
		} else {
			Sys::Syslog::syslog('info','%s',$msg);
		}

	# Or log to main mechanism
724
	} else {
725
		$self->log(LOG_INFO,sprintf($msg,@args));
726
727
728
729
	}
}


730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
# Protocol response setting...
sub protocol_response
{
	my $self = shift;
	my $server = $self->{'server'};

	# Make sure the response handler exists
	if (!defined($server->{'_protocol_handler'}->{'protocol_response'})) {
		$self->log(LOG_ERR,"[CBPOLICYD] No protocol response handler available");
		return -1;
	}

	return $server->{'_protocol_handler'}->{'protocol_response'}($self,@_);
}


# Get protocol response
sub protocol_getresponse
{
	my $self = shift;
	my $server = $self->{'server'};

	# Make sure the response handler exists
	if (!defined($server->{'_protocol_handler'}->{'protocol_getresponse'})) {
		$self->log(LOG_ERR,"[CBPOLICYD] No protocol getresponse handler available");
		return -1;
	}

	return $server->{'_protocol_handler'}->{'protocol_getresponse'}($self);
}


762
763
# Display help
sub displayHelp {
Nigel Kukard's avatar
Nigel Kukard committed
764
	print(STDERR "PolicyD (Cluebringer) v".VERSION." - Copyright (c) 2007-2014 AllWorldIT\n");
765

766
767
768
769
770
	print(STDERR<<EOF);

Usage: $0 [args]
    --config=<file>        Configuration file
    --debug                Put into debug mode
771
    --fg                   Don't go into background
772
773
774
775

EOF
}

776
777
778
779
780


__PACKAGE__->run;


Nigel Kukard's avatar
Nigel Kukard committed
781

782
783
1;
# vim: ts=4