]> git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/Ip.cc
Moved src/ACL* and a few related files into src/acl/.
[thirdparty/squid.git] / src / acl / Ip.cc
1 /*
2 * $Id$
3 *
4 * DEBUG: section 28 Access Control
5 * AUTHOR: Duane Wessels
6 *
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
9 *
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.
18 *
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.
23 *
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.
28 *
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.
32 *
33 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
34 */
35
36 #include "squid.h"
37 #include "acl/Ip.h"
38 #include "acl/Checklist.h"
39 #include "MemBuf.h"
40 #include "wordlist.h"
41
42 void *
43 ACLIP::operator new (size_t byteCount)
44 {
45 fatal ("ACLIP::operator new: unused");
46 return (void *)1;
47 }
48
49 void
50 ACLIP::operator delete (void *address)
51 {
52 fatal ("ACLIP::operator delete: unused");
53 }
54
55 /**
56 * Writes an IP ACL data into a buffer, then copies the buffer into the wordlist given
57 *
58 \param ip ACL data structure to display
59 \param state wordlist structure which is being generated
60 */
61 void
62 ACLIP::DumpIpListWalkee(acl_ip_data * const & ip, void *state)
63 {
64 char tmpbuf[ ((MAX_IPSTRLEN*2)+6) ]; // space for 2 IPs and a CIDR mask(3) and seperators(3).
65 MemBuf mb;
66 wordlist **W = static_cast<wordlist **>(state);
67 tmpbuf[0] = '\0';
68
69 mb.init();
70 assert(mb.max_capacity > 0 && 1==1 );
71
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);
77 mb.clean();
78 }
79
80 /**
81 * print/format an acl_ip_data structure for debugging output.
82 *
83 \param buf string buffer to write to
84 \param len size of the buffer available
85 */
86 void
87 acl_ip_data::toStr(char *buf, int len) const
88 {
89 char *b1 = buf;
90 char *b2 = NULL;
91 char *b3 = NULL;
92 int rlen = 0;
93
94 addr1.NtoA(b1, len - rlen );
95 rlen = strlen(buf);
96 b2 = buf + rlen;
97
98 if (!addr2.IsAnyAddr()) {
99 b2[0] = '-';
100 rlen++;
101 addr2.NtoA(&(b2[1]), len - rlen );
102 rlen = strlen(buf);
103 } else
104 b2[0] = '\0';
105
106 b3 = buf + rlen;
107
108 if (!mask.IsNoAddr()) {
109 b3[0] = '/';
110 rlen++;
111 #if USE_IPV6
112 snprintf(&(b3[1]), (len-rlen), "%u", mask.GetCIDR() - (addr1.IsIPv4()?96:0) );
113 #else
114 snprintf(&(b3[1]), (len-rlen), "%u", mask.GetCIDR() );
115 #endif
116 } else
117 b3[0] = '\0';
118 }
119
120 /*
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.
126 */
127 int
128 aclIpAddrNetworkCompare(acl_ip_data * const &p, acl_ip_data * const &q)
129 {
130 IpAddress A = p->addr1;
131
132 /* apply netmask */
133 A.ApplyMask(q->mask);
134
135 debugs(28,9, "aclIpAddrNetworkCompare: compare: " << p->addr1 << "/" << q->mask << " (" << A << ") vs " <<
136 q->addr1 << "-" << q->addr2 << "/" << q->mask);
137
138 if (q->addr2.IsAnyAddr()) { /* single address check */
139
140 return A.matchIPAddr( q->addr1 );
141
142 } else { /* range address check */
143
144 if ( (A >= q->addr1) && (A <= q->addr2) )
145 return 0; /* valid. inside range. */
146 else
147 return A.matchIPAddr( q->addr1 ); /* outside of range, 'less than' */
148 }
149 }
150
151
152 /*
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.
160 */
161 int
162 acl_ip_data::NetworkCompare(acl_ip_data * const & a, acl_ip_data * const &b)
163 {
164 int ret;
165 bool bina = true;
166 ret = aclIpAddrNetworkCompare(b, a);
167
168 if (ret != 0) {
169 bina = false;
170 ret = aclIpAddrNetworkCompare(a, b);
171 }
172
173 if (ret == 0) {
174 char buf_n1[3*(MAX_IPSTRLEN+1)];
175 char buf_n2[3*(MAX_IPSTRLEN+1)];
176 if (bina) {
177 b->toStr(buf_n1, 3*(MAX_IPSTRLEN+1));
178 a->toStr(buf_n2, 3*(MAX_IPSTRLEN+1));
179 } else {
180 a->toStr(buf_n1, 3*(MAX_IPSTRLEN+1));
181 b->toStr(buf_n2, 3*(MAX_IPSTRLEN+1));
182 }
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 << "'");
186 }
187
188 return ret;
189 }
190
191 /*
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!
195 */
196 bool
197 acl_ip_data::DecodeMask(const char *asc, IpAddress &mask, int ctype)
198 {
199 char junk;
200 int a1 = 0;
201
202 /* default is a mask that doesn't change any IP */
203 mask.SetNoAddr();
204
205 if (!asc || !*asc) {
206 return true;
207 }
208
209 /* An int mask 128, 32 */
210 if ((sscanf(asc, "%d%c", &a1, &junk)==1) &&
211 (a1 <= 128) && (a1 >= 0)
212 ) {
213 return mask.ApplyMask(a1, ctype);
214 }
215
216 /* dotted notation */
217 /* assignment returns true if asc contained an IP address as text */
218 if ((mask = asc)) {
219 #if USE_IPV6
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.");
222 if(mask.IsIPv4()) {
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. */
230 mask.SetNoAddr();
231 return mask.ApplyMask(m,AF_INET);
232 }
233 #endif /* USE_IPV6 */
234 return true;
235 }
236
237 return false;
238 }
239
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"
250
251 acl_ip_data *
252 acl_ip_data::FactoryParse(const char *t)
253 {
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;
259 IpAddress temp;
260 char c;
261 unsigned int changed;
262 acl_ip_data *q = new acl_ip_data;
263 int iptype = AF_UNSPEC;
264
265 debugs(28, 5, "aclIpParseIpData: " << t);
266
267 /* Special ACL RHS "all" matches entire Internet */
268 if (strcasecmp(t, "all") == 0) {
269 q->addr1.SetAnyAddr();
270 q->addr2.SetEmpty();
271 q->mask.SetAnyAddr();
272 return q;
273 }
274
275 #if USE_IPV6
276 /* Special ACL RHS "ipv6" matches IPv6-Unicast Internet */
277 if (strcasecmp(t, "ipv6") == 0) {
278 t = "2000::/3";
279 }
280 #endif
281
282 // IPv4
283 if (sscanf(t, SCAN_ACL1_4, addr1, addr2, mask) == 3) {
284 iptype=AF_INET;
285 } else if (sscanf(t, SCAN_ACL2_4, addr1, addr2, &c) >= 2) {
286 mask[0] = '\0';
287 iptype=AF_INET;
288 } else if (sscanf(t, SCAN_ACL3_4, addr1, mask) == 2) {
289 addr2[0] = '\0';
290 iptype=AF_INET;
291 } else if (sscanf(t, SCAN_ACL4_4, addr1,&c) == 2) {
292 addr2[0] = '\0';
293 mask[0] = '\0';
294 iptype=AF_INET;
295
296 // IPv6
297 } else if (sscanf(t, SCAN_ACL1_6, addr1, addr2, mask) == 3) {
298 iptype=AF_INET6;
299 } else if (sscanf(t, SCAN_ACL2_6, addr1, addr2, &c) >= 2) {
300 mask[0] = '\0';
301 iptype=AF_INET6;
302 } else if (sscanf(t, SCAN_ACL3_6, addr1, mask) == 2) {
303 addr2[0] = '\0';
304 iptype=AF_INET6;
305 } else if (sscanf(t, SCAN_ACL4_6, addr1, mask) == 2) {
306 addr2[0] = '\0';
307 iptype=AF_INET6;
308
309 // Neither
310 } else if (sscanf(t, "%[^/]/%s", addr1, mask) == 2) {
311 addr2[0] = '\0';
312 } else if (sscanf(t, "%s", addr1) == 1) {
313 /*
314 * Note, must use plain xgetaddrinfo() here because at startup
315 * ipcache hasn't been initialized
316 * TODO: offload this to one of the IpAddress lookups.
317 */
318
319 debugs(28, 5, "aclIpParseIpData: Lookup Host/IP " << addr1);
320 struct addrinfo *hp = NULL, *x = NULL;
321 struct addrinfo hints;
322 IpAddress *prev_addr = NULL;
323
324 memset(&hints, 0, sizeof(struct addrinfo));
325
326 if ( iptype != AF_UNSPEC ) {
327 hints.ai_flags |= AI_NUMERICHOST;
328 }
329
330 #if 0 && USE_IPV6 && !IPV6_SPECIAL_SPLITSTACK
331 hints.ai_flags |= AI_V4MAPPED | AI_ALL;
332 #endif
333
334 int errcode = xgetaddrinfo(addr1,NULL,&hints,&hp);
335 if (hp == NULL) {
336 debugs(28, 0, "aclIpParseIpData: Bad host/IP: '" << addr1 <<
337 "' in '" << t << "', flags=" << hints.ai_flags <<
338 " : (" << errcode << ") " << xgai_strerror(errcode) );
339 self_destruct();
340 return NULL;
341 }
342
343 Q = &q;
344
345 for (x = hp; x != NULL;) {
346 if ((r = *Q) == NULL)
347 r = *Q = new acl_ip_data;
348
349 /* getaddrinfo given a host has a nasty tendency to return duplicate addr's */
350 /* BUT sorted fortunately, so we can drop most of them easily */
351 r->addr1 = *x;
352 x = x->ai_next;
353 if ( prev_addr && r->addr1 == *prev_addr) {
354 debugs(28, 3, "aclIpParseIpData: Duplicate host/IP: '" << r->addr1 << "' dropped.");
355 delete r;
356 *Q = NULL;
357 continue;
358 } else
359 prev_addr = &r->addr1;
360
361 debugs(28, 3, "aclIpParseIpData: Located host/IP: '" << r->addr1 << "'");
362
363 r->addr2.SetAnyAddr();
364 r->mask.SetNoAddr();
365
366 Q = &r->next;
367
368 debugs(28, 3, "" << addr1 << " --> " << r->addr1 );
369 }
370
371 if (*Q != NULL) {
372 debugs(28, 0, "aclIpParseIpData: Bad host/IP: '" << t << "'");
373 self_destruct();
374 return NULL;
375 }
376
377 xfreeaddrinfo(hp);
378
379 return q;
380 }
381
382 #if !USE_IPV6
383 /* ignore IPv6 addresses when built with IPv4-only */
384 if ( iptype == AF_INET6 ) {
385 debugs(28, 0, "aclIpParseIpData: IPv6 has not been enabled. build with '--enable-ipv6'");
386 return NULL;
387 }
388 #endif
389
390 /* Decode addr1 */
391 if (!*addr1 || !(q->addr1 = addr1)) {
392 debugs(28, 0, "aclIpParseIpData: unknown first address in '" << t << "'");
393 delete q;
394 self_destruct();
395 return NULL;
396 }
397
398 /* Decode addr2 */
399 if (!*addr2)
400 q->addr2.SetAnyAddr();
401 else if (!(q->addr2=addr2) ) {
402 debugs(28, 0, "aclIpParseIpData: unknown second address in '" << t << "'");
403 delete q;
404 self_destruct();
405 return NULL;
406 }
407
408 /* Decode mask (NULL or empty means a exact host mask) */
409 if (!DecodeMask(mask, q->mask, iptype)) {
410 debugs(28, 0, "aclParseIpData: unknown netmask '" << mask << "' in '" << t << "'");
411 delete q;
412 self_destruct();
413 return NULL;
414 }
415
416 changed = 0;
417 changed += q->addr1.ApplyMask(q->mask);
418 changed += q->addr2.ApplyMask(q->mask);
419
420 if (changed)
421 debugs(28, 0, "aclIpParseIpData: WARNING: Netmask masks away part of the specified IP in '" << t << "'");
422
423 /* 1.2.3.4/255.255.255.0 --> 1.2.3.0 */
424 /* Same as IPv6 (not so trivial to depict) */
425 return q;
426 }
427
428 void
429 ACLIP::parse()
430 {
431 char *t = NULL;
432
433 while ((t = strtokFile())) {
434 acl_ip_data *q = acl_ip_data::FactoryParse(t);
435
436 while (q != NULL) {
437 data = data->insert(q, acl_ip_data::NetworkCompare);
438 q = q->next;
439 }
440 }
441 }
442
443 ACLIP::~ACLIP()
444 {
445 if (data)
446 data->destroy(IPSplay::DefaultFree);
447 }
448
449 wordlist *
450 ACLIP::dump() const
451 {
452 wordlist *w = NULL;
453 data->walk (DumpIpListWalkee, &w);
454 return w;
455 }
456
457 bool
458 ACLIP::empty () const
459 {
460 return data->empty();
461 }
462
463 int
464 ACLIP::match(IpAddress &clientip)
465 {
466 static acl_ip_data ClientAddress;
467 /*
468 * aclIpAddrNetworkCompare() takes two acl_ip_data pointers as
469 * arguments, so we must create a fake one for the client's IP
470 * address. Since we are scanning for a single IP mask and addr2
471 * MUST be set to empty.
472 */
473 ClientAddress.addr1 = clientip;
474 ClientAddress.addr2.SetEmpty();
475 ClientAddress.mask.SetEmpty();
476
477 data = data->splay(&ClientAddress, aclIpAddrNetworkCompare);
478 debugs(28, 3, "aclIpMatchIp: '" << clientip << "' " << (splayLastResult ? "NOT found" : "found"));
479 return !splayLastResult;
480 }
481
482 acl_ip_data::acl_ip_data () :addr1(), addr2(), mask(), next (NULL) {}
483
484 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) {}