4
* DEBUG: section 28 Access Control
5
* AUTHOR: Duane Wessels
7
* SQUID Web Proxy Cache http://www.squid-cache.org/
8
* ----------------------------------------------------------
10
* Squid is the result of efforts by numerous individuals from
11
* the Internet community; see the CONTRIBUTORS file for full
12
* details. Many organizations have provided support for Squid's
13
* development; see the SPONSORS file for full details. Squid is
14
* Copyrighted (C) 2001 by the Regents of the University of
15
* California; see the COPYRIGHT file for full details. Squid
16
* incorporates software developed and/or copyrighted by other
17
* sources; see the CREDITS file for full details.
19
* This program is free software; you can redistribute it and/or modify
20
* it under the terms of the GNU General Public License as published by
21
* the Free Software Foundation; either version 2 of the License, or
22
* (at your option) any later version.
24
* This program is distributed in the hope that it will be useful,
25
* but WITHOUT ANY WARRANTY; without even the implied warranty of
26
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
* GNU General Public License for more details.
29
* You should have received a copy of the GNU General Public License
30
* along with this program; if not, write to the Free Software
31
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33
* Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
38
#include "acl/Checklist.h"
43
ACLIP::operator new (size_t byteCount)
45
fatal ("ACLIP::operator new: unused");
50
ACLIP::operator delete (void *address)
52
fatal ("ACLIP::operator delete: unused");
56
* Writes an IP ACL data into a buffer, then copies the buffer into the wordlist given
58
\param ip ACL data structure to display
59
\param state wordlist structure which is being generated
62
ACLIP::DumpIpListWalkee(acl_ip_data * const & ip, void *state)
64
char tmpbuf[ ((MAX_IPSTRLEN*2)+6) ]; // space for 2 IPs and a CIDR mask(3) and seperators(3).
66
wordlist **W = static_cast<wordlist **>(state);
70
assert(mb.max_capacity > 0 && 1==1 );
72
ip->toStr(tmpbuf, sizeof(tmpbuf) );
73
assert(mb.max_capacity > 0 && 2==2 );
74
mb.append(tmpbuf, strlen(tmpbuf) );
75
assert(mb.max_capacity > 0 && 3==3);
76
wordlistAdd(W, mb.buf);
81
* print/format an acl_ip_data structure for debugging output.
83
\param buf string buffer to write to
84
\param len size of the buffer available
87
acl_ip_data::toStr(char *buf, int len) const
94
addr1.NtoA(b1, len - rlen );
98
if (!addr2.IsAnyAddr()) {
101
addr2.NtoA(&(b2[1]), len - rlen );
108
if (!mask.IsNoAddr()) {
112
snprintf(&(b3[1]), (len-rlen), "%u", mask.GetCIDR() - (addr1.IsIPv4()?96:0) );
114
snprintf(&(b3[1]), (len-rlen), "%u", mask.GetCIDR() );
121
* aclIpAddrNetworkCompare - The guts of the comparison for IP ACLs
122
* matching checks. The first argument (p) is a "host" address,
123
* i.e. the IP address of a cache client. The second argument (q)
124
* is an entry in some address-based access control element. This
125
* function is called via ACLIP::match() and the splay library.
128
aclIpAddrNetworkCompare(acl_ip_data * const &p, acl_ip_data * const &q)
130
IpAddress A = p->addr1;
133
A.ApplyMask(q->mask);
135
debugs(28,9, "aclIpAddrNetworkCompare: compare: " << p->addr1 << "/" << q->mask << " (" << A << ") vs " <<
136
q->addr1 << "-" << q->addr2 << "/" << q->mask);
138
if (q->addr2.IsAnyAddr()) { /* single address check */
140
return A.matchIPAddr( q->addr1 );
142
} else { /* range address check */
144
if ( (A >= q->addr1) && (A <= q->addr2) )
145
return 0; /* valid. inside range. */
147
return A.matchIPAddr( q->addr1 ); /* outside of range, 'less than' */
153
* acl_ip_data::NetworkCompare - Compare two acl_ip_data entries. Strictly
154
* used by the splay insertion routine. It emits a warning if it
155
* detects a "collision" or overlap that would confuse the splay
156
* sorting algorithm. Much like aclDomainCompare.
157
* The first argument (p) is a "host" address, i.e. the IP address of a cache client.
158
* The second argument (b) is a "network" address that might have a subnet and/or range.
159
* We mask the host address bits with the network subnet mask.
162
acl_ip_data::NetworkCompare(acl_ip_data * const & a, acl_ip_data * const &b)
166
ret = aclIpAddrNetworkCompare(b, a);
170
ret = aclIpAddrNetworkCompare(a, b);
174
char buf_n1[3*(MAX_IPSTRLEN+1)];
175
char buf_n2[3*(MAX_IPSTRLEN+1)];
177
b->toStr(buf_n1, 3*(MAX_IPSTRLEN+1));
178
a->toStr(buf_n2, 3*(MAX_IPSTRLEN+1));
180
a->toStr(buf_n1, 3*(MAX_IPSTRLEN+1));
181
b->toStr(buf_n2, 3*(MAX_IPSTRLEN+1));
183
debugs(28, 0, "WARNING: (" << (bina?'B':'A') << ") '" << buf_n1 << "' is a subnetwork of (" << (bina?'A':'B') << ") '" << buf_n2 << "'");
184
debugs(28, 0, "WARNING: because of this '" << (bina?buf_n2:buf_n1) << "' is ignored to keep splay tree searching predictable");
185
debugs(28, 0, "WARNING: You should probably remove '" << buf_n1 << "' from the ACL named '" << AclMatchedName << "'");
192
* Decode an ascii representation (asc) of a IP netmask address or CIDR,
193
* and place resulting information in mask.
194
* This function should NOT be called if 'asc' is a hostname!
197
acl_ip_data::DecodeMask(const char *asc, IpAddress &mask, int ctype)
202
/* default is a mask that doesn't change any IP */
209
/* An int mask 128, 32 */
210
if ((sscanf(asc, "%d%c", &a1, &junk)==1) &&
211
(a1 <= 128) && (a1 >= 0)
213
return mask.ApplyMask(a1, ctype);
216
/* dotted notation */
217
/* assignment returns true if asc contained an IP address as text */
220
/* HACK: IPv4 netmasks don't cleanly map to IPv6 masks. */
221
debugs(28, DBG_IMPORTANT, "WARNING: Netmasks are deprecated. Please use CIDR masks instead.");
223
/* locate what CIDR mask was _probably_ meant to be in its native protocol format. */
224
/* this will completely crap out with a security fail-open if the admin is playing mask tricks */
225
/* however, thats their fault, and we do warn. see bug 2601 for the effects if we don't do this. */
226
unsigned int m = mask.GetCIDR();
227
debugs(28, DBG_CRITICAL, "WARNING: IPv4 netmasks are particularly nasty when used to compare IPv6 to IPv4 ranges.");
228
debugs(28, DBG_CRITICAL, "WARNING: For now we assume you meant to write /" << m);
229
/* reset the mask completely, and crop to the CIDR boundary back properly. */
231
return mask.ApplyMask(m,AF_INET);
233
#endif /* USE_IPV6 */
240
/* Handle either type of address, IPv6 will be discarded with a warning if disabled */
241
#define SCAN_ACL1_6 "%[0123456789ABCDEFabcdef:]-%[0123456789ABCDEFabcdef:]/%[0123456789]"
242
#define SCAN_ACL2_6 "%[0123456789ABCDEFabcdef:]-%[0123456789ABCDEFabcdef:]%c"
243
#define SCAN_ACL3_6 "%[0123456789ABCDEFabcdef:]/%[0123456789]"
244
#define SCAN_ACL4_6 "%[0123456789ABCDEFabcdef:]/%c"
245
/* We DO need to know which is which though, for proper CIDR masking. */
246
#define SCAN_ACL1_4 "%[0123456789.]-%[0123456789.]/%[0123456789.]"
247
#define SCAN_ACL2_4 "%[0123456789.]-%[0123456789.]%c"
248
#define SCAN_ACL3_4 "%[0123456789.]/%[0123456789.]"
249
#define SCAN_ACL4_4 "%[0123456789.]/%c"
252
acl_ip_data::FactoryParse(const char *t)
254
LOCAL_ARRAY(char, addr1, 256);
255
LOCAL_ARRAY(char, addr2, 256);
256
LOCAL_ARRAY(char, mask, 256);
257
acl_ip_data *r = NULL;
258
acl_ip_data **Q = NULL;
261
unsigned int changed;
262
acl_ip_data *q = new acl_ip_data;
263
int iptype = AF_UNSPEC;
265
debugs(28, 5, "aclIpParseIpData: " << t);
267
/* Special ACL RHS "all" matches entire Internet */
268
if (strcasecmp(t, "all") == 0) {
269
debugs(28, 9, "aclIpParseIpData: magic 'all' found.");
270
q->addr1.SetAnyAddr();
272
q->mask.SetAnyAddr();
277
/* Special ACL RHS "ipv6" matches IPv6-Unicast Internet */
278
if (strcasecmp(t, "ipv6") == 0) {
279
debugs(28, 9, "aclIpParseIpData: magic 'ipv6' found.");
281
/* AYJ: due to the nature os IPv6 this will not always work,
282
* we may need to turn recursive to catch all the valid v6 sub-nets. */
287
if (sscanf(t, SCAN_ACL1_4, addr1, addr2, mask) == 3) {
288
debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN1-v4: " << SCAN_ACL1_4);
290
} else if (sscanf(t, SCAN_ACL2_4, addr1, addr2, &c) >= 2) {
291
debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN2-v4: " << SCAN_ACL2_4);
294
} else if (sscanf(t, SCAN_ACL3_4, addr1, mask) == 2) {
295
debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN3-v4: " << SCAN_ACL3_4);
298
} else if (sscanf(t, SCAN_ACL4_4, addr1,&c) == 2) {
299
debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN4-v4: " << SCAN_ACL4_4);
305
} else if (sscanf(t, SCAN_ACL1_6, addr1, addr2, mask) == 3) {
306
debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN1-v4: " << SCAN_ACL1_6);
308
} else if (sscanf(t, SCAN_ACL2_6, addr1, addr2, &c) >= 2) {
309
debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN2-v4: " << SCAN_ACL2_6);
312
} else if (sscanf(t, SCAN_ACL3_6, addr1, mask) == 2) {
313
debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN3-v4: " << SCAN_ACL3_6);
316
} else if (sscanf(t, SCAN_ACL4_6, addr1, mask) == 2) {
317
debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN4-v4: " << SCAN_ACL4_6);
322
} else if (sscanf(t, "%[^/]/%s", addr1, mask) == 2) {
323
debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: non-IP pattern: %[^/]/%s");
325
} else if (sscanf(t, "%s", addr1) == 1) {
327
* Note, must use plain xgetaddrinfo() here because at startup
328
* ipcache hasn't been initialized
329
* TODO: offload this to one of the IpAddress lookups.
332
debugs(28, 5, "aclIpParseIpData: Lookup Host/IP " << addr1);
333
struct addrinfo *hp = NULL, *x = NULL;
334
struct addrinfo hints;
335
IpAddress *prev_addr = NULL;
337
memset(&hints, 0, sizeof(struct addrinfo));
339
if ( iptype != AF_UNSPEC ) {
340
hints.ai_flags |= AI_NUMERICHOST;
343
#if 0 && USE_IPV6 && !IPV6_SPECIAL_SPLITSTACK
344
hints.ai_flags |= AI_V4MAPPED | AI_ALL;
347
int errcode = xgetaddrinfo(addr1,NULL,&hints,&hp);
349
debugs(28, 0, "aclIpParseIpData: Bad host/IP: '" << addr1 <<
350
"' in '" << t << "', flags=" << hints.ai_flags <<
351
" : (" << errcode << ") " << xgai_strerror(errcode) );
358
for (x = hp; x != NULL;) {
359
if ((r = *Q) == NULL)
360
r = *Q = new acl_ip_data;
362
/* getaddrinfo given a host has a nasty tendency to return duplicate addr's */
363
/* BUT sorted fortunately, so we can drop most of them easily */
366
if ( prev_addr && r->addr1 == *prev_addr) {
367
debugs(28, 3, "aclIpParseIpData: Duplicate host/IP: '" << r->addr1 << "' dropped.");
372
prev_addr = &r->addr1;
374
debugs(28, 3, "aclIpParseIpData: Located host/IP: '" << r->addr1 << "'");
376
r->addr2.SetAnyAddr();
381
debugs(28, 3, "" << addr1 << " --> " << r->addr1 );
385
debugs(28, 0, "aclIpParseIpData: Bad host/IP: '" << t << "'");
396
/* ignore IPv6 addresses when built with IPv4-only */
397
if ( iptype == AF_INET6 ) {
398
debugs(28, 0, "aclIpParseIpData: IPv6 has not been enabled. build with '--enable-ipv6'");
404
if (!*addr1 || !(q->addr1 = addr1)) {
405
debugs(28, 0, "aclIpParseIpData: unknown first address in '" << t << "'");
413
q->addr2.SetAnyAddr();
414
else if (!(q->addr2=addr2) ) {
415
debugs(28, 0, "aclIpParseIpData: unknown second address in '" << t << "'");
421
/* Decode mask (NULL or empty means a exact host mask) */
422
if (!DecodeMask(mask, q->mask, iptype)) {
423
debugs(28, 0, "aclParseIpData: unknown netmask '" << mask << "' in '" << t << "'");
430
changed += q->addr1.ApplyMask(q->mask);
431
changed += q->addr2.ApplyMask(q->mask);
434
debugs(28, 0, "aclIpParseIpData: WARNING: Netmask masks away part of the specified IP in '" << t << "'");
436
debugs(28,9, HERE << "Parsed: " << q->addr1 << "-" << q->addr2 << "/" << q->mask << "(/" << q->mask.GetCIDR() <<")");
438
/* 1.2.3.4/255.255.255.0 --> 1.2.3.0 */
439
/* Same as IPv6 (not so trivial to depict) */
448
while ((t = strtokFile())) {
449
acl_ip_data *q = acl_ip_data::FactoryParse(t);
452
data = data->insert(q, acl_ip_data::NetworkCompare);
461
data->destroy(IPSplay::DefaultFree);
468
data->walk (DumpIpListWalkee, &w);
473
ACLIP::empty () const
475
return data->empty();
479
ACLIP::match(IpAddress &clientip)
481
static acl_ip_data ClientAddress;
483
* aclIpAddrNetworkCompare() takes two acl_ip_data pointers as
484
* arguments, so we must create a fake one for the client's IP
485
* address. Since we are scanning for a single IP mask and addr2
486
* MUST be set to empty.
488
ClientAddress.addr1 = clientip;
489
ClientAddress.addr2.SetEmpty();
490
ClientAddress.mask.SetEmpty();
492
data = data->splay(&ClientAddress, aclIpAddrNetworkCompare);
493
debugs(28, 3, "aclIpMatchIp: '" << clientip << "' " << (splayLastResult ? "NOT found" : "found"));
494
return !splayLastResult;
497
acl_ip_data::acl_ip_data () :addr1(), addr2(), mask(), next (NULL) {}
499
acl_ip_data::acl_ip_data (IpAddress const &anAddress1, IpAddress const &anAddress2, IpAddress const &aMask, acl_ip_data *aNext) : addr1(anAddress1), addr2(anAddress2), mask(aMask), next(aNext) {}