line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Squid::Guard; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
72708
|
use 5.008; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
50
|
|
4
|
1
|
|
|
1
|
|
91
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
42
|
|
5
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
68
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our @ISA = qw(); |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
our $VERSION = '0.15'; |
10
|
|
|
|
|
|
|
|
11
|
1
|
|
|
1
|
|
6
|
use Carp; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
85
|
|
12
|
1
|
|
|
1
|
|
9318
|
use DB_File; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
use Fcntl; |
14
|
|
|
|
|
|
|
use Squid::Guard::Request; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 NAME |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
Squid::Guard - Redirector for the Squid web proxy |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 SYNOPSYS |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
use Squid::Guard; |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
my $sg = Squid::Guard->new(); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
$sg->redir("http://proxy/cgi-bin/deny.pl";); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
$sg->checkf(\&check); |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
$sg->run; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=head1 DESCRIPTION |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
Squid::Guard is a module for creating a simple yet flexible |
35
|
|
|
|
|
|
|
redirector for the Squid web cache engine. |
36
|
|
|
|
|
|
|
This module was inspired by squidGuard, a popular squid redirector |
37
|
|
|
|
|
|
|
written in C, but aims to be more flexible and in some ways simpler |
38
|
|
|
|
|
|
|
to use. |
39
|
|
|
|
|
|
|
I was happy with squidGuard and used it for years. But I needed |
40
|
|
|
|
|
|
|
certain extra features like the ability to differentiate |
41
|
|
|
|
|
|
|
between users based on some external program output, group |
42
|
|
|
|
|
|
|
belongings etc. |
43
|
|
|
|
|
|
|
squidGuard did not support this, so Squid::Guard was born. |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=head2 Squid::Guard->new( opt => val, ...) |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
API call to create a new server. Does not actually start running anything until you call C<-Erun()>. |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=cut |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub new { |
53
|
|
|
|
|
|
|
my $class = shift; |
54
|
|
|
|
|
|
|
my %opts = @_; |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
my $self = {}; |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
$self->{dbdir} = undef; |
59
|
|
|
|
|
|
|
$self->{forcedbupdate} = 0; |
60
|
|
|
|
|
|
|
$self->{checkf} = undef; |
61
|
|
|
|
|
|
|
$self->{concurrency} = 0; |
62
|
|
|
|
|
|
|
$self->{categ} = {}; |
63
|
|
|
|
|
|
|
$self->{redir} = (); |
64
|
|
|
|
|
|
|
$self->{strictauth} = 0; |
65
|
|
|
|
|
|
|
$self->{verbose} = 0; |
66
|
|
|
|
|
|
|
$self->{debug} = 0; |
67
|
|
|
|
|
|
|
$self->{oneshot} = 0; |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
for( keys %opts ) { |
70
|
|
|
|
|
|
|
$self->{$_} = $opts{$_}; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
bless($self, $class); |
74
|
|
|
|
|
|
|
return $self; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
=head2 $sg->redir() |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
Get/set the redir page. |
81
|
|
|
|
|
|
|
The following macros are supported: |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
=over |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=item %u the requested url |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=item %p the path and the optional query string of %u, but note for convenience without the leading "/" |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=item %a the client IP address |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=item %n the client FQDN |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=item %i the user name, if available |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=item %t the C function result (see) |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=item %% the % sign |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=back |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
If set to the special value C, then the return value of the checkf function, if true, is used directly as the redirection url |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=cut |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
sub redir { |
106
|
|
|
|
|
|
|
my $self = shift; |
107
|
|
|
|
|
|
|
if (@_) { $self->{redir} = shift } |
108
|
|
|
|
|
|
|
return $self->{redir}; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=head2 $sg->checkf() |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
Sets the check callback function, which is called upn each request received. |
115
|
|
|
|
|
|
|
The check function receives as arguments the current C object, and a L object on which the user can perform tests. |
116
|
|
|
|
|
|
|
A false return value means no redirection is to be proformed. A true return value means that the request is to be redirected to what was declared with C. |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=cut |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
sub checkf { |
121
|
|
|
|
|
|
|
my $self = shift; |
122
|
|
|
|
|
|
|
my $funcref = shift; |
123
|
|
|
|
|
|
|
$self->{checkf} = $funcref; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=head2 $sg->concurrency() |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
Enables the concurrency protocol. For now, the implementation is rather poor: the ID is simply read and echoed to squid. |
130
|
|
|
|
|
|
|
See url_rewrite_concurrency in squid.conf |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=cut |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub concurrency { |
135
|
|
|
|
|
|
|
my $self = shift; |
136
|
|
|
|
|
|
|
my $num = shift; |
137
|
|
|
|
|
|
|
$self->{concurrency} = $num; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
my $cachettl = 0; |
142
|
|
|
|
|
|
|
my $cachepurgelastrun = 0; |
143
|
|
|
|
|
|
|
my %cacheh; # this contains the real cache items |
144
|
|
|
|
|
|
|
my @cachea; # this contains the cache keys with the time they where written in the cache. |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
=head2 $sg->cache() |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
Enables caching in expensive subs which involve spawning external processes. At the moment, caching is implemented in C (which calls wbinfo 3 times) and in C (which can be expensive in some nss configurations). |
149
|
|
|
|
|
|
|
An argument must be suplied, representing the time to live of cached items, in seconds. |
150
|
|
|
|
|
|
|
The time to live, as the whole cached objects, are shared among all the objects belonging to this class. No problem since usually only one object is in use. |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=cut |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
sub cache { |
155
|
|
|
|
|
|
|
my $self = shift; |
156
|
|
|
|
|
|
|
my $ttl = shift; |
157
|
|
|
|
|
|
|
$cachettl = $ttl; |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
sub _cachepurge() { |
162
|
|
|
|
|
|
|
my $time = time(); |
163
|
|
|
|
|
|
|
return if $cachepurgelastrun == $time; # do not purge too often |
164
|
|
|
|
|
|
|
$cachepurgelastrun = $time; |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
my $t = $time - $cachettl; |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
return unless @cachea; # try to avoid looping through if unnecessary |
169
|
|
|
|
|
|
|
return if $cachea[0]->[0] > $t; |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
my $ndel = 0; |
172
|
|
|
|
|
|
|
LOOP: foreach my $p ( @cachea ) { |
173
|
|
|
|
|
|
|
last LOOP if $p->[0] > $t; |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
my $k = $p->[1]; |
176
|
|
|
|
|
|
|
delete( $cacheh{$k} ) if defined( $cacheh{$k} ) && $cacheh{$k}->[0] <= $t; |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
$ndel++; |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
$ndel and splice(@cachea, 0, $ndel); |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
sub _cachewr($$) { |
186
|
|
|
|
|
|
|
my ($k, $v) = @_; |
187
|
|
|
|
|
|
|
defined($v) or $v = ""; # be sure not to cache undef values since _cacherd returns undef when the value is not in the cache |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
my $t = time; |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
my @arr = ( $t, $v ); |
192
|
|
|
|
|
|
|
$cacheh{$k} = \@arr; |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
my @arra = ($t, $k); |
195
|
|
|
|
|
|
|
push @cachea, \@arra; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
sub _cacherd($) { |
200
|
|
|
|
|
|
|
my ($k) = @_; |
201
|
|
|
|
|
|
|
# Purge the cache when reading from it. This also ensures that the remaining cache record are in their ttl. This could be done in other occasions too |
202
|
|
|
|
|
|
|
_cachepurge(); |
203
|
|
|
|
|
|
|
return defined($cacheh{$k}) ? $cacheh{$k}->[1] : undef; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=head2 $sg->verbose() |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
Get/set verbosity. Currently only one level of verbosity is supported |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=cut |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub verbose { |
214
|
|
|
|
|
|
|
my $self = shift; |
215
|
|
|
|
|
|
|
if (@_) { $self->{verbose} = shift } |
216
|
|
|
|
|
|
|
return $self->{verbose}; |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=head2 $sg->debug() |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
Emit debug info |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=cut |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
sub debug { |
227
|
|
|
|
|
|
|
my $self = shift; |
228
|
|
|
|
|
|
|
if (@_) { $self->{debug} = shift } |
229
|
|
|
|
|
|
|
$self->{debug} and $self->{verbose} = $self->{debug}; |
230
|
|
|
|
|
|
|
return $self->{debug}; |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=head2 $sg->oneshot() |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
Executes only a single iteration then exits (can be used when debugging) |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=cut |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
sub oneshot { |
241
|
|
|
|
|
|
|
my $self = shift; |
242
|
|
|
|
|
|
|
if (@_) { $self->{oneshot} = shift } |
243
|
|
|
|
|
|
|
return $self->{oneshot}; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
=head2 $sg->handle($req) |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
Handles a request, returning the empty string or the redirected url. |
250
|
|
|
|
|
|
|
The request can be either a string in the format passed to the redirector by squid, or a Squid::Guard::Request object. |
251
|
|
|
|
|
|
|
This sub is usually called internally by run() to handle a request, but can be called directly too. |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
=cut |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
sub handle { |
256
|
|
|
|
|
|
|
my $self = shift; |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
return "" unless $self->{checkf}; |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
my $arg = shift; |
261
|
|
|
|
|
|
|
my $req = ref($arg) ? $arg : Squid::Guard::Request->new($arg); |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
my $redir = ""; |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
my $res = $self->{checkf}->( $self, $req ); |
266
|
|
|
|
|
|
|
if( $res ) { |
267
|
|
|
|
|
|
|
if( $self->{redir} eq 'CHECKF' ) { |
268
|
|
|
|
|
|
|
$redir = $res; |
269
|
|
|
|
|
|
|
} else { |
270
|
|
|
|
|
|
|
$redir = $self->{redir} || croak "A request was submitted, but redir url is not defined"; |
271
|
|
|
|
|
|
|
$redir =~ s/(?addr/ge; |
272
|
|
|
|
|
|
|
$redir =~ s/(?fqdn or "unknown"/ge; |
273
|
|
|
|
|
|
|
my $i = $req->ident || "unknown"; |
274
|
|
|
|
|
|
|
$i =~ s/([^-._A-Za-z0-9])/sprintf("%%%02X", ord($1))/eg; |
275
|
|
|
|
|
|
|
$redir =~ s/(?
|
276
|
|
|
|
|
|
|
#$redir =~ s/(?
|
277
|
|
|
|
|
|
|
$redir =~ s/(?url/ge; |
278
|
|
|
|
|
|
|
my $pq = $req->path_query; |
279
|
|
|
|
|
|
|
$pq =~ s-^/--o; |
280
|
|
|
|
|
|
|
$redir =~ s/(?
|
281
|
|
|
|
|
|
|
my $r = $res; |
282
|
|
|
|
|
|
|
$r =~ s/([^-._A-Za-z0-9])/sprintf("%%%02X", ord($1))/eg; |
283
|
|
|
|
|
|
|
$redir =~ s/(?
|
284
|
|
|
|
|
|
|
$redir =~ s/%%/%/; |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
# Redirections seem not to be understood when the request was for HTTPS. |
287
|
|
|
|
|
|
|
# Info taken from http://www.mail-archive.com/squid-users@squid-cache.org/msg58422.html : |
288
|
|
|
|
|
|
|
# Squid is a little awkward: |
289
|
|
|
|
|
|
|
# the URL returned by squidguard must have the protocol as the original URL. |
290
|
|
|
|
|
|
|
# So for a URL with HTTPS protocol, squidguard must return a URL that uses the HTTPS protocol. |
291
|
|
|
|
|
|
|
# This is really not nice but the workaround is to use a 302 redirection: |
292
|
|
|
|
|
|
|
# redirect 302:http://www.internal-server.com/blocked.html |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# another one on the issue: http://www.techienuggets.com/Comments?tx=114527 |
295
|
|
|
|
|
|
|
# Blocking/filtering SSL pages with SquidGuard do not work very well. You |
296
|
|
|
|
|
|
|
# need to use Squid acls for that, or wrap up SquidGuard as an external |
297
|
|
|
|
|
|
|
# acl instead of url rewriter.. |
298
|
|
|
|
|
|
|
# |
299
|
|
|
|
|
|
|
# The reason is that |
300
|
|
|
|
|
|
|
# a) Most browsers will not accept a browser redirect in response to |
301
|
|
|
|
|
|
|
# CONNECT. |
302
|
|
|
|
|
|
|
# |
303
|
|
|
|
|
|
|
# b) You can't rewrite a CONNECT request into a http:// requrest. |
304
|
|
|
|
|
|
|
# |
305
|
|
|
|
|
|
|
# c) Most browsers will be quite upset if you rewrite the CONNECT to a |
306
|
|
|
|
|
|
|
# different host than requested. |
307
|
|
|
|
|
|
|
# |
308
|
|
|
|
|
|
|
# meaning that there is not much you actually can do with CONNECT requests |
309
|
|
|
|
|
|
|
# in SquidGuard that won't make browsers upset. |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
# So let's redirect. |
312
|
|
|
|
|
|
|
# Maybe we should check if $url begins with http:// . |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
if( $req->method eq 'CONNECT' ) { |
315
|
|
|
|
|
|
|
$redir = "302:$redir"; |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
return $redir; |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
=head2 $sg->run() |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
Starts handling the requests, reading them from one per line in the format used by Squid to talk to the url_rewrite_program |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
=cut |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
sub run { |
331
|
|
|
|
|
|
|
my $self = shift; |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
$self->{redir} || croak "Can not run when redir url is not defined"; |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
$|=1; # force a flush after every print on STDOUT |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
while () { |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
chomp; |
340
|
|
|
|
|
|
|
$self->{verbose} and print STDERR "Examining $_\n"; |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
my $ret = ""; |
343
|
|
|
|
|
|
|
if( $self->{concurrency} ) { |
344
|
|
|
|
|
|
|
s/^(\d+\s+)//o; |
345
|
|
|
|
|
|
|
$ret = $1; |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
my $redir = $self->handle($_); |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
if( $redir ) { |
351
|
|
|
|
|
|
|
$self->{verbose} and print STDERR "Returning $redir\n"; |
352
|
|
|
|
|
|
|
$ret .= $redir; |
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
print "$ret\n"; |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
last if $self->{oneshot}; |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
=head2 Black/white-list support |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
Squid::Guard provides support for using precompiled black or white lists, in a way similar to what squidGuard does. These lists are organized in categories. Each category has its own path (a directory) where three files can reside. These files are named domains, urls and expressions. There's no need for all three to be there, and in most situations only the domains and urls files are used. These files list domains, urls and/or (regular) expressions which describe if a request belong to the category. You can decide, in the checkf function, to allow or to redirect a request belonging to a certain category. |
365
|
|
|
|
|
|
|
Similarly to what squidGuard does, the domains and urls files have to be compiled in .db form prior to be used. This makes it possible to run huge domains and urls lists, with acceptable performance. |
366
|
|
|
|
|
|
|
You can find precompiled lists on the net, or create your own. |
367
|
|
|
|
|
|
|
Beginning with version 0.13, there is EXPERIMENTAL support for the userdomains file. This file lists domains associated with users. The request will be checked against the domains only if the request has the associated identity corresponding to the user. The file is made of lines in the format C. At the moment, the file is entirely read in memory and no corresponding .db is generated/needed. The userdomain feature is EXPERIMENTAL and subject to change. |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=head2 $sg->dbdir() |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
Get/set dbdir parameter, i.e. the directory where category subdirs are found. .db files generated from domains and urls files will reside here too. |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
=cut |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
sub dbdir { |
376
|
|
|
|
|
|
|
my $self = shift; |
377
|
|
|
|
|
|
|
if (@_) { $self->{dbdir} = shift } |
378
|
|
|
|
|
|
|
return $self->{dbdir}; |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
=head2 $sg->addcateg( name => path, ... ) |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
Adds one or more categories. |
385
|
|
|
|
|
|
|
C is the directory, relative to dbdir, containing the C, C, C and C files. |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
=cut |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
sub addcateg { |
390
|
|
|
|
|
|
|
my $self = shift; |
391
|
|
|
|
|
|
|
my %h = ( @_ ); |
392
|
|
|
|
|
|
|
foreach my $cat (keys %h) { |
393
|
|
|
|
|
|
|
$self->{categ}->{$cat}->{loc} = $h{$cat}; |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
my $l = $self->{dbdir} . '/' . $self->{categ}->{$cat}->{loc}; |
396
|
|
|
|
|
|
|
#print STDERR "$l\n"; |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
my $domsrc = "${l}/domains"; |
399
|
|
|
|
|
|
|
my $domdb = "${domsrc}.db"; |
400
|
|
|
|
|
|
|
if( -f $domsrc ) { |
401
|
|
|
|
|
|
|
# tie .db for reading |
402
|
|
|
|
|
|
|
my %h; |
403
|
|
|
|
|
|
|
my $X = tie (%h, 'DB_File', $domdb, O_RDONLY, 0644, $DB_BTREE) || croak ("Cannot open $domdb: $!"); |
404
|
|
|
|
|
|
|
$self->{categ}->{$cat}->{d} = \%h; |
405
|
|
|
|
|
|
|
$self->{categ}->{$cat}->{dX} = $X; |
406
|
|
|
|
|
|
|
} |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
my $urlsrc = "${l}/urls"; |
409
|
|
|
|
|
|
|
my $urldb = "${urlsrc}.db"; |
410
|
|
|
|
|
|
|
if( -f $urlsrc ) { |
411
|
|
|
|
|
|
|
# tie .db for reading |
412
|
|
|
|
|
|
|
my %h; |
413
|
|
|
|
|
|
|
my $X = tie (%h, 'DB_File', $urldb, O_RDONLY, 0644, $DB_BTREE) || croak ("Cannot open $urldb: $!"); |
414
|
|
|
|
|
|
|
$self->{categ}->{$cat}->{u} = \%h; |
415
|
|
|
|
|
|
|
$self->{categ}->{$cat}->{uX} = $X; |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
my $e = "$l/expressions"; |
419
|
|
|
|
|
|
|
if( -f $e ) { |
420
|
|
|
|
|
|
|
my @a; |
421
|
|
|
|
|
|
|
open( E, "< $e" ) or croak "Cannot open $e: $!"; |
422
|
|
|
|
|
|
|
while( ) { |
423
|
|
|
|
|
|
|
chomp; |
424
|
|
|
|
|
|
|
s/#.*//o; |
425
|
|
|
|
|
|
|
next if /^\s*$/o; |
426
|
|
|
|
|
|
|
push @a, qr/$_/i; # array of regexps. Can't use 'o' regexp option, since I would put in the array always the same regexp (the first one). But it seems that with qr, 'o' is obsolete. |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
close E; |
429
|
|
|
|
|
|
|
$self->{categ}->{$cat}->{e} = \@a; |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
my $ud = "$l/userdomains"; |
433
|
|
|
|
|
|
|
if( -f $ud ) { |
434
|
|
|
|
|
|
|
my $hr = {}; |
435
|
|
|
|
|
|
|
open( UD, "< $ud" ) or croak "Cannot open $ud: $!"; |
436
|
|
|
|
|
|
|
while( ) { |
437
|
|
|
|
|
|
|
chomp; |
438
|
|
|
|
|
|
|
s/#.*//o; |
439
|
|
|
|
|
|
|
next unless /^\s*([^\|]+)\|(.*\S)/o; |
440
|
|
|
|
|
|
|
$hr->{$1}->{$2} = 1; |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
close UD; |
443
|
|
|
|
|
|
|
$self->{categ}->{$cat}->{ud} = $hr; |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
return 1; |
447
|
|
|
|
|
|
|
} |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
=head2 $sg->mkdb( name => path, ... ) |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
Creates/updates the .db files for the categories. |
453
|
|
|
|
|
|
|
Will search in C for the potential presence of the C and C plaintext files. |
454
|
|
|
|
|
|
|
According to the value of the C flag (see), will create or update the .db file from them. |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
=cut |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
sub mkdb { |
459
|
|
|
|
|
|
|
my $self = shift; |
460
|
|
|
|
|
|
|
my %h = ( @_ ); |
461
|
|
|
|
|
|
|
foreach my $cat (keys %h) { |
462
|
|
|
|
|
|
|
$self->{categ}->{$cat}->{loc} = $h{$cat}; |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
my $l = $self->{dbdir} . '/' . $self->{categ}->{$cat}->{loc}; |
465
|
|
|
|
|
|
|
#print STDERR "$l\n"; |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
my $domsrc = "${l}/domains"; |
468
|
|
|
|
|
|
|
my $domdb = "${domsrc}.db"; |
469
|
|
|
|
|
|
|
if( -f $domsrc ) { |
470
|
|
|
|
|
|
|
# update .db, if needed |
471
|
|
|
|
|
|
|
if( $self->{forcedbupdate} || (stat($domsrc))[9] > ( (stat($domdb))[9] || 0 ) ) { |
472
|
|
|
|
|
|
|
$self->{verbose} and print STDERR "Making $domdb\n"; |
473
|
|
|
|
|
|
|
my %h; |
474
|
|
|
|
|
|
|
my $X = tie (%h, 'DB_File', $domdb, O_CREAT|O_TRUNC|O_RDWR, 0644, $DB_BTREE) || croak ("Cannot create $domdb: $!"); |
475
|
|
|
|
|
|
|
open( F, "< $domsrc") or croak "Cannot open $domsrc"; |
476
|
|
|
|
|
|
|
while( ) { |
477
|
|
|
|
|
|
|
chomp; |
478
|
|
|
|
|
|
|
s/#.*//o; |
479
|
|
|
|
|
|
|
next if /^\s*$/o; |
480
|
|
|
|
|
|
|
$h{lc($_)} = undef; |
481
|
|
|
|
|
|
|
} |
482
|
|
|
|
|
|
|
close F; |
483
|
|
|
|
|
|
|
undef $X; |
484
|
|
|
|
|
|
|
untie %h; |
485
|
|
|
|
|
|
|
} else { |
486
|
|
|
|
|
|
|
$self->{verbose} and print STDERR "$domdb more recent than $domsrc, skipped\n"; |
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
} |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
my $urlsrc = "${l}/urls"; |
491
|
|
|
|
|
|
|
my $urldb = "${urlsrc}.db"; |
492
|
|
|
|
|
|
|
if( -f $urlsrc ) { |
493
|
|
|
|
|
|
|
# update .db, if needed |
494
|
|
|
|
|
|
|
if( $self->{forcedbupdate} || (stat($urlsrc))[9] > ( (stat($urldb))[9] || 0 ) ) { |
495
|
|
|
|
|
|
|
$self->{verbose} and print STDERR "Making $urldb\n"; |
496
|
|
|
|
|
|
|
my %h; |
497
|
|
|
|
|
|
|
my $X = tie (%h, 'DB_File', $urldb, O_CREAT|O_TRUNC|O_RDWR, 0644, $DB_BTREE) || croak ("Cannot create $urldb: $!"); |
498
|
|
|
|
|
|
|
open( F, "< $urlsrc") or croak "Cannot open $urlsrc"; |
499
|
|
|
|
|
|
|
while( ) { |
500
|
|
|
|
|
|
|
chomp; |
501
|
|
|
|
|
|
|
s/#.*//o; |
502
|
|
|
|
|
|
|
next if /^\s*$/o; |
503
|
|
|
|
|
|
|
$h{lc($_)} = undef; |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
close F; |
506
|
|
|
|
|
|
|
undef $X; |
507
|
|
|
|
|
|
|
untie %h; |
508
|
|
|
|
|
|
|
} else { |
509
|
|
|
|
|
|
|
$self->{verbose} and print STDERR "$urldb more recent than $urlsrc, skipped\n"; |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
} |
512
|
|
|
|
|
|
|
} |
513
|
|
|
|
|
|
|
return 1; |
514
|
|
|
|
|
|
|
} |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
=head2 $sg->forcedbupdate() |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
Controls whether mkdb should forcibly update the .db files. |
520
|
|
|
|
|
|
|
If set to a false value (which is the default), existing .db files are refreshed only if older than the respective plaintext file. |
521
|
|
|
|
|
|
|
If set to a true value, .db files are always (re)created. |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
=cut |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
sub forcedbupdate { |
526
|
|
|
|
|
|
|
my $self = shift; |
527
|
|
|
|
|
|
|
if (@_) { $self->{forcedbupdate} = shift } |
528
|
|
|
|
|
|
|
return $self->{forcedbupdate}; |
529
|
|
|
|
|
|
|
} |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
#=head2 $sg->getcateg() |
533
|
|
|
|
|
|
|
# |
534
|
|
|
|
|
|
|
#Gets the defined categories |
535
|
|
|
|
|
|
|
# |
536
|
|
|
|
|
|
|
#=cut |
537
|
|
|
|
|
|
|
# |
538
|
|
|
|
|
|
|
#sub getcateg { |
539
|
|
|
|
|
|
|
# my $self = shift; |
540
|
|
|
|
|
|
|
# my %h; |
541
|
|
|
|
|
|
|
# for( keys %{$self->{categ}} ) { |
542
|
|
|
|
|
|
|
# $h{$_} = $self->{categ}->{$_}->{loc}; |
543
|
|
|
|
|
|
|
# } |
544
|
|
|
|
|
|
|
# return %h; |
545
|
|
|
|
|
|
|
#} |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
# =head2 $sg->_domains() |
549
|
|
|
|
|
|
|
# |
550
|
|
|
|
|
|
|
# Finds the super-domains where the given domain is nested. |
551
|
|
|
|
|
|
|
# This is a helper sub for C |
552
|
|
|
|
|
|
|
# |
553
|
|
|
|
|
|
|
# =cut |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
sub _domains($) { |
556
|
|
|
|
|
|
|
my $host = shift; |
557
|
|
|
|
|
|
|
return () unless $host; |
558
|
|
|
|
|
|
|
# www . iotti . biz |
559
|
|
|
|
|
|
|
# 0 1 2 |
560
|
|
|
|
|
|
|
my @a = split(/\./, $host); |
561
|
|
|
|
|
|
|
my $num = $#a; |
562
|
|
|
|
|
|
|
my @b; |
563
|
|
|
|
|
|
|
for( 0 .. $num ) { |
564
|
|
|
|
|
|
|
my $j = $num - $_; |
565
|
|
|
|
|
|
|
push @b, join(".", @a[$j .. $num]); |
566
|
|
|
|
|
|
|
} |
567
|
|
|
|
|
|
|
return @b; |
568
|
|
|
|
|
|
|
} |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
# =head2 $sg->_uris() |
572
|
|
|
|
|
|
|
# |
573
|
|
|
|
|
|
|
# Finds the uris containing the given uri. |
574
|
|
|
|
|
|
|
# This is a helper sub for C |
575
|
|
|
|
|
|
|
# |
576
|
|
|
|
|
|
|
# =cut |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
sub _uris($) { |
579
|
|
|
|
|
|
|
my $uri = shift; |
580
|
|
|
|
|
|
|
return () unless $uri; |
581
|
|
|
|
|
|
|
# www.iotti.biz / dir1 / dir2 / dir3 / file |
582
|
|
|
|
|
|
|
# 0 1 2 3 4 |
583
|
|
|
|
|
|
|
my @a = split(/\//, $uri); |
584
|
|
|
|
|
|
|
my $num = $#a; |
585
|
|
|
|
|
|
|
my @b; |
586
|
|
|
|
|
|
|
for( 0 .. $num ) { |
587
|
|
|
|
|
|
|
my $sub_uri = join("/", @a[0 .. $_]); |
588
|
|
|
|
|
|
|
push @b, $sub_uri; |
589
|
|
|
|
|
|
|
push @b, $sub_uri . '/' if $_ < $num; # check www.iotti.biz/dir/ too (with the trailing slashe) since some publicly-available lists carry urls with trailing slashes |
590
|
|
|
|
|
|
|
} |
591
|
|
|
|
|
|
|
return @b; |
592
|
|
|
|
|
|
|
} |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
=head2 $sg->checkincateg($req, $categ, ... ) |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
Checks if a request is in one category or more |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
=cut |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
sub checkincateg($$@) { |
602
|
|
|
|
|
|
|
my ( $self, $req, @categs ) = @_; |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
foreach my $categ (@categs) { |
605
|
|
|
|
|
|
|
my $catref = $self->{categ}; |
606
|
|
|
|
|
|
|
defined( $catref->{$categ} ) or croak "The requested category $categ does not exist"; |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
#print STDERR "s $req->scheme h $req->host p $req->path\n"; |
609
|
|
|
|
|
|
|
if( defined( $catref->{$categ}->{d} ) ) { |
610
|
|
|
|
|
|
|
$self->{debug} and print STDERR " Check " . $req->host . " in $categ domains\n"; |
611
|
|
|
|
|
|
|
my $ref = $catref->{$categ}->{d}; |
612
|
|
|
|
|
|
|
foreach( _domains($req->host) ) { |
613
|
|
|
|
|
|
|
$self->{debug} and print STDERR " Check $_\n"; |
614
|
|
|
|
|
|
|
if(exists($ref->{$_})) { |
615
|
|
|
|
|
|
|
$self->{debug} and print STDERR " FOUND\n"; |
616
|
|
|
|
|
|
|
return $categ; |
617
|
|
|
|
|
|
|
} |
618
|
|
|
|
|
|
|
} |
619
|
|
|
|
|
|
|
} |
620
|
|
|
|
|
|
|
if( defined( $catref->{$categ}->{u} ) ) { |
621
|
|
|
|
|
|
|
# in url checking, we test the authority part + the optional path part + the optional query part |
622
|
|
|
|
|
|
|
my $what = $req->authority_path_query; |
623
|
|
|
|
|
|
|
$self->{debug} and print STDERR " Check $what in $categ urls\n"; |
624
|
|
|
|
|
|
|
my $ref = $catref->{$categ}->{u}; |
625
|
|
|
|
|
|
|
foreach( _uris($what) ) { |
626
|
|
|
|
|
|
|
$self->{debug} and print STDERR " Check $_\n"; |
627
|
|
|
|
|
|
|
if(exists($ref->{$_})) { |
628
|
|
|
|
|
|
|
$self->{debug} and print STDERR " FOUND\n"; |
629
|
|
|
|
|
|
|
return $categ; |
630
|
|
|
|
|
|
|
} |
631
|
|
|
|
|
|
|
} |
632
|
|
|
|
|
|
|
} |
633
|
|
|
|
|
|
|
if( defined( $catref->{$categ}->{e} ) ) { |
634
|
|
|
|
|
|
|
my $what = $req->url; |
635
|
|
|
|
|
|
|
$self->{debug} and print STDERR " Check $what in $categ expressions\n"; |
636
|
|
|
|
|
|
|
my $ref = $catref->{$categ}->{e}; |
637
|
|
|
|
|
|
|
foreach( @$ref ) { |
638
|
|
|
|
|
|
|
$self->{debug} and print STDERR " Check $_\n"; |
639
|
|
|
|
|
|
|
if( $what =~ /$_/i ) { # Can't use 'o' regexp option, since I would compare always the same regexp. |
640
|
|
|
|
|
|
|
$self->{debug} and print STDERR " FOUND\n"; |
641
|
|
|
|
|
|
|
return $categ; |
642
|
|
|
|
|
|
|
} |
643
|
|
|
|
|
|
|
} |
644
|
|
|
|
|
|
|
} |
645
|
|
|
|
|
|
|
if( $req->ident and defined( $catref->{$categ}->{ud}->{$req->ident} ) ) { |
646
|
|
|
|
|
|
|
$self->{debug} and print STDERR " Check " . $req->host . " in $categ userdomains for user " . $req->ident . "\n"; |
647
|
|
|
|
|
|
|
my $hr = $catref->{$categ}->{ud}->{$req->ident}; |
648
|
|
|
|
|
|
|
# TODO: optimization: precompile _domains($req->host) only once for domains and userdomains |
649
|
|
|
|
|
|
|
foreach( _domains($req->host) ) { |
650
|
|
|
|
|
|
|
$self->{debug} and print STDERR " Check $_\n"; |
651
|
|
|
|
|
|
|
if($hr->{$_}) { |
652
|
|
|
|
|
|
|
$self->{debug} and print STDERR " FOUND\n"; |
653
|
|
|
|
|
|
|
return $categ; |
654
|
|
|
|
|
|
|
} |
655
|
|
|
|
|
|
|
} |
656
|
|
|
|
|
|
|
} |
657
|
|
|
|
|
|
|
} |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
return ''; |
660
|
|
|
|
|
|
|
} |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
# Gets a passwd row, making use of the cache if enabled. |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
sub _getpwnamcache($) { |
666
|
|
|
|
|
|
|
my $nam = shift; |
667
|
|
|
|
|
|
|
my $k = "PWNAM: $nam"; |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
if( $cachettl ) { |
670
|
|
|
|
|
|
|
my $v = _cacherd( $k ); |
671
|
|
|
|
|
|
|
defined($v) and return split( /:/, $v ); |
672
|
|
|
|
|
|
|
} |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
my @a = getpwnam($nam); |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
if( $cachettl ) { |
677
|
|
|
|
|
|
|
_cachewr( $k, join( ':', @a ) ); |
678
|
|
|
|
|
|
|
} |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
return @a; |
681
|
|
|
|
|
|
|
} |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
sub _getgrnamcache($) { |
685
|
|
|
|
|
|
|
my $nam = shift; |
686
|
|
|
|
|
|
|
my $k = "GRNAM: $nam"; |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
if( $cachettl ) { |
689
|
|
|
|
|
|
|
my $v = _cacherd( $k ); |
690
|
|
|
|
|
|
|
defined($v) and return split( /:/, $v ); |
691
|
|
|
|
|
|
|
} |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
my @a = getgrnam($nam); |
694
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
if( $cachettl ) { |
696
|
|
|
|
|
|
|
_cachewr( $k, join( ':', @a ) ); |
697
|
|
|
|
|
|
|
} |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
return @a; |
700
|
|
|
|
|
|
|
} |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
# Runs a command, making use of the cache if enabled. |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
sub _runcache($) { |
706
|
|
|
|
|
|
|
my $cmd = shift; |
707
|
|
|
|
|
|
|
my $k = "RUN: $cmd"; |
708
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
my $v; |
710
|
|
|
|
|
|
|
if( $cachettl ) { |
711
|
|
|
|
|
|
|
$v = _cacherd( $k ); |
712
|
|
|
|
|
|
|
defined($v) and return $v; |
713
|
|
|
|
|
|
|
} |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
$v = `$cmd`; |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
if( $cachettl ) { |
718
|
|
|
|
|
|
|
_cachewr( $k, $v ); |
719
|
|
|
|
|
|
|
} |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
return $v; |
722
|
|
|
|
|
|
|
} |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
=head2 Other help subs that can be used in the checkf function |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
=head2 $sg->checkingroup($user, $group, ... ) |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
Checks if a user is in a UNIX grop |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
=cut |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
sub checkingroup($$@) { |
735
|
|
|
|
|
|
|
my ( $self, $user, @groups ) = @_; |
736
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
return 0 unless $user; |
738
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
my @pwent = _getpwnamcache($user); |
740
|
|
|
|
|
|
|
if( ! @pwent ) { |
741
|
|
|
|
|
|
|
print STDERR "Can not find user $user\n"; |
742
|
|
|
|
|
|
|
return ''; |
743
|
|
|
|
|
|
|
} |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
my $uid = $pwent[2]; |
746
|
|
|
|
|
|
|
my $uprimgid = $pwent[3]; |
747
|
|
|
|
|
|
|
if( ! defined $uid || ! defined $uprimgid ) { |
748
|
|
|
|
|
|
|
print STDERR "Can not find uid and gid corresponding to $user\n"; |
749
|
|
|
|
|
|
|
return ''; |
750
|
|
|
|
|
|
|
} |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
foreach my $group (@groups) { |
753
|
|
|
|
|
|
|
my @grent = _getgrnamcache($group); |
754
|
|
|
|
|
|
|
if( ! @grent ) { |
755
|
|
|
|
|
|
|
print STDERR "Can not find group $group\n"; |
756
|
|
|
|
|
|
|
next; |
757
|
|
|
|
|
|
|
} |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
my $gid = $grent[2]; |
760
|
|
|
|
|
|
|
if( ! defined $gid ) { |
761
|
|
|
|
|
|
|
print STDERR "Can not find gid corresponding to $group\n"; |
762
|
|
|
|
|
|
|
next; |
763
|
|
|
|
|
|
|
} |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
if( $uprimgid == $gid ) { |
766
|
|
|
|
|
|
|
$self->{debug} and print STDERR "FOUND $user has primary group $group\n"; |
767
|
|
|
|
|
|
|
return $group; |
768
|
|
|
|
|
|
|
} |
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
my @membri = split(/\s+/, $grent[3]); |
771
|
|
|
|
|
|
|
$self->{debug} and print STDERR "Group $group contains:\n" . join("\n", @membri) . "\n"; |
772
|
|
|
|
|
|
|
for(@membri) { |
773
|
|
|
|
|
|
|
my @pwent2 = _getpwnamcache($_); |
774
|
|
|
|
|
|
|
my $uid2 = $pwent2[2]; |
775
|
|
|
|
|
|
|
if( ! defined $uid2 ) { |
776
|
|
|
|
|
|
|
print STDERR "Can not find uid corresponding to $_\n"; |
777
|
|
|
|
|
|
|
next; |
778
|
|
|
|
|
|
|
} |
779
|
|
|
|
|
|
|
if( $uid2 == $uid ) { |
780
|
|
|
|
|
|
|
$self->{debug} and print STDERR "FOUND $user is in $group\n"; |
781
|
|
|
|
|
|
|
return $group; |
782
|
|
|
|
|
|
|
} |
783
|
|
|
|
|
|
|
} |
784
|
|
|
|
|
|
|
} |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
return ''; |
787
|
|
|
|
|
|
|
} |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
=head2 $sg->checkinwbgroup($user, $group, ...) |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
Checks if a user is in a WinBind grop |
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
=cut |
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
sub checkinwbgroup($$@) { |
797
|
|
|
|
|
|
|
my ( $self, $user, @groups ) = @_; |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
return '' unless $user; |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
my $userSID = _runcache("wbinfo -n '$user'"); |
802
|
|
|
|
|
|
|
if( $? ) { |
803
|
|
|
|
|
|
|
print STDERR "Can not find user $user in winbind\n"; |
804
|
|
|
|
|
|
|
return ''; |
805
|
|
|
|
|
|
|
} |
806
|
|
|
|
|
|
|
$userSID =~ s/\s.*//o; |
807
|
|
|
|
|
|
|
chomp $userSID; |
808
|
|
|
|
|
|
|
$self->{debug} and print STDERR "Found user $user with SID $userSID\n"; |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
my %groupsSIDs; |
811
|
|
|
|
|
|
|
foreach my $group (@groups) { |
812
|
|
|
|
|
|
|
my $groupSID = _runcache("wbinfo -n '$group'"); |
813
|
|
|
|
|
|
|
if( $? ) { |
814
|
|
|
|
|
|
|
print STDERR "Can not find group $group in winbind\n"; |
815
|
|
|
|
|
|
|
return ''; |
816
|
|
|
|
|
|
|
} |
817
|
|
|
|
|
|
|
$groupSID =~ s/\s.*//o; |
818
|
|
|
|
|
|
|
chomp $groupSID; |
819
|
|
|
|
|
|
|
$self->{debug} and print STDERR "Found group $group with SID $groupSID\n"; |
820
|
|
|
|
|
|
|
$groupsSIDs{$groupSID} = $group; |
821
|
|
|
|
|
|
|
} |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
my @userInSIDs = _runcache("wbinfo --user-domgroups '$userSID'"); |
824
|
|
|
|
|
|
|
if( $? ) { |
825
|
|
|
|
|
|
|
print STDERR "Can not find the SIDs of the groups of $user - $userSID\n"; |
826
|
|
|
|
|
|
|
return ''; |
827
|
|
|
|
|
|
|
} |
828
|
|
|
|
|
|
|
$self->{debug} and print STDERR "$user is in the following groups:\n @userInSIDs"; |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
foreach ( @userInSIDs ) { |
831
|
|
|
|
|
|
|
chomp; |
832
|
|
|
|
|
|
|
if ( $groupsSIDs{$_} ) { |
833
|
|
|
|
|
|
|
$self->{debug} and print STDERR " FOUND\n"; |
834
|
|
|
|
|
|
|
return $groupsSIDs{$_}; |
835
|
|
|
|
|
|
|
} |
836
|
|
|
|
|
|
|
} |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
return ''; |
839
|
|
|
|
|
|
|
} |
840
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
=head2 $sg->checkinaddr($req) |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
Checks if a request is for an explicit IP address |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
=cut |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
sub checkinaddr($$) { |
849
|
|
|
|
|
|
|
my ( $self, $req ) = @_; |
850
|
|
|
|
|
|
|
# TODO: Maybe the test should be more accurate and more general |
851
|
|
|
|
|
|
|
return 1 if $req->host =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/o; |
852
|
|
|
|
|
|
|
return 0; |
853
|
|
|
|
|
|
|
} |
854
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
1; |
857
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
__END__ |