/* * $Id$ * * DEBUG: section 28 Access Control * AUTHOR: Duane Wessels * * SQUID Web Proxy Cache http://www.squid-cache.org/ * ---------------------------------------------------------- * * Squid is the result of efforts by numerous individuals from * the Internet community; see the CONTRIBUTORS file for full * details. Many organizations have provided support for Squid's * development; see the SPONSORS file for full details. Squid is * Copyrighted (C) 2001 by the Regents of the University of * California; see the COPYRIGHT file for full details. Squid * incorporates software developed and/or copyrighted by other * sources; see the CREDITS file for full details. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * * Copyright (c) 2003, Robert Collins */ #include "squid.h" #include "ACLIP.h" #include "ACLChecklist.h" #include "MemBuf.h" #include "wordlist.h" void * ACLIP::operator new (size_t byteCount) { fatal ("ACLIP::operator new: unused"); return (void *)1; } void ACLIP::operator delete (void *address) { fatal ("ACLIP::operator delete: unused"); } void ACLIP::DumpIpListWalkee(acl_ip_data * const & ip, void *state) { MemBuf mb; wordlist **W = static_cast(state); mb.init(); mb.Printf("%s", inet_ntoa(ip->addr1)); if (ip->addr2.s_addr != any_addr.s_addr) mb.Printf("-%s", inet_ntoa(ip->addr2)); if (ip->mask.s_addr != no_addr.s_addr) mb.Printf("/%s", inet_ntoa(ip->mask)); wordlistAdd(W, mb.buf); mb.clean(); } /* * aclIpDataToStr - print/format an acl_ip_data structure for * debugging output. */ void acl_ip_data::toStr(char *buf, int len) const { char b1[20]; char b2[20]; char b3[20]; snprintf(b1, 20, "%s", inet_ntoa(addr1)); if (addr2.s_addr != any_addr.s_addr) snprintf(b2, 20, "-%s", inet_ntoa(addr2)); else b2[0] = '\0'; if (mask.s_addr != no_addr.s_addr) snprintf(b3, 20, "/%s", inet_ntoa(mask)); else b3[0] = '\0'; snprintf(buf, len, "%s%s%s", b1, b2, b3); } /* * aclIpAddrNetworkCompare - The guts of the comparison for IP ACLs * matching checks. The first argument (p) is a "host" address, * i.e. the IP address of a cache client. The second argument (q) * is an entry in some address-based access control element. This * function is called via ACLIP::match() and the splay library. */ int aclIpAddrNetworkCompare(acl_ip_data * const &p, acl_ip_data * const &q) { struct IN_ADDR A = p->addr1; const struct IN_ADDR B = q->addr1; const struct IN_ADDR C = q->addr2; A.s_addr &= q->mask.s_addr; /* apply netmask */ if (C.s_addr == 0) { /* single address check */ if (ntohl(A.s_addr) > ntohl(B.s_addr)) return 1; else if (ntohl(A.s_addr) < ntohl(B.s_addr)) return -1; else return 0; } else { /* range address check */ if (ntohl(A.s_addr) > ntohl(C.s_addr)) return 1; else if (ntohl(A.s_addr) < ntohl(B.s_addr)) return -1; else return 0; } } /* * acl_ip_data::NetworkCompare - Compare two acl_ip_data entries. Strictly * used by the splay insertion routine. It emits a warning if it * detects a "collision" or overlap that would confuse the splay * sorting algorithm. Much like aclDomainCompare. * The first argument (p) is a "host" address, i.e. the IP address of a cache client. * The second argument (b) is a "network" address that might have a subnet and/or range. * We mask the host address bits with the network subnet mask. */ int acl_ip_data::NetworkCompare(acl_ip_data * const & a, acl_ip_data * const &b) { int ret; ret = aclIpAddrNetworkCompare(b, a); if (ret != 0) { ret = aclIpAddrNetworkCompare(a, b); } if (ret == 0) { char buf_n1[60]; char buf_n2[60]; char buf_a[60]; b->toStr(buf_n1, 60); a->toStr(buf_n2, 60); a->toStr(buf_a, 60); /* TODO: this warning may display the wrong way around */ debugs(28, 0, "WARNING: '" << buf_n1 << "' is a subnetwork of '" << buf_n2 << "'"); debugs(28, 0, "WARNING: because of this '" << buf_a << "' is ignored to keep splay tree searching predictable"); debugs(28, 0, "WARNING: You should probably remove '" << buf_n1 << "' from the ACL named '" << AclMatchedName << "'"); } return ret; } /* * Decode an ascii representation (asc) of a IP netmask address or CIDR, * and place resulting information in mask. * This function should NOT be called if 'asc' is a hostname! */ bool acl_ip_data::DecodeMask(const char *asc, struct IN_ADDR *mask) { char junk; int a1 = 0; if (!asc || !*asc) { mask->s_addr = htonl(0xFFFFFFFFul); return true; } if (sscanf(asc, "%d%c", &a1, &junk) == 1 && a1 >= 0 && a1 < 33) { /* a significant bits value for a mask */ mask->s_addr = a1 ? htonl(0xfffffffful << (32 - a1)) : 0; return true; } /* dotted notation */ if (safe_inet_addr(asc, mask)) return true; return false; } #define SCAN_ACL1 "%[0123456789.]-%[0123456789.]/%[0123456789.]" #define SCAN_ACL2 "%[0123456789.]-%[0123456789.]%c" #define SCAN_ACL3 "%[0123456789.]/%[0123456789.]" #define SCAN_ACL4 "%[0123456789.]%c" acl_ip_data * acl_ip_data::FactoryParse(const char *t) { LOCAL_ARRAY(char, addr2, 256); LOCAL_ARRAY(char, mask, 256); acl_ip_data *r; acl_ip_data **Q; char **x; char c; debugs(28, 5, "aclParseIpData: " << t); acl_ip_data *q = new acl_ip_data; if (!strcasecmp(t, "all")) { q->addr1.s_addr = 0; q->addr2.s_addr = 0; q->mask.s_addr = 0; return q; } LOCAL_ARRAY(char, addr1, 256); if (sscanf(t, SCAN_ACL1, addr1, addr2, mask) == 3) { (void) 0; } else if (sscanf(t, SCAN_ACL2, addr1, addr2, &c) == 2) { mask[0] = '\0'; } else if (sscanf(t, SCAN_ACL3, addr1, mask) == 2) { addr2[0] = '\0'; } else if (sscanf(t, SCAN_ACL4, addr1, &c) == 1) { addr2[0] = '\0'; mask[0] = '\0'; } else if (sscanf(t, "%[^/]/%s", addr1, mask) == 2) { addr2[0] = '\0'; } else if (sscanf(t, "%s", addr1) == 1) { /* * Note, must use plain gethostbyname() here because at startup * ipcache hasn't been initialized */ struct hostent *hp; if ((hp = gethostbyname(addr1)) == NULL) { debugs(28, 0, "aclParseIpData: Bad host/IP: '" << t << "'"); self_destruct(); } Q = &q; for (x = hp->h_addr_list; x != NULL && *x != NULL; x++) { if ((r = *Q) == NULL) r = *Q = new acl_ip_data; xmemcpy(&r->addr1.s_addr, *x, sizeof(r->addr1.s_addr)); r->addr2.s_addr = 0; DecodeMask(NULL, &r->mask); Q = &r->next; debugs(28, 3, "" << addr1 << " --> " << inet_ntoa(r->addr1)); } if (*Q != NULL) { debugs(28, 0, "aclParseIpData: Bad host/IP: '" << t << "'"); self_destruct(); } return q; } /* Decode addr1 */ if (!safe_inet_addr(addr1, &q->addr1)) { debugs(28, 0, "aclParseIpData: unknown first address in '" << t << "'"); delete q; self_destruct(); return NULL; } /* Decode addr2 */ if (*addr2 && !safe_inet_addr(addr2, &q->addr2)) { debugs(28, 0, "aclParseIpData: unknown second address in '" << t << "'"); delete q; self_destruct(); return NULL; } /* Decode mask (NULL or empty means a exact host mask) */ if (!DecodeMask(mask, &q->mask)) { debugs(28, 0, "aclParseIpData: unknown netmask '" << mask << "' in '" << t << "'"); delete q; self_destruct(); return NULL; } if ((q->addr1.s_addr & q->mask.s_addr) != q->addr1.s_addr || (q->addr2.s_addr & q->mask.s_addr) != q->addr2.s_addr) debugs(28, 0, "aclParseIpData: WARNING: Netmask masks away part of the specified IP in '" << t << "'"); q->addr1.s_addr &= q->mask.s_addr; q->addr2.s_addr &= q->mask.s_addr; /* 1.2.3.4/255.255.255.0 --> 1.2.3.0 */ return q; } void ACLIP::parse() { char *t = NULL; while ((t = strtokFile())) { acl_ip_data *q = acl_ip_data::FactoryParse(t); while (q != NULL) { data = data->insert(q, acl_ip_data::NetworkCompare); q = q->next; } } } ACLIP::~ACLIP() { if (data) data->destroy(IPSplay::DefaultFree); } wordlist * ACLIP::dump() const { wordlist *w = NULL; data->walk (DumpIpListWalkee, &w); return w; } bool ACLIP::empty () const { return data->empty(); } int ACLIP::match(struct IN_ADDR &clientip) { static acl_ip_data ClientAddress (any_addr, any_addr, no_addr, NULL); /* * aclIpAddrNetworkCompare() takes two acl_ip_data pointers as * arguments, so we must create a fake one for the client's IP * address, and use a /32 netmask. However, the current code * probably only accesses the addr1 element of this argument, * so it might be possible to leave addr2 and mask unset. */ ClientAddress.addr1 = clientip; acl_ip_data *ClientAddressPointer = &ClientAddress; data = data->splay(ClientAddressPointer, aclIpAddrNetworkCompare); debugs(28, 3, "aclMatchIp: '" << inet_ntoa(clientip) << "' " << (splayLastResult ? "NOT found" : "found")); return !splayLastResult; } acl_ip_data::acl_ip_data () :addr1(any_addr), addr2(any_addr), mask (any_addr), next (NULL) {} acl_ip_data::acl_ip_data (struct IN_ADDR const &anAddress1, struct IN_ADDR const &anAddress2, struct IN_ADDR const &aMask, acl_ip_data *aNext) : addr1(anAddress1), addr2(anAddress2), mask(aMask), next(aNext){}