]> git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/Ip.cc
Merged from trunk r12948.
[thirdparty/squid.git] / src / acl / Ip.cc
1 /*
2 * DEBUG: section 28 Access Control
3 * AUTHOR: Duane Wessels
4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
32 */
33
34 #include "squid.h"
35 #include "acl/Ip.h"
36 #include "acl/Checklist.h"
37 #include "cache_cf.h"
38 #include "Debug.h"
39 #include "ip/tools.h"
40 #include "MemBuf.h"
41 #include "wordlist.h"
42
43 void *
44 ACLIP::operator new (size_t byteCount)
45 {
46 fatal ("ACLIP::operator new: unused");
47 return (void *)1;
48 }
49
50 void
51 ACLIP::operator delete (void *address)
52 {
53 fatal ("ACLIP::operator delete: unused");
54 }
55
56 /**
57 * Writes an IP ACL data into a buffer, then copies the buffer into the wordlist given
58 *
59 \param ip ACL data structure to display
60 \param state wordlist structure which is being generated
61 */
62 void
63 ACLIP::DumpIpListWalkee(acl_ip_data * const & ip, void *state)
64 {
65 char tmpbuf[ ((MAX_IPSTRLEN*2)+6) ]; // space for 2 IPs and a CIDR mask(3) and seperators(3).
66 MemBuf mb;
67 wordlist **W = static_cast<wordlist **>(state);
68 tmpbuf[0] = '\0';
69
70 mb.init();
71 assert(mb.max_capacity > 0 && 1==1 );
72
73 ip->toStr(tmpbuf, sizeof(tmpbuf) );
74 assert(mb.max_capacity > 0 && 2==2 );
75 mb.append(tmpbuf, strlen(tmpbuf) );
76 assert(mb.max_capacity > 0 && 3==3);
77 wordlistAdd(W, mb.buf);
78 mb.clean();
79 }
80
81 /**
82 * print/format an acl_ip_data structure for debugging output.
83 *
84 \param buf string buffer to write to
85 \param len size of the buffer available
86 */
87 void
88 acl_ip_data::toStr(char *buf, int len) const
89 {
90 char *b1 = buf;
91 char *b2 = NULL;
92 char *b3 = NULL;
93 int rlen = 0;
94
95 addr1.toStr(b1, len - rlen );
96 rlen = strlen(buf);
97 b2 = buf + rlen;
98
99 if (!addr2.isAnyAddr()) {
100 b2[0] = '-';
101 ++rlen;
102 addr2.toStr(&(b2[1]), len - rlen );
103 rlen = strlen(buf);
104 } else
105 b2[0] = '\0';
106
107 b3 = buf + rlen;
108
109 if (!mask.isNoAddr()) {
110 b3[0] = '/';
111 ++rlen;
112 int cidr = mask.cidr() - (addr1.isIPv4()?96:0);
113 snprintf(&(b3[1]), (len-rlen), "%u", (unsigned int)(cidr<0?0:cidr) );
114 } else
115 b3[0] = '\0';
116 }
117
118 /*
119 * aclIpAddrNetworkCompare - The guts of the comparison for IP ACLs
120 * matching checks. The first argument (p) is a "host" address,
121 * i.e. the IP address of a cache client. The second argument (q)
122 * is an entry in some address-based access control element. This
123 * function is called via ACLIP::match() and the splay library.
124 */
125 int
126 aclIpAddrNetworkCompare(acl_ip_data * const &p, acl_ip_data * const &q)
127 {
128 Ip::Address A = p->addr1;
129
130 /* apply netmask */
131 A.applyMask(q->mask);
132
133 debugs(28,9, "aclIpAddrNetworkCompare: compare: " << p->addr1 << "/" << q->mask << " (" << A << ") vs " <<
134 q->addr1 << "-" << q->addr2 << "/" << q->mask);
135
136 if (q->addr2.isAnyAddr()) { /* single address check */
137
138 return A.matchIPAddr( q->addr1 );
139
140 } else { /* range address check */
141
142 if ( (A >= q->addr1) && (A <= q->addr2) )
143 return 0; /* valid. inside range. */
144 else
145 return A.matchIPAddr( q->addr1 ); /* outside of range, 'less than' */
146 }
147 }
148
149 /*
150 * acl_ip_data::NetworkCompare - Compare two acl_ip_data entries. Strictly
151 * used by the splay insertion routine. It emits a warning if it
152 * detects a "collision" or overlap that would confuse the splay
153 * sorting algorithm. Much like aclDomainCompare.
154 * The first argument (p) is a "host" address, i.e. the IP address of a cache client.
155 * The second argument (b) is a "network" address that might have a subnet and/or range.
156 * We mask the host address bits with the network subnet mask.
157 */
158 int
159 acl_ip_data::NetworkCompare(acl_ip_data * const & a, acl_ip_data * const &b)
160 {
161 int ret;
162 bool bina = true;
163 ret = aclIpAddrNetworkCompare(b, a);
164
165 if (ret != 0) {
166 bina = false;
167 ret = aclIpAddrNetworkCompare(a, b);
168 }
169
170 if (ret == 0) {
171 char buf_n1[3*(MAX_IPSTRLEN+1)];
172 char buf_n2[3*(MAX_IPSTRLEN+1)];
173 if (bina) {
174 b->toStr(buf_n1, 3*(MAX_IPSTRLEN+1));
175 a->toStr(buf_n2, 3*(MAX_IPSTRLEN+1));
176 } else {
177 a->toStr(buf_n1, 3*(MAX_IPSTRLEN+1));
178 b->toStr(buf_n2, 3*(MAX_IPSTRLEN+1));
179 }
180 debugs(28, DBG_CRITICAL, "WARNING: (" << (bina?'B':'A') << ") '" << buf_n1 << "' is a subnetwork of (" << (bina?'A':'B') << ") '" << buf_n2 << "'");
181 debugs(28, DBG_CRITICAL, "WARNING: because of this '" << (bina?buf_n2:buf_n1) << "' is ignored to keep splay tree searching predictable");
182 debugs(28, DBG_CRITICAL, "WARNING: You should probably remove '" << buf_n1 << "' from the ACL named '" << AclMatchedName << "'");
183 }
184
185 return ret;
186 }
187
188 /**
189 * Decode an ascii representation (asc) of a IP netmask address or CIDR,
190 * and place resulting information in mask.
191 * This function should NOT be called if 'asc' is a hostname!
192 */
193 bool
194 acl_ip_data::DecodeMask(const char *asc, Ip::Address &mask, int ctype)
195 {
196 char junk;
197 int a1 = 0;
198
199 /* default is a mask that doesn't change any IP */
200 mask.setNoAddr();
201
202 if (!asc || !*asc) {
203 return true;
204 }
205
206 /* An int mask 128, 32 */
207 if ((sscanf(asc, "%d%c", &a1, &junk)==1) &&
208 (a1 <= 128) && (a1 >= 0)
209 ) {
210 return mask.applyMask(a1, ctype);
211 }
212
213 /* dotted notation */
214 /* assignment returns true if asc contained an IP address as text */
215 if ((mask = asc)) {
216 /* HACK: IPv4 netmasks don't cleanly map to IPv6 masks. */
217 debugs(28, DBG_CRITICAL, "WARNING: Netmasks are deprecated. Please use CIDR masks instead.");
218 if (mask.isIPv4()) {
219 /* locate what CIDR mask was _probably_ meant to be in its native protocol format. */
220 /* this will completely crap out with a security fail-open if the admin is playing mask tricks */
221 /* however, thats their fault, and we do warn. see bug 2601 for the effects if we don't do this. */
222 unsigned int m = mask.cidr();
223 debugs(28, DBG_CRITICAL, "WARNING: IPv4 netmasks are particularly nasty when used to compare IPv6 to IPv4 ranges.");
224 debugs(28, DBG_CRITICAL, "WARNING: For now we will assume you meant to write /" << m);
225 /* reset the mask completely, and crop to the CIDR boundary back properly. */
226 mask.setNoAddr();
227 return mask.applyMask(m,AF_INET);
228 }
229 return true;
230 }
231
232 return false;
233 }
234
235 /* Handle either type of address, IPv6 will be discarded with a warning if disabled */
236 #define SCAN_ACL1_6 "%[0123456789ABCDEFabcdef:]-%[0123456789ABCDEFabcdef:]/%[0123456789]"
237 #define SCAN_ACL2_6 "%[0123456789ABCDEFabcdef:]-%[0123456789ABCDEFabcdef:]%c"
238 #define SCAN_ACL3_6 "%[0123456789ABCDEFabcdef:]/%[0123456789]"
239 #define SCAN_ACL4_6 "%[0123456789ABCDEFabcdef:]/%c"
240 /* We DO need to know which is which though, for proper CIDR masking. */
241 #define SCAN_ACL1_4 "%[0123456789.]-%[0123456789.]/%[0123456789.]"
242 #define SCAN_ACL2_4 "%[0123456789.]-%[0123456789.]%c"
243 #define SCAN_ACL3_4 "%[0123456789.]/%[0123456789.]"
244 #define SCAN_ACL4_4 "%[0123456789.]/%c"
245
246 acl_ip_data *
247 acl_ip_data::FactoryParse(const char *t)
248 {
249 LOCAL_ARRAY(char, addr1, 256);
250 LOCAL_ARRAY(char, addr2, 256);
251 LOCAL_ARRAY(char, mask, 256);
252 acl_ip_data *r = NULL;
253 acl_ip_data **Q = NULL;
254 Ip::Address temp;
255 char c;
256 unsigned int changed;
257 acl_ip_data *q = new acl_ip_data;
258 int iptype = AF_UNSPEC;
259
260 debugs(28, 5, "aclIpParseIpData: " << t);
261
262 /* Special ACL RHS "all" matches entire Internet */
263 if (strcmp(t, "all") == 0) {
264 debugs(28, 9, "aclIpParseIpData: magic 'all' found.");
265 q->addr1.setAnyAddr();
266 q->addr2.setEmpty();
267 q->mask.setAnyAddr();
268 return q;
269 }
270
271 /* Detect some old broken strings equivalent to 'all'.
272 * treat them nicely. But be loud until its fixed. */
273 if (strcmp(t, "0/0") == 0 || strcmp(t, "0.0.0.0/0") == 0 || strcmp(t, "0.0.0.0/0.0.0.0") == 0 ||
274 strcmp(t, "0.0.0.0-255.255.255.255") == 0 || strcmp(t, "0.0.0.0-0.0.0.0/0") == 0) {
275
276 debugs(28,DBG_CRITICAL, "ERROR: '" << t << "' needs to be replaced by the term 'all'.");
277 debugs(28,DBG_CRITICAL, "SECURITY NOTICE: Overriding config setting. Using 'all' instead.");
278 q->addr1.setAnyAddr();
279 q->addr2.setEmpty();
280 q->mask.setAnyAddr();
281 return q;
282 }
283
284 /* Special ACL RHS "ipv4" matches IPv4 Internet
285 * A nod to IANA; we include the entire class space in case
286 * they manage to find a way to recover and use it */
287 if (strcmp(t, "ipv4") == 0) {
288 q->mask.setNoAddr();
289 q->mask.applyMask(0, AF_INET);
290 return q;
291 }
292
293 /* Special ACL RHS "ipv6" matches IPv6-Unicast Internet */
294 if (strcmp(t, "ipv6") == 0) {
295 debugs(28, 9, "aclIpParseIpData: magic 'ipv6' found.");
296 r = q; // save head of the list for result.
297
298 /* 0000::/4 is a mix of localhost and obsolete IPv4-mapping space. Not valid outside this host. */
299
300 /* Future global unicast space: 1000::/4 */
301 q->addr1 = "1000::";
302 q->mask.setNoAddr();
303 q->mask.applyMask(4, AF_INET6);
304
305 /* Current global unicast space: 2000::/4 = (2000::/4 - 3000::/4) */
306 q->next = new acl_ip_data;
307 q = q->next;
308 q->addr1 = "2000::";
309 q->mask.setNoAddr();
310 q->mask.applyMask(3, AF_INET6);
311
312 /* Future global unicast space: 4000::/2 = (4000::/4 - 7000::/4) */
313 q->next = new acl_ip_data;
314 q = q->next;
315 q->addr1 = "4000::";
316 q->mask.setNoAddr();
317 q->mask.applyMask(2, AF_INET6);
318
319 /* Future global unicast space: 8000::/2 = (8000::/4 - B000::/4) */
320 q->next = new acl_ip_data;
321 q = q->next;
322 q->addr1 = "8000::";
323 q->mask.setNoAddr();
324 q->mask.applyMask(2, AF_INET6);
325
326 /* Future global unicast space: C000::/3 = (C000::/4 - D000::/4) */
327 q->next = new acl_ip_data;
328 q = q->next;
329 q->addr1 = "C000::";
330 q->mask.setNoAddr();
331 q->mask.applyMask(3, AF_INET6);
332
333 /* Future global unicast space: E000::/4 */
334 q->next = new acl_ip_data;
335 q = q->next;
336 q->addr1 = "E000::";
337 q->mask.setNoAddr();
338 q->mask.applyMask(4, AF_INET6);
339
340 /* F000::/4 is mostly reserved non-unicast. With some exceptions ... */
341
342 /* RFC 4193 Unique-Local unicast space: FC00::/7 */
343 q->next = new acl_ip_data;
344 q = q->next;
345 q->addr1 = "FC00::";
346 q->mask.setNoAddr();
347 q->mask.applyMask(7, AF_INET6);
348
349 /* Link-Local unicast space: FE80::/10 */
350 q->next = new acl_ip_data;
351 q = q->next;
352 q->addr1 = "FE80::";
353 q->mask.setNoAddr();
354 q->mask.applyMask(10, AF_INET6);
355
356 return r;
357 }
358
359 // IPv4
360 if (sscanf(t, SCAN_ACL1_4, addr1, addr2, mask) == 3) {
361 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN1-v4: " << SCAN_ACL1_4);
362 iptype=AF_INET;
363 } else if (sscanf(t, SCAN_ACL2_4, addr1, addr2, &c) >= 2) {
364 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN2-v4: " << SCAN_ACL2_4);
365 mask[0] = '\0';
366 iptype=AF_INET;
367 } else if (sscanf(t, SCAN_ACL3_4, addr1, mask) == 2) {
368 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN3-v4: " << SCAN_ACL3_4);
369 addr2[0] = '\0';
370 iptype=AF_INET;
371 } else if (sscanf(t, SCAN_ACL4_4, addr1,&c) == 2) {
372 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN4-v4: " << SCAN_ACL4_4);
373 addr2[0] = '\0';
374 mask[0] = '\0';
375 iptype=AF_INET;
376
377 // IPv6
378 } else if (sscanf(t, SCAN_ACL1_6, addr1, addr2, mask) == 3) {
379 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN1-v6: " << SCAN_ACL1_6);
380 iptype=AF_INET6;
381 } else if (sscanf(t, SCAN_ACL2_6, addr1, addr2, &c) >= 2) {
382 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN2-v6: " << SCAN_ACL2_6);
383 mask[0] = '\0';
384 iptype=AF_INET6;
385 } else if (sscanf(t, SCAN_ACL3_6, addr1, mask) == 2) {
386 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN3-v6: " << SCAN_ACL3_6);
387 addr2[0] = '\0';
388 iptype=AF_INET6;
389 } else if (sscanf(t, SCAN_ACL4_6, addr1, mask) == 2) {
390 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: SCAN4-v6: " << SCAN_ACL4_6);
391 addr2[0] = '\0';
392 iptype=AF_INET6;
393
394 // Neither
395 } else if (sscanf(t, "%[^/]/%s", addr1, mask) == 2) {
396 debugs(28, 9, "aclIpParseIpData: '" << t << "' matched: non-IP pattern: %[^/]/%s");
397 addr2[0] = '\0';
398 } else if (sscanf(t, "%s", addr1) == 1) {
399 /*
400 * Note, must use plain getaddrinfo() here because at startup
401 * ipcache hasn't been initialized
402 * TODO: offload this to one of the Ip::Address lookups.
403 */
404
405 debugs(28, 5, "aclIpParseIpData: Lookup Host/IP " << addr1);
406 struct addrinfo *hp = NULL, *x = NULL;
407 struct addrinfo hints;
408 Ip::Address *prev_addr = NULL;
409
410 memset(&hints, 0, sizeof(struct addrinfo));
411
412 int errcode = getaddrinfo(addr1,NULL,&hints,&hp);
413 if (hp == NULL) {
414 debugs(28, DBG_CRITICAL, "aclIpParseIpData: Bad host/IP: '" << addr1 <<
415 "' in '" << t << "', flags=" << hints.ai_flags <<
416 " : (" << errcode << ") " << gai_strerror(errcode) );
417 self_destruct();
418 return NULL;
419 }
420
421 Q = &q;
422
423 for (x = hp; x != NULL;) {
424 if ((r = *Q) == NULL)
425 r = *Q = new acl_ip_data;
426
427 /* getaddrinfo given a host has a nasty tendency to return duplicate addr's */
428 /* BUT sorted fortunately, so we can drop most of them easily */
429 r->addr1 = *x;
430 x = x->ai_next;
431 if ( prev_addr && r->addr1 == *prev_addr) {
432 debugs(28, 3, "aclIpParseIpData: Duplicate host/IP: '" << r->addr1 << "' dropped.");
433 delete r;
434 *Q = NULL;
435 continue;
436 } else
437 prev_addr = &r->addr1;
438
439 debugs(28, 3, "aclIpParseIpData: Located host/IP: '" << r->addr1 << "'");
440
441 r->addr2.setAnyAddr();
442 r->mask.setNoAddr();
443
444 Q = &r->next;
445
446 debugs(28, 3, "" << addr1 << " --> " << r->addr1 );
447 }
448
449 if (*Q != NULL) {
450 debugs(28, DBG_CRITICAL, "aclIpParseIpData: Bad host/IP: '" << t << "'");
451 self_destruct();
452 return NULL;
453 }
454
455 freeaddrinfo(hp);
456
457 return q;
458 }
459
460 /* ignore IPv6 addresses when built with IPv4-only */
461 if ( iptype == AF_INET6 && !Ip::EnableIpv6) {
462 debugs(28, DBG_IMPORTANT, "aclIpParseIpData: IPv6 has not been enabled.");
463 delete q;
464 return NULL;
465 }
466
467 /* Decode addr1 */
468 if (!*addr1 || !(q->addr1 = addr1)) {
469 debugs(28, DBG_CRITICAL, "aclIpParseIpData: unknown first address in '" << t << "'");
470 delete q;
471 self_destruct();
472 return NULL;
473 }
474
475 /* Decode addr2 */
476 if (!*addr2)
477 q->addr2.setAnyAddr();
478 else if (!(q->addr2=addr2) ) {
479 debugs(28, DBG_CRITICAL, "aclIpParseIpData: unknown second address in '" << t << "'");
480 delete q;
481 self_destruct();
482 return NULL;
483 }
484
485 /* Decode mask (NULL or empty means a exact host mask) */
486 if (!DecodeMask(mask, q->mask, iptype)) {
487 debugs(28, DBG_CRITICAL, "aclParseIpData: unknown netmask '" << mask << "' in '" << t << "'");
488 delete q;
489 self_destruct();
490 return NULL;
491 }
492
493 changed = 0;
494 changed += q->addr1.applyMask(q->mask);
495 changed += q->addr2.applyMask(q->mask);
496
497 if (changed)
498 debugs(28, DBG_CRITICAL, "aclIpParseIpData: WARNING: Netmask masks away part of the specified IP in '" << t << "'");
499
500 debugs(28,9, HERE << "Parsed: " << q->addr1 << "-" << q->addr2 << "/" << q->mask << "(/" << q->mask.cidr() <<")");
501
502 /* 1.2.3.4/255.255.255.0 --> 1.2.3.0 */
503 /* Same as IPv6 (not so trivial to depict) */
504 return q;
505 }
506
507 void
508 ACLIP::parse()
509 {
510 flags.parseFlags();
511
512 while (char *t = strtokFile()) {
513 acl_ip_data *q = acl_ip_data::FactoryParse(t);
514
515 while (q != NULL) {
516 /* pop each result off the list and add it to the data tree individually */
517 acl_ip_data *next_node = q->next;
518 q->next = NULL;
519 data = data->insert(q, acl_ip_data::NetworkCompare);
520 q = next_node;
521 }
522 }
523 }
524
525 ACLIP::~ACLIP()
526 {
527 if (data)
528 data->destroy(IPSplay::DefaultFree);
529 }
530
531 wordlist *
532 ACLIP::dump() const
533 {
534 wordlist *w = NULL;
535 data->walk (DumpIpListWalkee, &w);
536 return w;
537 }
538
539 bool
540 ACLIP::empty () const
541 {
542 return data->empty();
543 }
544
545 int
546 ACLIP::match(Ip::Address &clientip)
547 {
548 static acl_ip_data ClientAddress;
549 /*
550 * aclIpAddrNetworkCompare() takes two acl_ip_data pointers as
551 * arguments, so we must create a fake one for the client's IP
552 * address. Since we are scanning for a single IP mask and addr2
553 * MUST be set to empty.
554 */
555 ClientAddress.addr1 = clientip;
556 ClientAddress.addr2.setEmpty();
557 ClientAddress.mask.setEmpty();
558
559 data = data->splay(&ClientAddress, aclIpAddrNetworkCompare);
560 debugs(28, 3, "aclIpMatchIp: '" << clientip << "' " << (splayLastResult ? "NOT found" : "found"));
561 return !splayLastResult;
562 }
563
564 acl_ip_data::acl_ip_data () :addr1(), addr2(), mask(), next (NULL) {}
565
566 acl_ip_data::acl_ip_data (Ip::Address const &anAddress1, Ip::Address const &anAddress2, Ip::Address const &aMask, acl_ip_data *aNext) : addr1(anAddress1), addr2(anAddress2), mask(aMask), next(aNext) {}