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