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