]>
Commit | Line | Data |
---|---|---|
79d39a72 | 1 | |
8213067d | 2 | /* |
db043f2e | 3 | * $Id: acl.cc,v 1.210 2000/01/05 23:32:17 wessels Exp $ |
30a4f2a8 | 4 | * |
5 | * DEBUG: section 28 Access Control | |
6 | * AUTHOR: Duane Wessels | |
7 | * | |
42c04c16 | 8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ |
e25c139f | 9 | * ---------------------------------------------------------- |
30a4f2a8 | 10 | * |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
e25c139f | 13 | * National Laboratory for Applied Network Research and funded by the |
14 | * National Science Foundation. Squid is Copyrighted (C) 1998 by | |
15 | * Duane Wessels and the University of California San Diego. Please | |
16 | * see the COPYRIGHT file for full details. Squid incorporates | |
17 | * software developed and/or copyrighted by other sources. Please see | |
18 | * the CREDITS file for full details. | |
30a4f2a8 | 19 | * |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
24 | * | |
25 | * This program is distributed in the hope that it will be useful, | |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
29 | * | |
30 | * You should have received a copy of the GNU General Public License | |
31 | * along with this program; if not, write to the Free Software | |
cbdec147 | 32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 33 | * |
8213067d | 34 | */ |
7dd57fa2 | 35 | |
36 | #include "squid.h" | |
f32789f4 | 37 | #include "splay.h" |
f32789f4 | 38 | |
e924600d | 39 | static int aclFromFile = 0; |
40 | static FILE *aclFile; | |
c68e9c6b | 41 | static hash_table *proxy_auth_cache = NULL; |
7dd57fa2 | 42 | |
f084f6af | 43 | static void aclParseDomainList(void *curlist); |
44 | static void aclParseIpList(void *curlist); | |
45 | static void aclParseIntlist(void *curlist); | |
46 | static void aclParseWordList(void *curlist); | |
47 | static void aclParseProtoList(void *curlist); | |
48 | static void aclParseMethodList(void *curlist); | |
49 | static void aclParseTimeSpec(void *curlist); | |
8f663d72 | 50 | static void aclParseIntRange(void *curlist); |
f084f6af | 51 | static char *strtokFile(void); |
7342cdc8 | 52 | static void aclDestroyAclList(acl_list * list); |
53 | static void aclDestroyTimeList(acl_time_data * data); | |
8f663d72 | 54 | static void aclDestroyIntRange(intrange *); |
ec878047 | 55 | static FREE aclFreeProxyAuthUser; |
1cfdbcf0 | 56 | static struct _acl *aclFindByName(const char *name); |
669fefd4 | 57 | static int aclMatchAcl(struct _acl *, aclCheck_t *); |
8f663d72 | 58 | static int aclMatchIntegerRange(intrange * data, int i); |
7342cdc8 | 59 | static int aclMatchTime(acl_time_data * data, time_t when); |
c68e9c6b | 60 | static int aclMatchUser(wordlist * data, const char *ident); |
f5b8bbc4 | 61 | static int aclMatchIp(void *dataptr, struct in_addr c); |
62 | static int aclMatchDomainList(void *dataptr, const char *); | |
8f663d72 | 63 | static int aclMatchIntegerRange(intrange * data, int i); |
9e160ce2 | 64 | #if SQUID_SNMP |
dba79ac5 | 65 | static int aclMatchWordList(wordlist *, const char *); |
9e160ce2 | 66 | #endif |
56b63fa1 | 67 | static squid_acl aclStrToType(const char *s); |
f5b8bbc4 | 68 | static int decode_addr(const char *, struct in_addr *, struct in_addr *); |
69 | static void aclCheck(aclCheck_t * checklist); | |
fc5d6f7f | 70 | static void aclCheckCallback(aclCheck_t * checklist, allow_t answer); |
3898f57f | 71 | #if USE_IDENT |
05832ae1 | 72 | static IDCB aclLookupIdentDone; |
3898f57f | 73 | #endif |
b69f7771 | 74 | static IPH aclLookupDstIPDone; |
53ad48e6 | 75 | static IPH aclLookupDstIPforASNDone; |
348b2031 | 76 | static FQDNH aclLookupSrcFQDNDone; |
77 | static FQDNH aclLookupDstFQDNDone; | |
c68e9c6b | 78 | static void aclLookupProxyAuthStart(aclCheck_t * checklist); |
79 | static void aclLookupProxyAuthDone(void *data, char *result); | |
f084f6af | 80 | static wordlist *aclDumpIpList(void *); |
16300b58 | 81 | static wordlist *aclDumpDomainList(void *data); |
7342cdc8 | 82 | static wordlist *aclDumpTimeSpecList(acl_time_data *); |
83 | static wordlist *aclDumpRegexList(relist * data); | |
84 | static wordlist *aclDumpIntlistList(intlist * data); | |
8f663d72 | 85 | static wordlist *aclDumpIntRangeList(intrange * data); |
7342cdc8 | 86 | static wordlist *aclDumpProtoList(intlist * data); |
87 | static wordlist *aclDumpMethodList(intlist * data); | |
f084f6af | 88 | static SPLAYCMP aclIpNetworkCompare; |
89 | static SPLAYCMP aclHostDomainCompare; | |
90 | static SPLAYCMP aclDomainCompare; | |
91 | static SPLAYWALKEE aclDumpIpListWalkee; | |
92 | static SPLAYWALKEE aclDumpDomainListWalkee; | |
c68e9c6b | 93 | static SPLAYFREE aclFreeIpData; |
f32789f4 | 94 | |
23351cb2 | 95 | #if USE_ARP_ACL |
f084f6af | 96 | static void aclParseArpList(void *curlist); |
23351cb2 | 97 | static int decode_eth(const char *asc, char *eth); |
98 | static int aclMatchArp(void *dataptr, struct in_addr c); | |
bd0c865a | 99 | static wordlist *aclDumpArpList(void *); |
f084f6af | 100 | static SPLAYCMP aclArpCompare; |
bd0c865a | 101 | static SPLAYWALKEE aclDumpArpListWalkee; |
23351cb2 | 102 | #endif |
103 | ||
e924600d | 104 | static char * |
c10aaf05 | 105 | strtokFile(void) |
106 | { | |
107 | char *t, *fn; | |
108 | LOCAL_ARRAY(char, buf, 256); | |
109 | ||
e5f6c5c2 | 110 | strtok_again: |
c10aaf05 | 111 | if (!aclFromFile) { |
112 | t = (strtok(NULL, w_space)); | |
113 | if (t && (*t == '\"' || *t == '\'')) { | |
114 | /* quote found, start reading from file */ | |
115 | fn = ++t; | |
e5f6c5c2 | 116 | while (*t && *t != '\"' && *t != '\'') |
117 | t++; | |
c10aaf05 | 118 | *t = '\0'; |
119 | if ((aclFile = fopen(fn, "r")) == NULL) { | |
a3d5953d | 120 | debug(28, 0) ("strtokFile: %s not found\n", fn); |
e5f6c5c2 | 121 | return (NULL); |
c10aaf05 | 122 | } |
123 | aclFromFile = 1; | |
124 | } else { | |
0153d498 | 125 | return t; |
c10aaf05 | 126 | } |
127 | } | |
128 | /* aclFromFile */ | |
129 | if (fgets(buf, 256, aclFile) == NULL) { | |
130 | /* stop reading from file */ | |
131 | fclose(aclFile); | |
132 | aclFromFile = 0; | |
133 | goto strtok_again; | |
134 | } else { | |
135 | t = buf; | |
136 | /* skip leading and trailing white space */ | |
137 | t += strspn(buf, w_space); | |
138 | t[strcspn(t, w_space)] = '\0'; | |
c68e9c6b | 139 | /* skip comments */ |
140 | if (*t == '#') | |
141 | goto strtok_again; | |
142 | /* skip blank lines */ | |
143 | if (!*t) | |
144 | goto strtok_again; | |
0153d498 | 145 | return t; |
c10aaf05 | 146 | } |
147 | } | |
148 | ||
8203a132 | 149 | static squid_acl |
56b63fa1 | 150 | aclStrToType(const char *s) |
7dd57fa2 | 151 | { |
152 | if (!strcmp(s, "src")) | |
153 | return ACL_SRC_IP; | |
30a4f2a8 | 154 | if (!strcmp(s, "dst")) |
155 | return ACL_DST_IP; | |
ae2c08a2 | 156 | if (!strcmp(s, "myip")) |
157 | return ACL_MY_IP; | |
7dd57fa2 | 158 | if (!strcmp(s, "domain")) |
159 | return ACL_DST_DOMAIN; | |
f88bb09c | 160 | if (!strcmp(s, "dstdomain")) |
161 | return ACL_DST_DOMAIN; | |
162 | if (!strcmp(s, "srcdomain")) | |
163 | return ACL_SRC_DOMAIN; | |
d0d41f07 | 164 | if (!strcmp(s, "dstdom_regex")) |
165 | return ACL_DST_DOM_REGEX; | |
166 | if (!strcmp(s, "srcdom_regex")) | |
167 | return ACL_SRC_DOM_REGEX; | |
7dd57fa2 | 168 | if (!strcmp(s, "time")) |
169 | return ACL_TIME; | |
170 | if (!strcmp(s, "pattern")) | |
6e40f263 | 171 | return ACL_URLPATH_REGEX; |
172 | if (!strcmp(s, "urlpath_regex")) | |
173 | return ACL_URLPATH_REGEX; | |
174 | if (!strcmp(s, "url_regex")) | |
7dd57fa2 | 175 | return ACL_URL_REGEX; |
176 | if (!strcmp(s, "port")) | |
177 | return ACL_URL_PORT; | |
7e3ce7b9 | 178 | if (!strcmp(s, "myport")) |
179 | return ACL_MY_PORT; | |
9bc73deb | 180 | if (!strcmp(s, "maxconn")) |
181 | return ACL_MAXCONN; | |
3898f57f | 182 | #if USE_IDENT |
c68e9c6b | 183 | if (!strcmp(s, "ident")) |
184 | return ACL_IDENT; | |
3898f57f | 185 | #endif |
30a4f2a8 | 186 | if (!strncmp(s, "proto", 5)) |
7dd57fa2 | 187 | return ACL_PROTO; |
92a6f4b1 | 188 | if (!strcmp(s, "method")) |
189 | return ACL_METHOD; | |
d3416189 | 190 | if (!strcmp(s, "browser")) |
191 | return ACL_BROWSER; | |
afe95a7e | 192 | if (!strcmp(s, "proxy_auth")) |
193 | return ACL_PROXY_AUTH; | |
5d6c7aad | 194 | if (!strcmp(s, "src_as")) |
195 | return ACL_SRC_ASN; | |
196 | if (!strcmp(s, "dst_as")) | |
197 | return ACL_DST_ASN; | |
dba79ac5 | 198 | #if SQUID_SNMP |
43d4303e | 199 | if (!strcmp(s, "snmp_community")) |
dba79ac5 | 200 | return ACL_SNMP_COMMUNITY; |
201 | #endif | |
bd05e3e3 | 202 | if (!strcmp(s, "src_rtt")) |
203 | return ACL_NETDB_SRC_RTT; | |
66c75c41 | 204 | #if USE_ARP_ACL |
205 | if (!strcmp(s, "arp")) | |
206 | return ACL_SRC_ARP; | |
207 | #endif | |
7dd57fa2 | 208 | return ACL_NONE; |
209 | } | |
210 | ||
56b63fa1 | 211 | const char * |
212 | aclTypeToStr(squid_acl type) | |
213 | { | |
214 | if (type == ACL_SRC_IP) | |
215 | return "src"; | |
216 | if (type == ACL_DST_IP) | |
217 | return "dst"; | |
ae2c08a2 | 218 | if (type == ACL_MY_IP) |
219 | return "myip"; | |
56b63fa1 | 220 | if (type == ACL_DST_DOMAIN) |
221 | return "dstdomain"; | |
222 | if (type == ACL_SRC_DOMAIN) | |
223 | return "srcdomain"; | |
d0d41f07 | 224 | if (type == ACL_DST_DOM_REGEX) |
225 | return "dstdom_regex"; | |
226 | if (type == ACL_SRC_DOM_REGEX) | |
227 | return "srcdom_regex"; | |
56b63fa1 | 228 | if (type == ACL_TIME) |
229 | return "time"; | |
230 | if (type == ACL_URLPATH_REGEX) | |
231 | return "urlpath_regex"; | |
232 | if (type == ACL_URL_REGEX) | |
233 | return "url_regex"; | |
234 | if (type == ACL_URL_PORT) | |
235 | return "port"; | |
7e3ce7b9 | 236 | if (type == ACL_MY_PORT) |
237 | return "myport"; | |
9bc73deb | 238 | if (type == ACL_MAXCONN) |
239 | return "maxconn"; | |
3898f57f | 240 | #if USE_IDENT |
c68e9c6b | 241 | if (type == ACL_IDENT) |
242 | return "ident"; | |
3898f57f | 243 | #endif |
56b63fa1 | 244 | if (type == ACL_PROTO) |
245 | return "proto"; | |
246 | if (type == ACL_METHOD) | |
247 | return "method"; | |
248 | if (type == ACL_BROWSER) | |
249 | return "browser"; | |
250 | if (type == ACL_PROXY_AUTH) | |
251 | return "proxy_auth"; | |
252 | if (type == ACL_SRC_ASN) | |
253 | return "src_as"; | |
254 | if (type == ACL_DST_ASN) | |
255 | return "dst_as"; | |
dba79ac5 | 256 | #if SQUID_SNMP |
257 | if (type == ACL_SNMP_COMMUNITY) | |
43d4303e | 258 | return "snmp_community"; |
dba79ac5 | 259 | #endif |
bd05e3e3 | 260 | if (type == ACL_NETDB_SRC_RTT) |
261 | return "src_rtt"; | |
56b63fa1 | 262 | #if USE_ARP_ACL |
263 | if (type == ACL_SRC_ARP) | |
264 | return "arp"; | |
265 | #endif | |
266 | return "ERROR"; | |
267 | } | |
268 | ||
1cfdbcf0 | 269 | static acl * |
0ee4272b | 270 | aclFindByName(const char *name) |
7dd57fa2 | 271 | { |
7342cdc8 | 272 | acl *a; |
f1dc9b30 | 273 | for (a = Config.aclList; a; a = a->next) |
1969665d | 274 | if (!strcasecmp(a->name, name)) |
275 | return a; | |
276 | return NULL; | |
7dd57fa2 | 277 | } |
278 | ||
fda94b88 | 279 | static void |
280 | aclParseIntlist(void *curlist) | |
7dd57fa2 | 281 | { |
fda94b88 | 282 | intlist **Tail; |
7dd57fa2 | 283 | intlist *q = NULL; |
284 | char *t = NULL; | |
fda94b88 | 285 | for (Tail = curlist; *Tail; Tail = &((*Tail)->next)); |
c10aaf05 | 286 | while ((t = strtokFile())) { |
c68e9c6b | 287 | q = memAllocate(MEM_INTLIST); |
7dd57fa2 | 288 | q->i = atoi(t); |
289 | *(Tail) = q; | |
290 | Tail = &q->next; | |
291 | } | |
7dd57fa2 | 292 | } |
293 | ||
8f663d72 | 294 | static void |
295 | aclParseIntRange(void *curlist) | |
296 | { | |
297 | intrange **Tail; | |
298 | intrange *q = NULL; | |
299 | char *t = NULL; | |
300 | for (Tail = curlist; *Tail; Tail = &((*Tail)->next)); | |
301 | while ((t = strtokFile())) { | |
302 | q = xcalloc(1, sizeof(intrange)); | |
303 | q->i = atoi(t); | |
304 | t = strchr(t, '-'); | |
305 | if (t && *(++t)) | |
5942e8d4 | 306 | q->j = atoi(t); |
8f663d72 | 307 | else |
5942e8d4 | 308 | q->j = q->i; |
8f663d72 | 309 | *(Tail) = q; |
310 | Tail = &q->next; | |
311 | } | |
312 | } | |
313 | ||
fda94b88 | 314 | static void |
315 | aclParseProtoList(void *curlist) | |
7dd57fa2 | 316 | { |
fda94b88 | 317 | intlist **Tail; |
7dd57fa2 | 318 | intlist *q = NULL; |
319 | char *t = NULL; | |
fda94b88 | 320 | for (Tail = curlist; *Tail; Tail = &((*Tail)->next)); |
c10aaf05 | 321 | while ((t = strtokFile())) { |
c68e9c6b | 322 | q = memAllocate(MEM_INTLIST); |
92a6f4b1 | 323 | q->i = (int) urlParseProtocol(t); |
1969665d | 324 | *(Tail) = q; |
325 | Tail = &q->next; | |
7dd57fa2 | 326 | } |
7dd57fa2 | 327 | } |
92a6f4b1 | 328 | |
fda94b88 | 329 | static void |
330 | aclParseMethodList(void *curlist) | |
92a6f4b1 | 331 | { |
fda94b88 | 332 | intlist **Tail; |
92a6f4b1 | 333 | intlist *q = NULL; |
334 | char *t = NULL; | |
fda94b88 | 335 | for (Tail = curlist; *Tail; Tail = &((*Tail)->next)); |
c10aaf05 | 336 | while ((t = strtokFile())) { |
c68e9c6b | 337 | q = memAllocate(MEM_INTLIST); |
92a6f4b1 | 338 | q->i = (int) urlParseMethod(t); |
a90eae18 | 339 | if (q->i == METHOD_PURGE) |
17a0a4ee | 340 | Config.onoff.enable_purge = 1; |
92a6f4b1 | 341 | *(Tail) = q; |
342 | Tail = &q->next; | |
343 | } | |
92a6f4b1 | 344 | } |
345 | ||
06e6d12a | 346 | /* |
347 | * Decode a ascii representation (asc) of a IP adress, and place | |
30a4f2a8 | 348 | * adress and netmask information in addr and mask. |
06e6d12a | 349 | * This function should NOT be called if 'asc' is a hostname! |
30a4f2a8 | 350 | */ |
8203a132 | 351 | static int |
0ee4272b | 352 | decode_addr(const char *asc, struct in_addr *addr, struct in_addr *mask) |
7dd57fa2 | 353 | { |
429fdbec | 354 | u_num32 a; |
ef2d27ff | 355 | int a1 = 0, a2 = 0, a3 = 0, a4 = 0; |
30a4f2a8 | 356 | |
357 | switch (sscanf(asc, "%d.%d.%d.%d", &a1, &a2, &a3, &a4)) { | |
358 | case 4: /* a dotted quad */ | |
429fdbec | 359 | if (!safe_inet_addr(asc, addr)) { |
a3d5953d | 360 | debug(28, 0) ("decode_addr: unsafe IP address: '%s'\n", asc); |
429fdbec | 361 | fatal("decode_addr: unsafe IP address"); |
30a4f2a8 | 362 | } |
363 | break; | |
364 | case 1: /* a significant bits value for a mask */ | |
365 | if (a1 >= 0 && a1 < 33) { | |
ca98227c | 366 | addr->s_addr = a1 ? htonl(0xfffffffful << (32 - a1)) : 0; |
30a4f2a8 | 367 | break; |
368 | } | |
369 | default: | |
06e6d12a | 370 | debug(28, 0) ("decode_addr: Invalid IP address '%s'\n", asc); |
371 | return 0; /* This is not valid address */ | |
30a4f2a8 | 372 | } |
373 | ||
374 | if (mask != NULL) { /* mask == NULL if called to decode a netmask */ | |
375 | ||
376 | /* Guess netmask */ | |
ff8d0ea6 | 377 | a = (u_num32) ntohl(addr->s_addr); |
86ee2017 | 378 | if (!(a & 0xFFFFFFFFul)) |
379 | mask->s_addr = htonl(0x00000000ul); | |
30a4f2a8 | 380 | else if (!(a & 0x00FFFFFF)) |
86ee2017 | 381 | mask->s_addr = htonl(0xFF000000ul); |
30a4f2a8 | 382 | else if (!(a & 0x0000FFFF)) |
86ee2017 | 383 | mask->s_addr = htonl(0xFFFF0000ul); |
30a4f2a8 | 384 | else if (!(a & 0x000000FF)) |
86ee2017 | 385 | mask->s_addr = htonl(0xFFFFFF00ul); |
30a4f2a8 | 386 | else |
86ee2017 | 387 | mask->s_addr = htonl(0xFFFFFFFFul); |
30a4f2a8 | 388 | } |
389 | return 1; | |
390 | } | |
391 | ||
75e5a32e | 392 | |
393 | #define SCAN_ACL1 "%[0123456789.]-%[0123456789.]/%[0123456789.]" | |
394 | #define SCAN_ACL2 "%[0123456789.]-%[0123456789.]" | |
395 | #define SCAN_ACL3 "%[0123456789.]/%[0123456789.]" | |
396 | #define SCAN_ACL4 "%[0123456789.]" | |
397 | ||
56b63fa1 | 398 | static acl_ip_data * |
dfc52ac5 | 399 | aclParseIpData(const char *t) |
400 | { | |
401 | LOCAL_ARRAY(char, addr1, 256); | |
402 | LOCAL_ARRAY(char, addr2, 256); | |
403 | LOCAL_ARRAY(char, mask, 256); | |
c68e9c6b | 404 | acl_ip_data *q = memAllocate(MEM_ACL_IP_DATA); |
06e6d12a | 405 | acl_ip_data *r; |
406 | acl_ip_data **Q; | |
407 | struct hostent *hp; | |
408 | char **x; | |
a3d5953d | 409 | debug(28, 5) ("aclParseIpData: %s\n", t); |
dfc52ac5 | 410 | if (!strcasecmp(t, "all")) { |
411 | q->addr1.s_addr = 0; | |
412 | q->addr2.s_addr = 0; | |
413 | q->mask.s_addr = 0; | |
414 | return q; | |
415 | } | |
75e5a32e | 416 | if (sscanf(t, SCAN_ACL1, addr1, addr2, mask) == 3) { |
dfc52ac5 | 417 | (void) 0; |
75e5a32e | 418 | } else if (sscanf(t, SCAN_ACL2, addr1, addr2) == 2) { |
dfc52ac5 | 419 | mask[0] = '\0'; |
75e5a32e | 420 | } else if (sscanf(t, SCAN_ACL3, addr1, mask) == 2) { |
dfc52ac5 | 421 | addr2[0] = '\0'; |
75e5a32e | 422 | } else if (sscanf(t, SCAN_ACL4, addr1) == 1) { |
dfc52ac5 | 423 | addr2[0] = '\0'; |
424 | mask[0] = '\0'; | |
425 | } else if (sscanf(t, "%[^/]/%s", addr1, mask) == 2) { | |
426 | addr2[0] = '\0'; | |
427 | } else if (sscanf(t, "%s", addr1) == 1) { | |
06e6d12a | 428 | /* |
429 | * Note, must use plain gethostbyname() here because at startup | |
430 | * ipcache hasn't been initialized | |
431 | */ | |
432 | if ((hp = gethostbyname(addr1)) == NULL) { | |
433 | debug(28, 0) ("aclParseIpData: Bad host/IP: '%s'\n", t); | |
434 | safe_free(q); | |
435 | return NULL; | |
436 | } | |
437 | Q = &q; | |
438 | for (x = hp->h_addr_list; x != NULL && *x != NULL; x++) { | |
439 | if ((r = *Q) == NULL) | |
c68e9c6b | 440 | r = *Q = memAllocate(MEM_ACL_IP_DATA); |
06e6d12a | 441 | xmemcpy(&r->addr1.s_addr, *x, sizeof(r->addr1.s_addr)); |
442 | r->addr2.s_addr = 0; | |
e711a2ff | 443 | r->mask.s_addr = no_addr.s_addr; /* 255.255.255.255 */ |
06e6d12a | 444 | Q = &r->next; |
445 | debug(28, 3) ("%s --> %s\n", addr1, inet_ntoa(r->addr1)); | |
446 | } | |
447 | return q; | |
dfc52ac5 | 448 | } else { |
a3d5953d | 449 | debug(28, 0) ("aclParseIpData: Bad host/IP: '%s'\n", t); |
dfc52ac5 | 450 | safe_free(q); |
451 | return NULL; | |
452 | } | |
453 | /* Decode addr1 */ | |
454 | if (!decode_addr(addr1, &q->addr1, &q->mask)) { | |
a3d5953d | 455 | debug(28, 0) ("%s line %d: %s\n", |
dfc52ac5 | 456 | cfg_filename, config_lineno, config_input_line); |
a3d5953d | 457 | debug(28, 0) ("aclParseIpData: Ignoring invalid IP acl entry: unknown first address '%s'\n", addr1); |
dfc52ac5 | 458 | safe_free(q); |
459 | return NULL; | |
460 | } | |
461 | /* Decode addr2 */ | |
462 | if (*addr2 && !decode_addr(addr2, &q->addr2, &q->mask)) { | |
a3d5953d | 463 | debug(28, 0) ("%s line %d: %s\n", |
dfc52ac5 | 464 | cfg_filename, config_lineno, config_input_line); |
a3d5953d | 465 | debug(28, 0) ("aclParseIpData: Ignoring invalid IP acl entry: unknown second address '%s'\n", addr2); |
dfc52ac5 | 466 | safe_free(q); |
467 | return NULL; | |
468 | } | |
469 | /* Decode mask */ | |
470 | if (*mask && !decode_addr(mask, &q->mask, NULL)) { | |
a3d5953d | 471 | debug(28, 0) ("%s line %d: %s\n", |
dfc52ac5 | 472 | cfg_filename, config_lineno, config_input_line); |
a3d5953d | 473 | debug(28, 0) ("aclParseIpData: Ignoring invalid IP acl entry: unknown netmask '%s'\n", mask); |
dfc52ac5 | 474 | safe_free(q); |
475 | return NULL; | |
476 | } | |
477 | q->addr1.s_addr &= q->mask.s_addr; | |
478 | q->addr2.s_addr &= q->mask.s_addr; | |
479 | /* 1.2.3.4/255.255.255.0 --> 1.2.3.0 */ | |
480 | return q; | |
481 | } | |
30a4f2a8 | 482 | |
f32789f4 | 483 | /******************/ |
484 | /* aclParseIpList */ | |
485 | /******************/ | |
8bad0cc5 | 486 | |
fda94b88 | 487 | static void |
488 | aclParseIpList(void *curlist) | |
30a4f2a8 | 489 | { |
cb0486c3 | 490 | char *t = NULL; |
f32789f4 | 491 | splayNode **Top = curlist; |
56b63fa1 | 492 | acl_ip_data *q = NULL; |
c10aaf05 | 493 | while ((t = strtokFile())) { |
06e6d12a | 494 | q = aclParseIpData(t); |
495 | while (q != NULL) { | |
496 | *Top = splay_insert(q, *Top, aclIpNetworkCompare); | |
497 | q = q->next; | |
498 | } | |
b1ef1220 | 499 | } |
8bad0cc5 | 500 | } |
501 | ||
fda94b88 | 502 | static void |
503 | aclParseTimeSpec(void *curlist) | |
7dd57fa2 | 504 | { |
7342cdc8 | 505 | acl_time_data *q = NULL; |
506 | acl_time_data **Tail; | |
92a6f4b1 | 507 | int h1, m1, h2, m2; |
508 | char *t = NULL; | |
fda94b88 | 509 | for (Tail = curlist; *Tail; Tail = &((*Tail)->next)); |
c68e9c6b | 510 | q = memAllocate(MEM_ACL_TIME_DATA); |
c10aaf05 | 511 | while ((t = strtokFile())) { |
92a6f4b1 | 512 | if (*t < '0' || *t > '9') { |
513 | /* assume its day-of-week spec */ | |
514 | while (*t) { | |
515 | switch (*t++) { | |
516 | case 'S': | |
fda94b88 | 517 | q->weekbits |= ACL_SUNDAY; |
92a6f4b1 | 518 | break; |
519 | case 'M': | |
fda94b88 | 520 | q->weekbits |= ACL_MONDAY; |
92a6f4b1 | 521 | break; |
522 | case 'T': | |
fda94b88 | 523 | q->weekbits |= ACL_TUESDAY; |
92a6f4b1 | 524 | break; |
525 | case 'W': | |
fda94b88 | 526 | q->weekbits |= ACL_WEDNESDAY; |
92a6f4b1 | 527 | break; |
528 | case 'H': | |
fda94b88 | 529 | q->weekbits |= ACL_THURSDAY; |
92a6f4b1 | 530 | break; |
531 | case 'F': | |
fda94b88 | 532 | q->weekbits |= ACL_FRIDAY; |
92a6f4b1 | 533 | break; |
534 | case 'A': | |
fda94b88 | 535 | q->weekbits |= ACL_SATURDAY; |
92a6f4b1 | 536 | break; |
30a4f2a8 | 537 | case 'D': |
fda94b88 | 538 | q->weekbits |= ACL_WEEKDAYS; |
30a4f2a8 | 539 | break; |
80b4b4d0 | 540 | case '-': |
541 | /* ignore placeholder */ | |
542 | break; | |
92a6f4b1 | 543 | default: |
a3d5953d | 544 | debug(28, 0) ("%s line %d: %s\n", |
b8de7ebe | 545 | cfg_filename, config_lineno, config_input_line); |
a3d5953d | 546 | debug(28, 0) ("aclParseTimeSpec: Bad Day '%c'\n", |
92a6f4b1 | 547 | *t); |
548 | break; | |
549 | } | |
550 | } | |
551 | } else { | |
552 | /* assume its time-of-day spec */ | |
553 | if (sscanf(t, "%d:%d-%d:%d", &h1, &m1, &h2, &m2) < 4) { | |
a3d5953d | 554 | debug(28, 0) ("%s line %d: %s\n", |
b8de7ebe | 555 | cfg_filename, config_lineno, config_input_line); |
a3d5953d | 556 | debug(28, 0) ("aclParseTimeSpec: IGNORING Bad time range\n"); |
fda94b88 | 557 | xfree(q); |
558 | return; | |
92a6f4b1 | 559 | } |
fda94b88 | 560 | q->start = h1 * 60 + m1; |
561 | q->stop = h2 * 60 + m2; | |
562 | if (q->start > q->stop) { | |
a3d5953d | 563 | debug(28, 0) ("%s line %d: %s\n", |
b8de7ebe | 564 | cfg_filename, config_lineno, config_input_line); |
a3d5953d | 565 | debug(28, 0) ("aclParseTimeSpec: IGNORING Reversed time range\n"); |
fda94b88 | 566 | xfree(q); |
567 | return; | |
92a6f4b1 | 568 | } |
569 | } | |
570 | } | |
fda94b88 | 571 | if (q->start == 0 && q->stop == 0) |
572 | q->stop = 23 * 60 + 59; | |
573 | if (q->weekbits == 0) | |
574 | q->weekbits = ACL_ALLWEEK; | |
575 | *(Tail) = q; | |
576 | Tail = &q->next; | |
7dd57fa2 | 577 | } |
578 | ||
fda94b88 | 579 | void |
0153d498 | 580 | aclParseRegexList(void *curlist) |
7dd57fa2 | 581 | { |
fda94b88 | 582 | relist **Tail; |
7dd57fa2 | 583 | relist *q = NULL; |
584 | char *t = NULL; | |
1969665d | 585 | regex_t comp; |
008fcc7b | 586 | int errcode; |
587 | int flags = REG_EXTENDED | REG_NOSUB; | |
fda94b88 | 588 | for (Tail = curlist; *Tail; Tail = &((*Tail)->next)); |
c10aaf05 | 589 | while ((t = strtokFile())) { |
0153d498 | 590 | if (strcmp(t, "-i") == 0) { |
a47b9029 | 591 | flags |= REG_ICASE; |
592 | continue; | |
0153d498 | 593 | } |
594 | if (strcmp(t, "+i") == 0) { | |
a47b9029 | 595 | flags &= ~REG_ICASE; |
596 | continue; | |
0153d498 | 597 | } |
008fcc7b | 598 | if ((errcode = regcomp(&comp, t, flags)) != 0) { |
599 | char errbuf[256]; | |
600 | regerror(errcode, &comp, errbuf, sizeof errbuf); | |
a3d5953d | 601 | debug(28, 0) ("%s line %d: %s\n", |
b8de7ebe | 602 | cfg_filename, config_lineno, config_input_line); |
a3d5953d | 603 | debug(28, 0) ("aclParseRegexList: Invalid regular expression '%s': %s\n", |
008fcc7b | 604 | t, errbuf); |
1969665d | 605 | continue; |
606 | } | |
c68e9c6b | 607 | q = memAllocate(MEM_RELIST); |
1969665d | 608 | q->pattern = xstrdup(t); |
609 | q->regex = comp; | |
610 | *(Tail) = q; | |
611 | Tail = &q->next; | |
7dd57fa2 | 612 | } |
7dd57fa2 | 613 | } |
614 | ||
fda94b88 | 615 | static void |
616 | aclParseWordList(void *curlist) | |
7dd57fa2 | 617 | { |
7dd57fa2 | 618 | char *t = NULL; |
c68e9c6b | 619 | while ((t = strtokFile())) |
620 | wordlistAdd(curlist, t); | |
7dd57fa2 | 621 | } |
622 | ||
f32789f4 | 623 | /**********************/ |
624 | /* aclParseDomainList */ | |
625 | /**********************/ | |
fda94b88 | 626 | |
f32789f4 | 627 | static void |
628 | aclParseDomainList(void *curlist) | |
629 | { | |
630 | char *t = NULL; | |
631 | splayNode **Top = curlist; | |
632 | while ((t = strtokFile())) { | |
633 | Tolower(t); | |
634 | *Top = splay_insert(xstrdup(t), *Top, aclDomainCompare); | |
635 | } | |
636 | } | |
637 | ||
8203a132 | 638 | void |
a47b9029 | 639 | aclParseAclLine(acl ** head) |
7dd57fa2 | 640 | { |
641 | /* we're already using strtok() to grok the line */ | |
642 | char *t = NULL; | |
7342cdc8 | 643 | acl *A = NULL; |
fda94b88 | 644 | LOCAL_ARRAY(char, aclname, ACL_NAME_SZ); |
645 | squid_acl acltype; | |
e924600d | 646 | int new_acl = 0; |
7dd57fa2 | 647 | |
7dd57fa2 | 648 | /* snarf the ACL name */ |
649 | if ((t = strtok(NULL, w_space)) == NULL) { | |
a3d5953d | 650 | debug(28, 0) ("%s line %d: %s\n", |
b8de7ebe | 651 | cfg_filename, config_lineno, config_input_line); |
a3d5953d | 652 | debug(28, 0) ("aclParseAclLine: missing ACL name.\n"); |
92a6f4b1 | 653 | return; |
654 | } | |
fda94b88 | 655 | xstrncpy(aclname, t, ACL_NAME_SZ); |
656 | /* snarf the ACL type */ | |
657 | if ((t = strtok(NULL, w_space)) == NULL) { | |
a3d5953d | 658 | debug(28, 0) ("%s line %d: %s\n", |
b8de7ebe | 659 | cfg_filename, config_lineno, config_input_line); |
a3d5953d | 660 | debug(28, 0) ("aclParseAclLine: missing ACL type.\n"); |
7dd57fa2 | 661 | return; |
662 | } | |
56b63fa1 | 663 | if ((acltype = aclStrToType(t)) == ACL_NONE) { |
a3d5953d | 664 | debug(28, 0) ("%s line %d: %s\n", |
b8de7ebe | 665 | cfg_filename, config_lineno, config_input_line); |
a3d5953d | 666 | debug(28, 0) ("aclParseAclLine: Invalid ACL type '%s'\n", t); |
7dd57fa2 | 667 | return; |
668 | } | |
9963e5a3 | 669 | if ((A = aclFindByName(aclname)) == NULL) { |
a3d5953d | 670 | debug(28, 3) ("aclParseAclLine: Creating ACL '%s'\n", aclname); |
c68e9c6b | 671 | A = memAllocate(MEM_ACL); |
fda94b88 | 672 | xstrncpy(A->name, aclname, ACL_NAME_SZ); |
673 | A->type = acltype; | |
52cd89fd | 674 | A->cfgline = xstrdup(config_input_line); |
e924600d | 675 | new_acl = 1; |
fda94b88 | 676 | } else { |
52cd89fd | 677 | if (acltype != A->type) { |
a3d5953d | 678 | debug(28, 0) ("aclParseAclLine: ACL '%s' already exists with different type, skipping.\n", A->name); |
fda94b88 | 679 | return; |
680 | } | |
a3d5953d | 681 | debug(28, 3) ("aclParseAclLine: Appending to '%s'\n", aclname); |
e924600d | 682 | new_acl = 0; |
fda94b88 | 683 | } |
b6a2f15e | 684 | /* |
685 | * Here we set AclMatchedName in case we need to use it in a | |
686 | * warning message in aclDomainCompare(). | |
687 | */ | |
688 | AclMatchedName = aclname; /* ugly */ | |
fda94b88 | 689 | switch (A->type) { |
7dd57fa2 | 690 | case ACL_SRC_IP: |
30a4f2a8 | 691 | case ACL_DST_IP: |
ae2c08a2 | 692 | case ACL_MY_IP: |
f3ae9e0d | 693 | aclParseIpList(&A->data); |
7dd57fa2 | 694 | break; |
f88bb09c | 695 | case ACL_SRC_DOMAIN: |
7dd57fa2 | 696 | case ACL_DST_DOMAIN: |
fda94b88 | 697 | aclParseDomainList(&A->data); |
7dd57fa2 | 698 | break; |
699 | case ACL_TIME: | |
fda94b88 | 700 | aclParseTimeSpec(&A->data); |
7dd57fa2 | 701 | break; |
702 | case ACL_URL_REGEX: | |
6e40f263 | 703 | case ACL_URLPATH_REGEX: |
6b8e7481 | 704 | case ACL_BROWSER: |
b347fea3 | 705 | case ACL_SRC_DOM_REGEX: |
706 | case ACL_DST_DOM_REGEX: | |
f1dc9b30 | 707 | aclParseRegexList(&A->data); |
7dd57fa2 | 708 | break; |
9fdbeb4f | 709 | case ACL_SRC_ASN: |
9bc73deb | 710 | case ACL_MAXCONN: |
9fdbeb4f | 711 | case ACL_DST_ASN: |
bd05e3e3 | 712 | case ACL_NETDB_SRC_RTT: |
fda94b88 | 713 | aclParseIntlist(&A->data); |
7dd57fa2 | 714 | break; |
8f663d72 | 715 | case ACL_URL_PORT: |
7e3ce7b9 | 716 | case ACL_MY_PORT: |
8f663d72 | 717 | aclParseIntRange(&A->data); |
718 | break; | |
3898f57f | 719 | #if USE_IDENT |
c68e9c6b | 720 | case ACL_IDENT: |
fda94b88 | 721 | aclParseWordList(&A->data); |
7dd57fa2 | 722 | break; |
3898f57f | 723 | #endif |
7dd57fa2 | 724 | case ACL_PROTO: |
fda94b88 | 725 | aclParseProtoList(&A->data); |
7dd57fa2 | 726 | break; |
92a6f4b1 | 727 | case ACL_METHOD: |
fda94b88 | 728 | aclParseMethodList(&A->data); |
92a6f4b1 | 729 | break; |
afe95a7e | 730 | case ACL_PROXY_AUTH: |
dba79ac5 | 731 | aclParseWordList(&A->data); |
c68e9c6b | 732 | if (!proxy_auth_cache) { |
733 | /* First time around, 7921 should be big enough */ | |
734 | proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string); | |
735 | assert(proxy_auth_cache); | |
736 | } | |
43d4303e | 737 | break; |
8c37f54d | 738 | #if SQUID_SNMP |
739 | case ACL_SNMP_COMMUNITY: | |
227e68b6 | 740 | aclParseWordList(&A->data); |
8c37f54d | 741 | break; |
227e68b6 | 742 | #endif |
66c75c41 | 743 | #if USE_ARP_ACL |
744 | case ACL_SRC_ARP: | |
745 | aclParseArpList(&A->data); | |
746 | break; | |
747 | #endif | |
7dd57fa2 | 748 | case ACL_NONE: |
749 | default: | |
5d6c7aad | 750 | fatal("Bad ACL type"); |
fda94b88 | 751 | break; |
7dd57fa2 | 752 | } |
b6a2f15e | 753 | /* |
754 | * Clear AclMatchedName from our temporary hack | |
6b8e7481 | 755 | */ |
b6a2f15e | 756 | AclMatchedName = NULL; /* ugly */ |
e924600d | 757 | if (!new_acl) |
758 | return; | |
759 | if (A->data == NULL) { | |
a3d5953d | 760 | debug(28, 0) ("aclParseAclLine: IGNORING invalid ACL: %s\n", |
e924600d | 761 | A->cfgline); |
762 | xfree(A); | |
763 | return; | |
764 | } | |
f1dc9b30 | 765 | /* append */ |
766 | while (*head) | |
767 | head = &(*head)->next; | |
768 | *head = A; | |
7dd57fa2 | 769 | } |
770 | ||
02922e76 | 771 | /* does name lookup, returns page_id */ |
772 | int | |
773 | aclGetDenyInfoPage(acl_deny_info_list ** head, const char *name) | |
e92d33a5 | 774 | { |
7342cdc8 | 775 | acl_deny_info_list *A = NULL; |
776 | acl_name_list *L = NULL; | |
e92d33a5 | 777 | |
778 | A = *head; | |
779 | if (NULL == *head) /* empty list */ | |
02922e76 | 780 | return -1; |
e92d33a5 | 781 | while (A) { |
782 | L = A->acl_list; | |
783 | if (NULL == L) /* empty list should never happen, but in case */ | |
784 | continue; | |
785 | while (L) { | |
786 | if (!strcmp(name, L->name)) | |
02922e76 | 787 | return A->err_page_id; |
e92d33a5 | 788 | L = L->next; |
789 | } | |
790 | A = A->next; | |
791 | } | |
02922e76 | 792 | return -1; |
e92d33a5 | 793 | } |
f32789f4 | 794 | |
1cfdbcf0 | 795 | /* does name lookup, returns if it is a proxy_auth acl */ |
796 | int | |
797 | aclIsProxyAuth(const char *name) | |
798 | { | |
799 | acl *a = aclFindByName(name); | |
227e68b6 | 800 | if (a) |
1cfdbcf0 | 801 | return a->type == ACL_PROXY_AUTH; |
802 | return 0; | |
803 | } | |
804 | ||
805 | ||
e92d33a5 | 806 | /* maex@space.net (05.09.96) |
5e79098a | 807 | * get the info for redirecting "access denied" to info pages |
808 | * TODO (probably ;-) | |
809 | * currently there is no optimization for | |
810 | * - more than one deny_info line with the same url | |
811 | * - a check, whether the given acl really is defined | |
812 | * - a check, whether an acl is added more than once for the same url | |
e92d33a5 | 813 | */ |
f32789f4 | 814 | |
8203a132 | 815 | void |
7342cdc8 | 816 | aclParseDenyInfoLine(acl_deny_info_list ** head) |
e92d33a5 | 817 | { |
818 | char *t = NULL; | |
7342cdc8 | 819 | acl_deny_info_list *A = NULL; |
820 | acl_deny_info_list *B = NULL; | |
821 | acl_deny_info_list **T = NULL; | |
822 | acl_name_list *L = NULL; | |
823 | acl_name_list **Tail = NULL; | |
e92d33a5 | 824 | |
02922e76 | 825 | /* first expect a page name */ |
e92d33a5 | 826 | if ((t = strtok(NULL, w_space)) == NULL) { |
a3d5953d | 827 | debug(28, 0) ("%s line %d: %s\n", |
e92d33a5 | 828 | cfg_filename, config_lineno, config_input_line); |
02922e76 | 829 | debug(28, 0) ("aclParseDenyInfoLine: missing 'error page' parameter.\n"); |
e92d33a5 | 830 | return; |
831 | } | |
7342cdc8 | 832 | A = xcalloc(1, sizeof(acl_deny_info_list)); |
02922e76 | 833 | A->err_page_id = errorReservePageId(t); |
02922e76 | 834 | A->err_page_name = xstrdup(t); |
7342cdc8 | 835 | A->next = (acl_deny_info_list *) NULL; |
e92d33a5 | 836 | /* next expect a list of ACL names */ |
837 | Tail = &A->acl_list; | |
838 | while ((t = strtok(NULL, w_space))) { | |
7342cdc8 | 839 | L = xcalloc(1, sizeof(acl_name_list)); |
d5aa0e3b | 840 | xstrncpy(L->name, t, ACL_NAME_SZ); |
e92d33a5 | 841 | *Tail = L; |
842 | Tail = &L->next; | |
843 | } | |
844 | if (A->acl_list == NULL) { | |
a3d5953d | 845 | debug(28, 0) ("%s line %d: %s\n", |
e92d33a5 | 846 | cfg_filename, config_lineno, config_input_line); |
a3d5953d | 847 | debug(28, 0) ("aclParseDenyInfoLine: deny_info line contains no ACL's, skipping\n"); |
e92d33a5 | 848 | xfree(A); |
849 | return; | |
850 | } | |
851 | for (B = *head, T = head; B; T = &B->next, B = B->next); /* find the tail */ | |
852 | *T = A; | |
853 | } | |
854 | ||
8203a132 | 855 | void |
7342cdc8 | 856 | aclParseAccessLine(acl_access ** head) |
7dd57fa2 | 857 | { |
858 | char *t = NULL; | |
7342cdc8 | 859 | acl_access *A = NULL; |
860 | acl_access *B = NULL; | |
861 | acl_access **T = NULL; | |
862 | acl_list *L = NULL; | |
863 | acl_list **Tail = NULL; | |
864 | acl *a = NULL; | |
7dd57fa2 | 865 | |
866 | /* first expect either 'allow' or 'deny' */ | |
867 | if ((t = strtok(NULL, w_space)) == NULL) { | |
a3d5953d | 868 | debug(28, 0) ("%s line %d: %s\n", |
b8de7ebe | 869 | cfg_filename, config_lineno, config_input_line); |
a3d5953d | 870 | debug(28, 0) ("aclParseAccessLine: missing 'allow' or 'deny'.\n"); |
7dd57fa2 | 871 | return; |
872 | } | |
c68e9c6b | 873 | A = memAllocate(MEM_ACL_ACCESS); |
bdf18524 | 874 | |
7dd57fa2 | 875 | if (!strcmp(t, "allow")) |
876 | A->allow = 1; | |
877 | else if (!strcmp(t, "deny")) | |
878 | A->allow = 0; | |
879 | else { | |
a3d5953d | 880 | debug(28, 0) ("%s line %d: %s\n", |
b8de7ebe | 881 | cfg_filename, config_lineno, config_input_line); |
a3d5953d | 882 | debug(28, 0) ("aclParseAccessLine: expecting 'allow' or 'deny', got '%s'.\n", t); |
7dd57fa2 | 883 | xfree(A); |
884 | return; | |
885 | } | |
886 | ||
887 | /* next expect a list of ACL names, possibly preceeded | |
888 | * by '!' for negation */ | |
8213067d | 889 | Tail = &A->acl_list; |
7dd57fa2 | 890 | while ((t = strtok(NULL, w_space))) { |
c68e9c6b | 891 | L = memAllocate(MEM_ACL_LIST); |
1969665d | 892 | L->op = 1; /* defaults to non-negated */ |
7dd57fa2 | 893 | if (*t == '!') { |
1969665d | 894 | /* negated ACL */ |
895 | L->op = 0; | |
896 | t++; | |
7dd57fa2 | 897 | } |
a3d5953d | 898 | debug(28, 3) ("aclParseAccessLine: looking for ACL name '%s'\n", t); |
7dd57fa2 | 899 | a = aclFindByName(t); |
900 | if (a == NULL) { | |
a3d5953d | 901 | debug(28, 0) ("%s line %d: %s\n", |
b8de7ebe | 902 | cfg_filename, config_lineno, config_input_line); |
a3d5953d | 903 | debug(28, 0) ("aclParseAccessLine: ACL name '%s' not found.\n", t); |
1969665d | 904 | xfree(L); |
905 | continue; | |
7dd57fa2 | 906 | } |
907 | L->acl = a; | |
908 | *Tail = L; | |
909 | Tail = &L->next; | |
910 | } | |
837dcbb9 | 911 | if (A->acl_list == NULL) { |
a3d5953d | 912 | debug(28, 0) ("%s line %d: %s\n", |
b8de7ebe | 913 | cfg_filename, config_lineno, config_input_line); |
a3d5953d | 914 | debug(28, 0) ("aclParseAccessLine: Access line contains no ACL's, skipping\n"); |
837dcbb9 | 915 | xfree(A); |
916 | return; | |
917 | } | |
3003c0f3 | 918 | A->cfgline = xstrdup(config_input_line); |
e71209ce | 919 | /* Append to the end of this list */ |
920 | for (B = *head, T = head; B; T = &B->next, B = B->next); | |
92a6f4b1 | 921 | *T = A; |
e71209ce | 922 | /* We lock _acl_access structures in aclCheck() */ |
db1cd23c | 923 | cbdataAdd(A, memFree, MEM_ACL_ACCESS); |
7dd57fa2 | 924 | } |
925 | ||
f32789f4 | 926 | /**************/ |
927 | /* aclMatchIp */ | |
928 | /**************/ | |
929 | ||
f32789f4 | 930 | static int |
931 | aclMatchIp(void *dataptr, struct in_addr c) | |
932 | { | |
933 | splayNode **Top = dataptr; | |
934 | *Top = splay_splay(&c, *Top, aclIpNetworkCompare); | |
a3d5953d | 935 | debug(28, 3) ("aclMatchIp: '%s' %s\n", |
f32789f4 | 936 | inet_ntoa(c), splayLastResult ? "NOT found" : "found"); |
937 | return !splayLastResult; | |
938 | } | |
939 | ||
f32789f4 | 940 | /**********************/ |
941 | /* aclMatchDomainList */ | |
942 | /**********************/ | |
943 | ||
f32789f4 | 944 | static int |
945 | aclMatchDomainList(void *dataptr, const char *host) | |
946 | { | |
947 | splayNode **Top = dataptr; | |
948 | if (host == NULL) | |
949 | return 0; | |
a3d5953d | 950 | debug(28, 3) ("aclMatchDomainList: checking '%s'\n", host); |
f32789f4 | 951 | *Top = splay_splay(host, *Top, aclHostDomainCompare); |
a3d5953d | 952 | debug(28, 3) ("aclMatchDomainList: '%s' %s\n", |
f32789f4 | 953 | host, splayLastResult ? "NOT found" : "found"); |
954 | return !splayLastResult; | |
abf6b4de | 955 | } |
956 | ||
33cdd606 | 957 | int |
0ee4272b | 958 | aclMatchRegex(relist * data, const char *word) |
abf6b4de | 959 | { |
c10aaf05 | 960 | relist *first, *prev; |
234967c9 | 961 | if (word == NULL) |
962 | return 0; | |
a3d5953d | 963 | debug(28, 3) ("aclMatchRegex: checking '%s'\n", word); |
c10aaf05 | 964 | first = data; |
965 | prev = NULL; | |
abf6b4de | 966 | while (data) { |
a3d5953d | 967 | debug(28, 3) ("aclMatchRegex: looking for '%s'\n", data->pattern); |
c10aaf05 | 968 | if (regexec(&data->regex, word, 0, 0, 0) == 0) { |
969 | if (prev != NULL) { | |
970 | /* shift the element just found to the second position | |
e5f6c5c2 | 971 | * in the list */ |
c10aaf05 | 972 | prev->next = data->next; |
973 | data->next = first->next; | |
974 | first->next = data; | |
975 | } | |
abf6b4de | 976 | return 1; |
c10aaf05 | 977 | } |
978 | prev = data; | |
abf6b4de | 979 | data = data->next; |
980 | } | |
981 | return 0; | |
982 | } | |
30a4f2a8 | 983 | |
94103840 | 984 | static int |
c68e9c6b | 985 | aclMatchUser(wordlist * data, const char *user) |
94103840 | 986 | { |
c68e9c6b | 987 | if (user == NULL) |
94103840 | 988 | return 0; |
c68e9c6b | 989 | debug(28, 3) ("aclMatchUser: checking '%s'\n", user); |
94103840 | 990 | while (data) { |
c68e9c6b | 991 | debug(28, 3) ("aclMatchUser: looking for '%s'\n", data->key); |
05832ae1 | 992 | if (strcmp(data->key, "REQUIRED") == 0 && *user != '\0' && strcmp(user, "-") != 0) |
94103840 | 993 | return 1; |
c68e9c6b | 994 | if (strcmp(data->key, user) == 0) |
94103840 | 995 | return 1; |
996 | data = data->next; | |
997 | } | |
998 | return 0; | |
999 | } | |
1000 | ||
afe95a7e | 1001 | static int |
c68e9c6b | 1002 | aclDecodeProxyAuth(const char *proxy_auth, char **user, char **password, char *buf, size_t bufsize) |
afe95a7e | 1003 | { |
afe95a7e | 1004 | char *sent_auth; |
c68e9c6b | 1005 | char *cleartext; |
c68e9c6b | 1006 | if (proxy_auth == NULL) |
afe95a7e | 1007 | return 0; |
7e3ce7b9 | 1008 | debug(28, 6) ("aclDecodeProxyAuth: header = '%s'\n", proxy_auth); |
1009 | if (strncasecmp(proxy_auth, "Basic ", 6) != 0) { | |
1010 | debug(28, 1) ("aclDecodeProxyAuth: Unsupported proxy-auth sheme, '%s'\n", proxy_auth); | |
afe95a7e | 1011 | return 0; |
7e3ce7b9 | 1012 | } |
1013 | proxy_auth += 6; /* "Basic " */ | |
c68e9c6b | 1014 | /* Trim leading whitespace before decoding */ |
b6a2f15e | 1015 | while (xisspace(*proxy_auth)) |
c68e9c6b | 1016 | proxy_auth++; |
7e3ce7b9 | 1017 | sent_auth = xstrdup(proxy_auth); /* username and password */ |
1018 | /* Trim trailing \n before decoding */ | |
1019 | strtok(sent_auth, "\n"); | |
afe95a7e | 1020 | cleartext = uudecode(sent_auth); |
1021 | xfree(sent_auth); | |
7e3ce7b9 | 1022 | /* |
1023 | * Don't allow NL or CR in the credentials. | |
1024 | * Oezguer Kesim <oec@codeblau.de> | |
1025 | */ | |
1026 | strtok(cleartext, "\r\n"); | |
c68e9c6b | 1027 | debug(28, 6) ("aclDecodeProxyAuth: cleartext = '%s'\n", cleartext); |
1028 | xstrncpy(buf, cleartext, bufsize); | |
afe95a7e | 1029 | xfree(cleartext); |
c68e9c6b | 1030 | *user = buf; |
1031 | if ((*password = strchr(*user, ':')) != NULL) | |
1032 | *(*password)++ = '\0'; | |
e42d5181 | 1033 | if (*password == NULL) { |
bb6b72a4 | 1034 | debug(28, 1) ("aclDecodeProxyAuth: no password in proxy authorization header '%s'\n", proxy_auth); |
78385265 | 1035 | return 0; |
1036 | } | |
1f38f50a | 1037 | if (**password == '\0') { |
1038 | debug(28, 1) ("aclDecodeProxyAuth: Disallowing empty password," | |
1039 | "user is '%s'\n", *user); | |
1040 | return 0; | |
1041 | } | |
c68e9c6b | 1042 | return 1; |
1043 | } | |
73e67ee0 | 1044 | |
c68e9c6b | 1045 | /* aclMatchProxyAuth can return three exit codes: |
1cfdbcf0 | 1046 | * 0 : user denied access |
c68e9c6b | 1047 | * 1 : user validated OK |
1048 | * -1 : check the password for this user via an external authenticator | |
1cfdbcf0 | 1049 | * -2 : invalid Proxy-authorization: header; |
1050 | * ask for Proxy-Authorization: header | |
c68e9c6b | 1051 | */ |
1052 | ||
1053 | static int | |
227e68b6 | 1054 | aclMatchProxyAuth(wordlist * data, const char *proxy_auth, acl_proxy_auth_user * auth_user, aclCheck_t * checklist) |
c68e9c6b | 1055 | { |
1056 | /* checklist is used to register user name when identified, nothing else */ | |
1057 | LOCAL_ARRAY(char, login_buf, USER_IDENT_SZ); | |
1058 | char *user, *password; | |
1059 | ||
1060 | if (!aclDecodeProxyAuth(proxy_auth, &user, &password, login_buf, sizeof(login_buf))) | |
1061 | /* No or invalid Proxy-Auth header */ | |
1cfdbcf0 | 1062 | return -2; |
c68e9c6b | 1063 | |
1064 | debug(28, 5) ("aclMatchProxyAuth: checking user '%s'\n", user); | |
1065 | ||
1cfdbcf0 | 1066 | if (auth_user) { |
1f38f50a | 1067 | /* |
1068 | * This should be optimized to a boolean argument indicating that the | |
1cfdbcf0 | 1069 | * password is invalid, instead of passing full acl_proxy_auth_user |
1070 | * structures, and all messing with checklist->proxy_auth should | |
1071 | * be restricted the functions that deal with the authenticator. | |
1072 | */ | |
1073 | assert(auth_user == checklist->auth_user); | |
227e68b6 | 1074 | checklist->auth_user = NULL; /* get rid of that special reference */ |
1cfdbcf0 | 1075 | /* Check result from external validation */ |
1076 | if (auth_user->passwd_ok != 1) { | |
1077 | /* password was checked but did not match */ | |
1078 | assert(auth_user->passwd_ok == 0); | |
1079 | debug(28, 4) ("aclMatchProxyAuth: authentication failed for user '%s'\n", | |
c68e9c6b | 1080 | user); |
c68e9c6b | 1081 | aclFreeProxyAuthUser(auth_user); |
1f38f50a | 1082 | /* |
1083 | * copy username to request for logging on client-side | |
1084 | * unless ident is known (do not override ident with | |
1085 | * false proxy auth names) | |
1086 | */ | |
c68e9c6b | 1087 | if (!*checklist->request->user_ident) |
1088 | xstrncpy(checklist->request->user_ident, user, USER_IDENT_SZ); | |
1cfdbcf0 | 1089 | return -2; |
1090 | } else { | |
1091 | /* password was checked and did match */ | |
1092 | debug(28, 4) ("aclMatchProxyAuth: user '%s' validated OK\n", user); | |
1093 | /* store validated user in hash, after filling in expiretime */ | |
aed86a7c | 1094 | xstrncpy(checklist->request->user_ident, user, USER_IDENT_SZ); |
1cfdbcf0 | 1095 | auth_user->expiretime = current_time.tv_sec + Config.authenticateTTL; |
9bc73deb | 1096 | auth_user->ip_expiretime = squid_curtime + Config.authenticateIpTTL; |
1097 | auth_user->ipaddr = checklist->src_addr; | |
1cfdbcf0 | 1098 | hash_join(proxy_auth_cache, (hash_link *) auth_user); |
1099 | /* Continue checking below, as normal */ | |
c68e9c6b | 1100 | } |
afe95a7e | 1101 | } |
1cfdbcf0 | 1102 | /* see if we already know this user */ |
1103 | auth_user = hash_lookup(proxy_auth_cache, user); | |
1104 | ||
1105 | if (!auth_user) { | |
1106 | /* user not yet known, ask external authenticator */ | |
1107 | debug(28, 4) ("aclMatchProxyAuth: user '%s' not yet known\n", user); | |
1108 | return -1; | |
1109 | } else if ((0 == strcmp(auth_user->passwd, password)) && | |
227e68b6 | 1110 | (auth_user->expiretime > current_time.tv_sec)) { |
9bc73deb | 1111 | if (checklist->src_addr.s_addr == auth_user->ipaddr.s_addr |
1112 | || auth_user->ip_expiretime <= squid_curtime) { | |
1113 | /* user already known and valid */ | |
1114 | debug(28, 5) ("aclMatchProxyAuth: user '%s' previously validated\n", | |
1115 | user); | |
1116 | /* Update IP ttl */ | |
1117 | auth_user->ip_expiretime = squid_curtime + Config.authenticateIpTTL; | |
1118 | auth_user->ipaddr = checklist->src_addr; | |
1119 | /* copy username to request for logging on client-side */ | |
1120 | xstrncpy(checklist->request->user_ident, user, USER_IDENT_SZ); | |
1121 | return aclMatchUser(data, user); | |
1122 | } else { | |
1123 | /* user has switched to another IP addr */ | |
1124 | debug(28, 1) ("aclMatchProxyAuth: user '%s' has changed IP address\n", user); | |
1125 | /* remove this user from the hash, making him unknown */ | |
1126 | hash_remove_link(proxy_auth_cache, (hash_link *) auth_user); | |
1127 | aclFreeProxyAuthUser(auth_user); | |
1128 | /* require the user to reauthenticate */ | |
1129 | return -2; | |
1130 | } | |
1cfdbcf0 | 1131 | } else { |
1132 | /* password mismatch/timeout */ | |
1133 | debug(28, 4) ("aclMatchProxyAuth: user '%s' password mismatch/timeout\n", | |
1134 | user); | |
1135 | /* remove this user from the hash, making him unknown */ | |
1136 | hash_remove_link(proxy_auth_cache, (hash_link *) auth_user); | |
1137 | aclFreeProxyAuthUser(auth_user); | |
1138 | /* ask the external authenticator in case the password is changed */ | |
1139 | /* wrong password will be trapped above so this does not loop */ | |
1140 | return -1; | |
1141 | } | |
c68e9c6b | 1142 | /* NOTREACHED */ |
1143 | ||
1144 | } | |
1145 | ||
1146 | static void | |
1147 | aclLookupProxyAuthStart(aclCheck_t * checklist) | |
1148 | { | |
1149 | LOCAL_ARRAY(char, login_buf, USER_IDENT_SZ); | |
1150 | const char *proxy_auth; | |
1151 | char *user, *password; | |
1152 | int ok; | |
1153 | acl_proxy_auth_user *auth_user; | |
c68e9c6b | 1154 | assert(!checklist->auth_user); |
c68e9c6b | 1155 | if (!checklist->request->flags.accelerated) { |
1156 | /* Proxy auth on proxy requests */ | |
1157 | proxy_auth = httpHeaderGetStr(&checklist->request->header, | |
1158 | HDR_PROXY_AUTHORIZATION); | |
1159 | } else { | |
1160 | /* WWW auth on accelerated requests */ | |
1161 | proxy_auth = httpHeaderGetStr(&checklist->request->header, | |
1162 | HDR_AUTHORIZATION); | |
afe95a7e | 1163 | } |
c68e9c6b | 1164 | ok = aclDecodeProxyAuth(proxy_auth, &user, &password, login_buf, |
1165 | sizeof(login_buf)); | |
e42d5181 | 1166 | /* |
1167 | * if aclDecodeProxyAuth() fails, the same call should have failed | |
1168 | * in aclMatchProxyAuth, and we should never get this far. | |
1169 | */ | |
1170 | assert(ok); | |
c68e9c6b | 1171 | debug(28, 4) ("aclLookupProxyAuthStart: going to ask authenticator on %s\n", user); |
1172 | /* we must still check this user's password */ | |
1173 | auth_user = memAllocate(MEM_ACL_PROXY_AUTH_USER); | |
1174 | auth_user->user = xstrdup(user); | |
1175 | auth_user->passwd = xstrdup(password); | |
1176 | auth_user->passwd_ok = -1; | |
1177 | auth_user->expiretime = -1; | |
1178 | checklist->auth_user = auth_user; | |
c68e9c6b | 1179 | authenticateStart(checklist->auth_user, aclLookupProxyAuthDone, |
1180 | checklist); | |
afe95a7e | 1181 | } |
1182 | ||
8203a132 | 1183 | static int |
1184 | aclMatchInteger(intlist * data, int i) | |
abf6b4de | 1185 | { |
c10aaf05 | 1186 | intlist *first, *prev; |
1187 | first = data; | |
1188 | prev = NULL; | |
abf6b4de | 1189 | while (data) { |
c10aaf05 | 1190 | if (data->i == i) { |
1191 | if (prev != NULL) { | |
1192 | /* shift the element just found to the second position | |
e5f6c5c2 | 1193 | * in the list */ |
c10aaf05 | 1194 | prev->next = data->next; |
1195 | data->next = first->next; | |
1196 | first->next = data; | |
1197 | } | |
abf6b4de | 1198 | return 1; |
c10aaf05 | 1199 | } |
1200 | prev = data; | |
abf6b4de | 1201 | data = data->next; |
1202 | } | |
1203 | return 0; | |
1204 | } | |
1205 | ||
8f663d72 | 1206 | static int |
1207 | aclMatchIntegerRange(intrange * data, int i) | |
1208 | { | |
1209 | intrange *first, *prev; | |
1210 | first = data; | |
1211 | prev = NULL; | |
1212 | while (data) { | |
1213 | if (i < data->i) { | |
5942e8d4 | 1214 | (void) 0; |
8f663d72 | 1215 | } else if (i > data->j) { |
5942e8d4 | 1216 | (void) 0; |
8f663d72 | 1217 | } else { |
1218 | /* matched */ | |
1219 | if (prev != NULL) { | |
1220 | /* shift the element just found to the second position | |
1221 | * in the list */ | |
1222 | prev->next = data->next; | |
1223 | data->next = first->next; | |
1224 | first->next = data; | |
1225 | } | |
1226 | return 1; | |
1227 | } | |
1228 | prev = data; | |
1229 | data = data->next; | |
1230 | } | |
1231 | return 0; | |
1232 | } | |
1233 | ||
8203a132 | 1234 | static int |
7342cdc8 | 1235 | aclMatchTime(acl_time_data * data, time_t when) |
92a6f4b1 | 1236 | { |
540830c4 | 1237 | static time_t last_when = 0; |
1238 | static struct tm tm; | |
1239 | time_t t; | |
fc5d6f7f | 1240 | assert(data != NULL); |
540830c4 | 1241 | if (when != last_when) { |
1242 | last_when = when; | |
30a4f2a8 | 1243 | xmemcpy(&tm, localtime(&when), sizeof(struct tm)); |
540830c4 | 1244 | } |
540830c4 | 1245 | t = (time_t) (tm.tm_hour * 60 + tm.tm_min); |
a3d5953d | 1246 | debug(28, 3) ("aclMatchTime: checking %d in %d-%d, weekbits=%x\n", |
9bf12439 | 1247 | (int) t, (int) data->start, (int) data->stop, data->weekbits); |
1248 | ||
540830c4 | 1249 | if (t < data->start || t > data->stop) |
1250 | return 0; | |
234967c9 | 1251 | return data->weekbits & (1 << tm.tm_wday) ? 1 : 0; |
92a6f4b1 | 1252 | } |
1253 | ||
9e160ce2 | 1254 | #if SQUID_SNMP |
dba79ac5 | 1255 | static int |
c68e9c6b | 1256 | aclMatchWordList(wordlist * w, const char *word) |
dba79ac5 | 1257 | { |
c68e9c6b | 1258 | debug(28, 3) ("aclMatchWordList: looking for '%s'\n", word); |
dba79ac5 | 1259 | while (w != NULL) { |
1260 | debug(28, 3) ("aclMatchWordList: checking '%s'\n", w->key); | |
c68e9c6b | 1261 | if (!strcmp(w->key, word)) |
dba79ac5 | 1262 | return 1; |
1263 | w = w->next; | |
1264 | } | |
1265 | return 0; | |
1266 | } | |
9e160ce2 | 1267 | #endif |
dba79ac5 | 1268 | |
669fefd4 | 1269 | static int |
9ef28b60 | 1270 | aclMatchAcl(acl * ae, aclCheck_t * checklist) |
1969665d | 1271 | { |
c1517455 | 1272 | request_t *r = checklist->request; |
0ee4272b | 1273 | const ipcache_addrs *ia = NULL; |
1274 | const char *fqdn = NULL; | |
1c73c7e6 | 1275 | char *esc_buf; |
1cfdbcf0 | 1276 | const char *header; |
db043f2e | 1277 | const char *browser; |
30a4f2a8 | 1278 | int k; |
9ef28b60 | 1279 | if (!ae) |
1969665d | 1280 | return 0; |
9ef28b60 | 1281 | debug(28, 3) ("aclMatchAcl: checking '%s'\n", ae->cfgline); |
1282 | switch (ae->type) { | |
1969665d | 1283 | case ACL_SRC_IP: |
9ef28b60 | 1284 | return aclMatchIp(&ae->data, checklist->src_addr); |
983061ed | 1285 | /* NOTREACHED */ |
ae2c08a2 | 1286 | case ACL_MY_IP: |
1287 | return aclMatchIp(&ae->data, checklist->my_addr); | |
1288 | /* NOTREACHED */ | |
30a4f2a8 | 1289 | case ACL_DST_IP: |
e5f6c5c2 | 1290 | ia = ipcache_gethostbyname(r->host, IP_LOOKUP_IF_MISS); |
1291 | if (ia) { | |
1292 | for (k = 0; k < (int) ia->count; k++) { | |
9ef28b60 | 1293 | if (aclMatchIp(&ae->data, ia->in_addrs[k])) |
665d36de | 1294 | return 1; |
1295 | } | |
1296 | return 0; | |
1297 | } else if (checklist->state[ACL_DST_IP] == ACL_LOOKUP_NONE) { | |
a3d5953d | 1298 | debug(28, 3) ("aclMatchAcl: Can't yet compare '%s' ACL for '%s'\n", |
9ef28b60 | 1299 | ae->name, r->host); |
e7a22b88 | 1300 | checklist->state[ACL_DST_IP] = ACL_LOOKUP_NEEDED; |
f88bb09c | 1301 | return 0; |
665d36de | 1302 | } else { |
9ef28b60 | 1303 | return aclMatchIp(&ae->data, no_addr); |
30a4f2a8 | 1304 | } |
30a4f2a8 | 1305 | /* NOTREACHED */ |
1969665d | 1306 | case ACL_DST_DOMAIN: |
4d650936 | 1307 | if ((ia = ipcacheCheckNumeric(r->host)) == NULL) |
9ef28b60 | 1308 | return aclMatchDomainList(&ae->data, r->host); |
4d650936 | 1309 | fqdn = fqdncache_gethostbyaddr(ia->in_addrs[0], FQDN_LOOKUP_IF_MISS); |
1310 | if (fqdn) | |
9ef28b60 | 1311 | return aclMatchDomainList(&ae->data, fqdn); |
4d650936 | 1312 | if (checklist->state[ACL_DST_DOMAIN] == ACL_LOOKUP_NONE) { |
a3d5953d | 1313 | debug(28, 3) ("aclMatchAcl: Can't yet compare '%s' ACL for '%s'\n", |
9ef28b60 | 1314 | ae->name, inet_ntoa(ia->in_addrs[0])); |
e7a22b88 | 1315 | checklist->state[ACL_DST_DOMAIN] = ACL_LOOKUP_NEEDED; |
4d650936 | 1316 | return 0; |
1317 | } | |
9ef28b60 | 1318 | return aclMatchDomainList(&ae->data, "none"); |
f88bb09c | 1319 | /* NOTREACHED */ |
1320 | case ACL_SRC_DOMAIN: | |
1321 | fqdn = fqdncache_gethostbyaddr(checklist->src_addr, FQDN_LOOKUP_IF_MISS); | |
665d36de | 1322 | if (fqdn) { |
9ef28b60 | 1323 | return aclMatchDomainList(&ae->data, fqdn); |
665d36de | 1324 | } else if (checklist->state[ACL_SRC_DOMAIN] == ACL_LOOKUP_NONE) { |
a3d5953d | 1325 | debug(28, 3) ("aclMatchAcl: Can't yet compare '%s' ACL for '%s'\n", |
9ef28b60 | 1326 | ae->name, inet_ntoa(checklist->src_addr)); |
e7a22b88 | 1327 | checklist->state[ACL_SRC_DOMAIN] = ACL_LOOKUP_NEEDED; |
f88bb09c | 1328 | return 0; |
1329 | } | |
9ef28b60 | 1330 | return aclMatchDomainList(&ae->data, "none"); |
d0d41f07 | 1331 | /* NOTREACHED */ |
1332 | case ACL_DST_DOM_REGEX: | |
1333 | if ((ia = ipcacheCheckNumeric(r->host)) == NULL) | |
9ef28b60 | 1334 | return aclMatchRegex(ae->data, r->host); |
d0d41f07 | 1335 | fqdn = fqdncache_gethostbyaddr(ia->in_addrs[0], FQDN_LOOKUP_IF_MISS); |
1336 | if (fqdn) | |
9ef28b60 | 1337 | return aclMatchRegex(ae->data, fqdn); |
d0d41f07 | 1338 | if (checklist->state[ACL_DST_DOMAIN] == ACL_LOOKUP_NONE) { |
1339 | debug(28, 3) ("aclMatchAcl: Can't yet compare '%s' ACL for '%s'\n", | |
9ef28b60 | 1340 | ae->name, inet_ntoa(ia->in_addrs[0])); |
d0d41f07 | 1341 | checklist->state[ACL_DST_DOMAIN] = ACL_LOOKUP_NEEDED; |
1342 | return 0; | |
1343 | } | |
9ef28b60 | 1344 | return aclMatchRegex(ae->data, "none"); |
d0d41f07 | 1345 | /* NOTREACHED */ |
1346 | case ACL_SRC_DOM_REGEX: | |
1347 | fqdn = fqdncache_gethostbyaddr(checklist->src_addr, FQDN_LOOKUP_IF_MISS); | |
1348 | if (fqdn) { | |
9ef28b60 | 1349 | return aclMatchRegex(ae->data, fqdn); |
d0d41f07 | 1350 | } else if (checklist->state[ACL_SRC_DOMAIN] == ACL_LOOKUP_NONE) { |
1351 | debug(28, 3) ("aclMatchAcl: Can't yet compare '%s' ACL for '%s'\n", | |
9ef28b60 | 1352 | ae->name, inet_ntoa(checklist->src_addr)); |
d0d41f07 | 1353 | checklist->state[ACL_SRC_DOMAIN] = ACL_LOOKUP_NEEDED; |
1354 | return 0; | |
1355 | } | |
9ef28b60 | 1356 | return aclMatchRegex(ae->data, "none"); |
983061ed | 1357 | /* NOTREACHED */ |
1969665d | 1358 | case ACL_TIME: |
9ef28b60 | 1359 | return aclMatchTime(ae->data, squid_curtime); |
983061ed | 1360 | /* NOTREACHED */ |
6e40f263 | 1361 | case ACL_URLPATH_REGEX: |
1c73c7e6 | 1362 | esc_buf = xstrdup(strBuf(r->urlpath)); |
1363 | rfc1738_unescape(esc_buf); | |
9ef28b60 | 1364 | k = aclMatchRegex(ae->data, esc_buf); |
1c73c7e6 | 1365 | safe_free(esc_buf); |
1366 | return k; | |
983061ed | 1367 | /* NOTREACHED */ |
6e40f263 | 1368 | case ACL_URL_REGEX: |
9b5d1d21 | 1369 | esc_buf = xstrdup(urlCanonical(r)); |
1c73c7e6 | 1370 | rfc1738_unescape(esc_buf); |
9ef28b60 | 1371 | k = aclMatchRegex(ae->data, esc_buf); |
1c73c7e6 | 1372 | safe_free(esc_buf); |
1373 | return k; | |
6e40f263 | 1374 | /* NOTREACHED */ |
9bc73deb | 1375 | case ACL_MAXCONN: |
1376 | k = clientdbEstablished(checklist->src_addr, 0); | |
1377 | return ((k > ((intlist *) ae->data)->i) ? 0 : 1); | |
1378 | /* NOTREACHED */ | |
1969665d | 1379 | case ACL_URL_PORT: |
7e3ce7b9 | 1380 | return aclMatchIntegerRange(ae->data, (int) r->port); |
1381 | /* NOTREACHED */ | |
1382 | case ACL_MY_PORT: | |
1383 | return aclMatchIntegerRange(ae->data, (int) checklist->my_port); | |
983061ed | 1384 | /* NOTREACHED */ |
3898f57f | 1385 | #if USE_IDENT |
c68e9c6b | 1386 | case ACL_IDENT: |
3898f57f | 1387 | if (checklist->ident[0]) { |
05832ae1 | 1388 | return aclMatchUser(ae->data, checklist->ident); |
1389 | } else { | |
1390 | checklist->state[ACL_IDENT] = ACL_LOOKUP_NEEDED; | |
1391 | return 0; | |
1392 | } | |
983061ed | 1393 | /* NOTREACHED */ |
3898f57f | 1394 | #endif |
1969665d | 1395 | case ACL_PROTO: |
9ef28b60 | 1396 | return aclMatchInteger(ae->data, r->protocol); |
983061ed | 1397 | /* NOTREACHED */ |
92a6f4b1 | 1398 | case ACL_METHOD: |
9ef28b60 | 1399 | return aclMatchInteger(ae->data, r->method); |
983061ed | 1400 | /* NOTREACHED */ |
d3416189 | 1401 | case ACL_BROWSER: |
db043f2e | 1402 | browser = httpHeaderGetStr(&checklist->request->header, HDR_USER_AGENT); |
1403 | if (NULL == browser) | |
1404 | return 0; | |
1405 | return aclMatchRegex(ae->data, browser); | |
d3416189 | 1406 | /* NOTREACHED */ |
afe95a7e | 1407 | case ACL_PROXY_AUTH: |
1ecaa0a0 | 1408 | if (NULL == r) { |
1409 | return -1; | |
1410 | } else if (!r->flags.accelerated) { | |
c68e9c6b | 1411 | /* Proxy authorization on proxy requests */ |
1cfdbcf0 | 1412 | header = httpHeaderGetStr(&checklist->request->header, |
227e68b6 | 1413 | HDR_PROXY_AUTHORIZATION); |
c68e9c6b | 1414 | } else if (r->flags.internal) { |
1415 | /* WWW authorization on accelerated internal requests */ | |
1cfdbcf0 | 1416 | header = httpHeaderGetStr(&checklist->request->header, |
227e68b6 | 1417 | HDR_AUTHORIZATION); |
c68e9c6b | 1418 | } else { |
1419 | #if AUTH_ON_ACCELERATION | |
1420 | /* WWW authorization on accelerated requests */ | |
1cfdbcf0 | 1421 | header = httpHeaderGetStr(&checklist->request->header, |
227e68b6 | 1422 | HDR_AUTHORIZATION); |
c68e9c6b | 1423 | #else |
1424 | debug(28, 1) ("aclMatchAcl: proxy_auth %s not applicable on accelerated requests.\n", ae->name); | |
1425 | return -1; | |
1426 | #endif | |
1427 | } | |
1cfdbcf0 | 1428 | /* |
1429 | * Register that we used the proxy authentication header so that | |
1430 | * it is not forwarded to the next proxy | |
1431 | */ | |
1432 | r->flags.used_proxy_auth = 1; | |
1433 | /* Check the password */ | |
227e68b6 | 1434 | switch (aclMatchProxyAuth(ae->data, |
1435 | header, | |
1436 | checklist->auth_user, | |
1437 | checklist)) { | |
1cfdbcf0 | 1438 | case 0: |
1439 | /* Correct password, but was not allowed in this ACL */ | |
1440 | return 0; | |
1441 | case 1: | |
1442 | /* user validated OK */ | |
1443 | return 1; | |
1444 | case -2: | |
afe95a7e | 1445 | /* no such user OR we need a proxy authentication header */ |
73e67ee0 | 1446 | checklist->state[ACL_PROXY_AUTH] = ACL_PROXY_AUTH_NEEDED; |
c68e9c6b | 1447 | /* |
1448 | * XXX This is a bit oddly done.. should perhaps use different | |
1449 | * return codes here | |
1450 | */ | |
afe95a7e | 1451 | return 0; |
1cfdbcf0 | 1452 | case -1: |
c68e9c6b | 1453 | /* |
1454 | * we need to validate the password | |
1455 | */ | |
1456 | checklist->state[ACL_PROXY_AUTH] = ACL_LOOKUP_NEEDED; | |
73e67ee0 | 1457 | return 0; |
afe95a7e | 1458 | } |
1459 | /* NOTREACHED */ | |
dba79ac5 | 1460 | #if SQUID_SNMP |
1461 | case ACL_SNMP_COMMUNITY: | |
1462 | return aclMatchWordList(ae->data, checklist->snmp_community); | |
1463 | #endif | |
5d6c7aad | 1464 | case ACL_SRC_ASN: |
9ef28b60 | 1465 | return asnMatchIp(ae->data, checklist->src_addr); |
5d6c7aad | 1466 | case ACL_DST_ASN: |
53ad48e6 | 1467 | ia = ipcache_gethostbyname(r->host, IP_LOOKUP_IF_MISS); |
1468 | if (ia) { | |
1469 | for (k = 0; k < (int) ia->count; k++) { | |
9ef28b60 | 1470 | if (asnMatchIp(ae->data, ia->in_addrs[k])) |
53ad48e6 | 1471 | return 1; |
1472 | } | |
1473 | return 0; | |
1474 | } else if (checklist->state[ACL_DST_ASN] == ACL_LOOKUP_NONE) { | |
1475 | debug(28, 3) ("asnMatchAcl: Can't yet compare '%s' ACL for '%s'\n", | |
9ef28b60 | 1476 | ae->name, r->host); |
53ad48e6 | 1477 | checklist->state[ACL_DST_ASN] = ACL_LOOKUP_NEEDED; |
1478 | } else { | |
9ef28b60 | 1479 | return asnMatchIp(ae->data, no_addr); |
53ad48e6 | 1480 | } |
5d6c7aad | 1481 | return 0; |
66c75c41 | 1482 | #if USE_ARP_ACL |
1483 | case ACL_SRC_ARP: | |
9ef28b60 | 1484 | return aclMatchArp(&ae->data, checklist->src_addr); |
66c75c41 | 1485 | #endif |
1969665d | 1486 | case ACL_NONE: |
1487 | default: | |
a3d5953d | 1488 | debug(28, 0) ("aclMatchAcl: '%s' has bad type %d\n", |
9ef28b60 | 1489 | ae->name, ae->type); |
1969665d | 1490 | return 0; |
1491 | } | |
30a4f2a8 | 1492 | /* NOTREACHED */ |
1969665d | 1493 | } |
1494 | ||
669fefd4 | 1495 | int |
7342cdc8 | 1496 | aclMatchAclList(const acl_list * list, aclCheck_t * checklist) |
1969665d | 1497 | { |
1969665d | 1498 | while (list) { |
e92d33a5 | 1499 | AclMatchedName = list->acl->name; |
a3d5953d | 1500 | debug(28, 3) ("aclMatchAclList: checking %s%s\n", |
ffa8aa23 | 1501 | list->op ? null_string : "!", list->acl->name); |
99d829f9 | 1502 | if (aclMatchAcl(list->acl, checklist) != list->op) { |
a3d5953d | 1503 | debug(28, 3) ("aclMatchAclList: returning 0\n"); |
abf6b4de | 1504 | return 0; |
837dcbb9 | 1505 | } |
abf6b4de | 1506 | list = list->next; |
1969665d | 1507 | } |
a3d5953d | 1508 | debug(28, 3) ("aclMatchAclList: returning 1\n"); |
1969665d | 1509 | return 1; |
1510 | } | |
1511 | ||
8203a132 | 1512 | int |
7342cdc8 | 1513 | aclCheckFast(const acl_access * A, aclCheck_t * checklist) |
1969665d | 1514 | { |
1969665d | 1515 | int allow = 0; |
0c511722 | 1516 | debug(28, 5) ("aclCheckFast: list: %p\n", A); |
92a6f4b1 | 1517 | while (A) { |
b6c0e933 | 1518 | allow = A->allow; |
1519 | if (aclMatchAclList(A->acl_list, checklist)) | |
1520 | return allow; | |
1521 | A = A->next; | |
75e88d56 | 1522 | } |
0c511722 | 1523 | debug(28, 5) ("aclCheckFast: no matches, returning: %d\n", !allow); |
75e88d56 | 1524 | return !allow; |
1525 | } | |
1526 | ||
1527 | static void | |
1528 | aclCheck(aclCheck_t * checklist) | |
1529 | { | |
fc5d6f7f | 1530 | allow_t allow = ACCESS_DENIED; |
7342cdc8 | 1531 | const acl_access *A; |
75e88d56 | 1532 | int match; |
348b2031 | 1533 | ipcache_addrs *ia; |
79d39a72 | 1534 | while ((A = checklist->access_list) != NULL) { |
e71209ce | 1535 | /* |
1536 | * If the _acl_access is no longer valid (i.e. its been | |
1537 | * freed because of a reconfigure), then bail on this | |
1538 | * access check. For now, return ACCESS_DENIED. | |
1539 | */ | |
1540 | if (!cbdataValid(A)) { | |
1541 | cbdataUnlock(A); | |
1542 | break; | |
1543 | } | |
a3d5953d | 1544 | debug(28, 3) ("aclCheck: checking '%s'\n", A->cfgline); |
1969665d | 1545 | allow = A->allow; |
75e88d56 | 1546 | match = aclMatchAclList(A->acl_list, checklist); |
e7a22b88 | 1547 | if (checklist->state[ACL_DST_IP] == ACL_LOOKUP_NEEDED) { |
b6c0e933 | 1548 | checklist->state[ACL_DST_IP] = ACL_LOOKUP_PENDING; |
1549 | ipcache_nbgethostbyname(checklist->request->host, | |
b6c0e933 | 1550 | aclLookupDstIPDone, |
8407afee | 1551 | checklist); |
b6c0e933 | 1552 | return; |
53ad48e6 | 1553 | } else if (checklist->state[ACL_DST_ASN] == ACL_LOOKUP_NEEDED) { |
1554 | checklist->state[ACL_DST_ASN] = ACL_LOOKUP_PENDING; | |
1555 | ipcache_nbgethostbyname(checklist->request->host, | |
1556 | aclLookupDstIPforASNDone, | |
1557 | checklist); | |
1558 | return; | |
e7a22b88 | 1559 | } else if (checklist->state[ACL_SRC_DOMAIN] == ACL_LOOKUP_NEEDED) { |
b6c0e933 | 1560 | checklist->state[ACL_SRC_DOMAIN] = ACL_LOOKUP_PENDING; |
1561 | fqdncache_nbgethostbyaddr(checklist->src_addr, | |
b6c0e933 | 1562 | aclLookupSrcFQDNDone, |
1563 | checklist); | |
1564 | return; | |
e7a22b88 | 1565 | } else if (checklist->state[ACL_DST_DOMAIN] == ACL_LOOKUP_NEEDED) { |
348b2031 | 1566 | ia = ipcacheCheckNumeric(checklist->request->host); |
1567 | if (ia == NULL) { | |
b6c0e933 | 1568 | checklist->state[ACL_DST_DOMAIN] = ACL_LOOKUP_DONE; |
e7a22b88 | 1569 | return; |
75e88d56 | 1570 | } |
348b2031 | 1571 | checklist->dst_addr = ia->in_addrs[0]; |
e7a22b88 | 1572 | checklist->state[ACL_DST_DOMAIN] = ACL_LOOKUP_PENDING; |
348b2031 | 1573 | fqdncache_nbgethostbyaddr(checklist->dst_addr, |
e7a22b88 | 1574 | aclLookupDstFQDNDone, |
1575 | checklist); | |
b6c0e933 | 1576 | return; |
c68e9c6b | 1577 | } else if (checklist->state[ACL_PROXY_AUTH] == ACL_LOOKUP_NEEDED) { |
73e67ee0 | 1578 | debug(28, 3) ("aclCheck: checking password via authenticator\n"); |
c68e9c6b | 1579 | aclLookupProxyAuthStart(checklist); |
1580 | checklist->state[ACL_PROXY_AUTH] = ACL_LOOKUP_PENDING; | |
1581 | return; | |
1582 | } else if (checklist->state[ACL_PROXY_AUTH] == ACL_PROXY_AUTH_NEEDED) { | |
1583 | /* Special case. Client is required to resend the request | |
1584 | * with authentication. The request is denied. | |
1585 | */ | |
1586 | allow = ACCESS_REQ_PROXY_AUTH; | |
1587 | match = -1; | |
3898f57f | 1588 | } |
1589 | #if USE_IDENT | |
1590 | else if (checklist->state[ACL_IDENT] == ACL_LOOKUP_NEEDED) { | |
c68e9c6b | 1591 | debug(28, 3) ("aclCheck: Doing ident lookup\n"); |
05832ae1 | 1592 | if (cbdataValid(checklist->conn)) { |
1593 | identStart(&checklist->conn->me, &checklist->conn->peer, | |
1594 | aclLookupIdentDone, checklist); | |
1595 | checklist->state[ACL_IDENT] = ACL_LOOKUP_PENDING; | |
1596 | return; | |
1597 | } else { | |
1598 | debug(28, 1) ("aclCheck: Can't start ident lookup. No client connection\n"); | |
1599 | cbdataUnlock(checklist->conn); | |
1600 | checklist->conn = NULL; | |
1601 | allow = 0; | |
1602 | match = -1; | |
1603 | } | |
73e67ee0 | 1604 | } |
3898f57f | 1605 | #endif |
e71209ce | 1606 | /* |
1607 | * We are done with this _acl_access entry. Either the request | |
c68e9c6b | 1608 | * is allowed, denied, requires authentication, or we move on to |
1609 | * the next entry. | |
e71209ce | 1610 | */ |
1611 | cbdataUnlock(A); | |
75e88d56 | 1612 | if (match) { |
a3d5953d | 1613 | debug(28, 3) ("aclCheck: match found, returning %d\n", allow); |
b6c0e933 | 1614 | aclCheckCallback(checklist, allow); |
75e88d56 | 1615 | return; |
8213067d | 1616 | } |
b6c0e933 | 1617 | checklist->access_list = A->next; |
e71209ce | 1618 | /* |
1619 | * Lock the next _acl_access entry | |
1620 | */ | |
1621 | if (A->next) | |
1622 | cbdataLock(A->next); | |
7dd57fa2 | 1623 | } |
a3d5953d | 1624 | debug(28, 3) ("aclCheck: NO match found, returning %d\n", !allow); |
75e88d56 | 1625 | aclCheckCallback(checklist, !allow); |
1626 | } | |
1627 | ||
348b2031 | 1628 | void |
1629 | aclChecklistFree(aclCheck_t * checklist) | |
75e88d56 | 1630 | { |
e7a22b88 | 1631 | if (checklist->state[ACL_SRC_DOMAIN] == ACL_LOOKUP_PENDING) |
b69f7771 | 1632 | fqdncacheUnregister(checklist->src_addr, checklist); |
e7a22b88 | 1633 | if (checklist->state[ACL_DST_DOMAIN] == ACL_LOOKUP_PENDING) |
348b2031 | 1634 | fqdncacheUnregister(checklist->dst_addr, checklist); |
8407afee | 1635 | if (checklist->state[ACL_DST_IP] == ACL_LOOKUP_PENDING) |
365e5b34 | 1636 | ipcacheUnregister(checklist->request->host, checklist); |
0856c124 | 1637 | if (checklist->request) |
2ac76861 | 1638 | requestUnlink(checklist->request); |
8407afee | 1639 | checklist->request = NULL; |
3898f57f | 1640 | #if USE_IDENT |
05832ae1 | 1641 | if (checklist->conn) { |
1642 | cbdataUnlock(checklist->conn); | |
1643 | checklist->conn = NULL; | |
1644 | } | |
3898f57f | 1645 | #endif |
8407afee | 1646 | cbdataFree(checklist); |
75e88d56 | 1647 | } |
1648 | ||
348b2031 | 1649 | static void |
fc5d6f7f | 1650 | aclCheckCallback(aclCheck_t * checklist, allow_t answer) |
348b2031 | 1651 | { |
a3d5953d | 1652 | debug(28, 3) ("aclCheckCallback: answer=%d\n", answer); |
8407afee | 1653 | if (cbdataValid(checklist->callback_data)) |
365e5b34 | 1654 | checklist->callback(answer, checklist->callback_data); |
8407afee | 1655 | cbdataUnlock(checklist->callback_data); |
348b2031 | 1656 | checklist->callback = NULL; |
1657 | checklist->callback_data = NULL; | |
1658 | aclChecklistFree(checklist); | |
1659 | } | |
1660 | ||
3898f57f | 1661 | #if USE_IDENT |
05832ae1 | 1662 | static void |
1663 | aclLookupIdentDone(const char *ident, void *data) | |
1664 | { | |
1665 | aclCheck_t *checklist = data; | |
1666 | if (ident) { | |
1667 | xstrncpy(checklist->ident, ident, sizeof(checklist->ident)); | |
1668 | xstrncpy(checklist->request->user_ident, ident, sizeof(checklist->request->user_ident)); | |
1669 | } else { | |
1670 | xstrncpy(checklist->ident, "-", sizeof(checklist->ident)); | |
1671 | } | |
3898f57f | 1672 | /* |
1673 | * Cache the ident result in the connection, to avoid redoing ident lookup | |
05832ae1 | 1674 | * over and over on persistent connections |
1675 | */ | |
3898f57f | 1676 | if (cbdataValid(checklist->conn) && !checklist->conn->ident[0]) |
05832ae1 | 1677 | xstrncpy(checklist->conn->ident, checklist->ident, sizeof(checklist->conn->ident)); |
1678 | aclCheck(checklist); | |
1679 | } | |
3898f57f | 1680 | #endif |
05832ae1 | 1681 | |
75e88d56 | 1682 | static void |
03a1ee42 | 1683 | aclLookupDstIPDone(const ipcache_addrs * ia, void *data) |
75e88d56 | 1684 | { |
b6c0e933 | 1685 | aclCheck_t *checklist = data; |
1686 | checklist->state[ACL_DST_IP] = ACL_LOOKUP_DONE; | |
1687 | aclCheck(checklist); | |
1969665d | 1688 | } |
73e67ee0 | 1689 | |
53ad48e6 | 1690 | static void |
1691 | aclLookupDstIPforASNDone(const ipcache_addrs * ia, void *data) | |
1692 | { | |
1693 | aclCheck_t *checklist = data; | |
1694 | checklist->state[ACL_DST_ASN] = ACL_LOOKUP_DONE; | |
1695 | aclCheck(checklist); | |
1696 | } | |
92a6f4b1 | 1697 | |
75e88d56 | 1698 | static void |
348b2031 | 1699 | aclLookupSrcFQDNDone(const char *fqdn, void *data) |
75e88d56 | 1700 | { |
b6c0e933 | 1701 | aclCheck_t *checklist = data; |
1702 | checklist->state[ACL_SRC_DOMAIN] = ACL_LOOKUP_DONE; | |
1703 | aclCheck(checklist); | |
75e88d56 | 1704 | } |
1705 | ||
1706 | static void | |
348b2031 | 1707 | aclLookupDstFQDNDone(const char *fqdn, void *data) |
75e88d56 | 1708 | { |
b6c0e933 | 1709 | aclCheck_t *checklist = data; |
e7a22b88 | 1710 | checklist->state[ACL_DST_DOMAIN] = ACL_LOOKUP_DONE; |
75e88d56 | 1711 | aclCheck(checklist); |
1712 | } | |
1713 | ||
73e67ee0 | 1714 | static void |
c68e9c6b | 1715 | aclLookupProxyAuthDone(void *data, char *result) |
73e67ee0 | 1716 | { |
1717 | aclCheck_t *checklist = data; | |
1718 | checklist->state[ACL_PROXY_AUTH] = ACL_LOOKUP_DONE; | |
c68e9c6b | 1719 | debug(28, 4) ("aclLookupProxyAuthDone: result = %s\n", |
1720 | result ? result : "NULL"); | |
73e67ee0 | 1721 | if (result && (strncasecmp(result, "OK", 2) == 0)) |
1722 | checklist->auth_user->passwd_ok = 1; | |
1723 | else | |
c68e9c6b | 1724 | checklist->auth_user->passwd_ok = 0; |
73e67ee0 | 1725 | aclCheck(checklist); |
1726 | } | |
1727 | ||
348b2031 | 1728 | aclCheck_t * |
7342cdc8 | 1729 | aclChecklistCreate(const acl_access * A, |
b6c0e933 | 1730 | request_t * request, |
99edd1c3 | 1731 | const char *ident) |
75e88d56 | 1732 | { |
53ad48e6 | 1733 | int i; |
c68e9c6b | 1734 | aclCheck_t *checklist = memAllocate(MEM_ACLCHECK_T); |
db1cd23c | 1735 | cbdataAdd(checklist, memFree, MEM_ACLCHECK_T); |
b6c0e933 | 1736 | checklist->access_list = A; |
e71209ce | 1737 | /* |
1738 | * aclCheck() makes sure checklist->access_list is a valid | |
1739 | * pointer, so lock it. | |
1740 | */ | |
1741 | cbdataLock(A); | |
7e3ce7b9 | 1742 | if (request != NULL) { |
2ac76861 | 1743 | checklist->request = requestLink(request); |
7e3ce7b9 | 1744 | checklist->src_addr = request->client_addr; |
1745 | checklist->my_addr = request->my_addr; | |
1746 | checklist->my_port = request->my_port; | |
1747 | } | |
53ad48e6 | 1748 | for (i = 0; i < ACL_ENUM_MAX; i++) |
1749 | checklist->state[i] = ACL_LOOKUP_NONE; | |
3898f57f | 1750 | #if USE_IDENT |
b6c0e933 | 1751 | if (ident) |
def67559 | 1752 | xstrncpy(checklist->ident, ident, USER_IDENT_SZ); |
3898f57f | 1753 | #endif |
73e67ee0 | 1754 | checklist->auth_user = NULL; /* init to NULL */ |
348b2031 | 1755 | return checklist; |
1756 | } | |
1757 | ||
1758 | void | |
1759 | aclNBCheck(aclCheck_t * checklist, PF callback, void *callback_data) | |
1760 | { | |
b6c0e933 | 1761 | checklist->callback = callback; |
1762 | checklist->callback_data = callback_data; | |
8407afee | 1763 | cbdataLock(callback_data); |
b6c0e933 | 1764 | aclCheck(checklist); |
75e88d56 | 1765 | } |
1766 | ||
1767 | ||
1768 | ||
1769 | ||
1770 | ||
1771 | ||
1772 | ||
f32789f4 | 1773 | /*********************/ |
1774 | /* Destroy functions */ | |
1775 | /*********************/ | |
1776 | ||
8203a132 | 1777 | static void |
7342cdc8 | 1778 | aclDestroyTimeList(acl_time_data * data) |
92a6f4b1 | 1779 | { |
7342cdc8 | 1780 | acl_time_data *next = NULL; |
540830c4 | 1781 | for (; data; data = next) { |
1782 | next = data->next; | |
db1cd23c | 1783 | memFree(data, MEM_ACL_TIME_DATA); |
540830c4 | 1784 | } |
92a6f4b1 | 1785 | } |
1786 | ||
33cdd606 | 1787 | void |
7342cdc8 | 1788 | aclDestroyRegexList(relist * data) |
92a6f4b1 | 1789 | { |
7342cdc8 | 1790 | relist *next = NULL; |
540830c4 | 1791 | for (; data; data = next) { |
1792 | next = data->next; | |
1793 | regfree(&data->regex); | |
1794 | safe_free(data->pattern); | |
db1cd23c | 1795 | memFree(data, MEM_RELIST); |
540830c4 | 1796 | } |
92a6f4b1 | 1797 | } |
1798 | ||
ec878047 | 1799 | static void |
1800 | aclFreeProxyAuthUser(void *data) | |
1801 | { | |
1802 | acl_proxy_auth_user *u = data; | |
1803 | xfree(u->user); | |
1804 | xfree(u->passwd); | |
db1cd23c | 1805 | memFree(u, MEM_ACL_PROXY_AUTH_USER); |
ec878047 | 1806 | } |
1807 | ||
afe95a7e | 1808 | static void |
c68e9c6b | 1809 | aclFreeIpData(void *p) |
afe95a7e | 1810 | { |
db1cd23c | 1811 | memFree(p, MEM_ACL_IP_DATA); |
afe95a7e | 1812 | } |
1813 | ||
8203a132 | 1814 | void |
a47b9029 | 1815 | aclDestroyAcls(acl ** head) |
540830c4 | 1816 | { |
7342cdc8 | 1817 | acl *a = NULL; |
1818 | acl *next = NULL; | |
f1dc9b30 | 1819 | for (a = *head; a; a = next) { |
540830c4 | 1820 | next = a->next; |
a3d5953d | 1821 | debug(28, 3) ("aclDestroyAcls: '%s'\n", a->cfgline); |
540830c4 | 1822 | switch (a->type) { |
1823 | case ACL_SRC_IP: | |
30a4f2a8 | 1824 | case ACL_DST_IP: |
ae2c08a2 | 1825 | case ACL_MY_IP: |
c68e9c6b | 1826 | splay_destroy(a->data, aclFreeIpData); |
540830c4 | 1827 | break; |
c68e9c6b | 1828 | case ACL_SRC_ARP: |
540830c4 | 1829 | case ACL_DST_DOMAIN: |
c20e73a0 | 1830 | case ACL_SRC_DOMAIN: |
f32789f4 | 1831 | splay_destroy(a->data, xfree); |
f32789f4 | 1832 | break; |
dba79ac5 | 1833 | #if SQUID_SNMP |
1834 | case ACL_SNMP_COMMUNITY: | |
1835 | #endif | |
3898f57f | 1836 | #if USE_IDENT |
c68e9c6b | 1837 | case ACL_IDENT: |
3898f57f | 1838 | #endif |
c68e9c6b | 1839 | case ACL_PROXY_AUTH: |
540830c4 | 1840 | wordlistDestroy((wordlist **) & a->data); |
1841 | break; | |
1842 | case ACL_TIME: | |
1843 | aclDestroyTimeList(a->data); | |
1844 | break; | |
1845 | case ACL_URL_REGEX: | |
6e40f263 | 1846 | case ACL_URLPATH_REGEX: |
6b8e7481 | 1847 | case ACL_BROWSER: |
71eb5c70 | 1848 | case ACL_SRC_DOM_REGEX: |
1849 | case ACL_DST_DOM_REGEX: | |
540830c4 | 1850 | aclDestroyRegexList(a->data); |
1851 | break; | |
540830c4 | 1852 | case ACL_PROTO: |
1853 | case ACL_METHOD: | |
9fdbeb4f | 1854 | case ACL_SRC_ASN: |
1855 | case ACL_DST_ASN: | |
6b8e7481 | 1856 | case ACL_NETDB_SRC_RTT: |
9bc73deb | 1857 | case ACL_MAXCONN: |
540830c4 | 1858 | intlistDestroy((intlist **) & a->data); |
1859 | break; | |
8f663d72 | 1860 | case ACL_URL_PORT: |
7e3ce7b9 | 1861 | case ACL_MY_PORT: |
8f663d72 | 1862 | aclDestroyIntRange(a->data); |
1863 | break; | |
540830c4 | 1864 | case ACL_NONE: |
1865 | default: | |
f6308664 | 1866 | debug(28, 1) ("aclDestroyAcls: no case for ACL type %d\n", a->type); |
540830c4 | 1867 | break; |
92a6f4b1 | 1868 | } |
540830c4 | 1869 | safe_free(a->cfgline); |
db1cd23c | 1870 | memFree(a, MEM_ACL); |
540830c4 | 1871 | } |
f1dc9b30 | 1872 | *head = NULL; |
92a6f4b1 | 1873 | } |
1874 | ||
8203a132 | 1875 | static void |
7342cdc8 | 1876 | aclDestroyAclList(acl_list * list) |
92a6f4b1 | 1877 | { |
7342cdc8 | 1878 | acl_list *next = NULL; |
540830c4 | 1879 | for (; list; list = next) { |
1880 | next = list->next; | |
db1cd23c | 1881 | memFree(list, MEM_ACL_LIST); |
540830c4 | 1882 | } |
92a6f4b1 | 1883 | } |
1884 | ||
8203a132 | 1885 | void |
7342cdc8 | 1886 | aclDestroyAccessList(acl_access ** list) |
92a6f4b1 | 1887 | { |
7342cdc8 | 1888 | acl_access *l = NULL; |
1889 | acl_access *next = NULL; | |
540830c4 | 1890 | for (l = *list; l; l = next) { |
a3d5953d | 1891 | debug(28, 3) ("aclDestroyAccessList: '%s'\n", l->cfgline); |
540830c4 | 1892 | next = l->next; |
1893 | aclDestroyAclList(l->acl_list); | |
1894 | l->acl_list = NULL; | |
1895 | safe_free(l->cfgline); | |
e71209ce | 1896 | cbdataFree(l); |
540830c4 | 1897 | } |
1898 | *list = NULL; | |
92a6f4b1 | 1899 | } |
e92d33a5 | 1900 | |
1901 | /* maex@space.net (06.09.1996) | |
f4296e99 | 1902 | * destroy an _acl_deny_info_list */ |
f32789f4 | 1903 | |
8203a132 | 1904 | void |
7342cdc8 | 1905 | aclDestroyDenyInfoList(acl_deny_info_list ** list) |
e92d33a5 | 1906 | { |
7342cdc8 | 1907 | acl_deny_info_list *a = NULL; |
1908 | acl_deny_info_list *a_next = NULL; | |
1909 | acl_name_list *l = NULL; | |
1910 | acl_name_list *l_next = NULL; | |
e92d33a5 | 1911 | |
1912 | for (a = *list; a; a = a_next) { | |
1913 | for (l = a->acl_list; l; l = l_next) { | |
1914 | l_next = l->next; | |
1915 | safe_free(l); | |
1916 | } | |
1917 | a_next = a->next; | |
02922e76 | 1918 | xfree(a->err_page_name); |
e92d33a5 | 1919 | safe_free(a); |
1920 | } | |
f4296e99 | 1921 | *list = NULL; |
e92d33a5 | 1922 | } |
f32789f4 | 1923 | |
8f663d72 | 1924 | static void |
5942e8d4 | 1925 | aclDestroyIntRange(intrange * list) |
8f663d72 | 1926 | { |
1927 | intrange *w = NULL; | |
1928 | intrange *n = NULL; | |
1929 | for (w = list; w; w = n) { | |
5942e8d4 | 1930 | n = w->next; |
1931 | safe_free(w); | |
8f663d72 | 1932 | } |
1933 | } | |
1934 | ||
f32789f4 | 1935 | /* general compare functions, these are used for tree search algorithms |
1936 | * so they return <0, 0 or >0 */ | |
1937 | ||
1938 | /* compare two domains */ | |
1939 | ||
f32789f4 | 1940 | static int |
9bc73deb | 1941 | aclDomainCompare(const void *a, const void *b) |
f32789f4 | 1942 | { |
9bc73deb | 1943 | const char *d1 = a; |
1944 | const char *d2 = b; | |
0fa38583 | 1945 | int l1; |
1946 | int l2; | |
1947 | while ('.' == *d1) | |
1948 | d1++; | |
1949 | while ('.' == *d2) | |
1950 | d2++; | |
1951 | l1 = strlen(d1); | |
1952 | l2 = strlen(d2); | |
1f38f50a | 1953 | while (d1[--l1] == d2[--l2]) { |
f32789f4 | 1954 | if ((l1 == 0) && (l2 == 0)) |
1955 | return 0; /* d1 == d2 */ | |
0fa38583 | 1956 | if (0 == l1) { |
1957 | if ('.' == d2[l2 - 1]) { | |
8c540630 | 1958 | debug(28, 0) ("WARNING: %s is a subdomain of %s\n", d2, d1); |
0fa38583 | 1959 | debug(28, 0) ("WARNING: This may break Splay tree searching\n"); |
1960 | debug(28, 0) ("WARNING: You should remove '%s' from the ACL named '%s'\n", d2, AclMatchedName); | |
1961 | } | |
f32789f4 | 1962 | return -1; /* d1 < d2 */ |
b6a2f15e | 1963 | } |
0fa38583 | 1964 | if (0 == l2) { |
1965 | if ('.' == d1[l1 - 1]) { | |
8c540630 | 1966 | debug(28, 0) ("WARNING: %s is a subdomain of %s\n", d1, d2); |
0fa38583 | 1967 | debug(28, 0) ("WARNING: This may break Splay tree searching\n"); |
1968 | debug(28, 0) ("WARNING: You should remove '%s' from the ACL named '%s'\n", d1, AclMatchedName); | |
1969 | } | |
f32789f4 | 1970 | return 1; /* d1 > d2 */ |
b6a2f15e | 1971 | } |
f32789f4 | 1972 | } |
1973 | return (d1[l1] - d2[l2]); | |
1974 | } | |
1975 | ||
f32789f4 | 1976 | /* compare a host and a domain */ |
1977 | ||
f32789f4 | 1978 | static int |
9bc73deb | 1979 | aclHostDomainCompare(const void *a, const void *b) |
f32789f4 | 1980 | { |
9bc73deb | 1981 | const char *h = a; |
1982 | const char *d = b; | |
7e3ce7b9 | 1983 | return matchDomainName(h, d); |
f32789f4 | 1984 | } |
1985 | ||
f32789f4 | 1986 | /* compare two network specs |
1987 | * | |
1988 | * NOTE: this is very similar to aclIpNetworkCompare and it's not yet | |
1989 | * clear whether this OK. The problem could be with when a network | |
1990 | * is a subset of the other networks: | |
1991 | * | |
1992 | * 128.1.2.0/255.255.255.128 == 128.1.2.0/255.255.255.0 ? | |
1993 | * | |
1994 | * Currently only the first address of the first network is used. | |
1995 | */ | |
1996 | ||
f32789f4 | 1997 | /* compare an address and a network spec */ |
1998 | ||
f32789f4 | 1999 | static int |
9bc73deb | 2000 | aclIpNetworkCompare(const void *a, const void *b) |
f32789f4 | 2001 | { |
9bc73deb | 2002 | struct in_addr A = *(const struct in_addr *) a; |
2003 | const acl_ip_data *q = b; | |
2004 | const struct in_addr B = q->addr1; | |
2005 | const struct in_addr C = q->addr2; | |
f32789f4 | 2006 | int rc = 0; |
429fdbec | 2007 | A.s_addr &= q->mask.s_addr; /* apply netmask */ |
f32789f4 | 2008 | if (C.s_addr == 0) { /* single address check */ |
429fdbec | 2009 | if (ntohl(A.s_addr) > ntohl(B.s_addr)) |
f32789f4 | 2010 | rc = 1; |
429fdbec | 2011 | else if (ntohl(A.s_addr) < ntohl(B.s_addr)) |
f32789f4 | 2012 | rc = -1; |
2013 | else | |
2014 | rc = 0; | |
2015 | } else { /* range address check */ | |
429fdbec | 2016 | if (ntohl(A.s_addr) > ntohl(C.s_addr)) |
f32789f4 | 2017 | rc = 1; |
429fdbec | 2018 | else if (ntohl(A.s_addr) < ntohl(B.s_addr)) |
f32789f4 | 2019 | rc = -1; |
2020 | else | |
2021 | rc = 0; | |
2022 | } | |
2023 | return rc; | |
2024 | } | |
f32789f4 | 2025 | |
f084f6af | 2026 | static void |
2027 | aclDumpIpListWalkee(void *node, void *state) | |
2028 | { | |
2029 | acl_ip_data *ip = node; | |
2030 | MemBuf mb; | |
2031 | wordlist **W = state; | |
f084f6af | 2032 | memBufDefInit(&mb); |
2033 | memBufPrintf(&mb, "%s", inet_ntoa(ip->addr1)); | |
2034 | if (ip->addr2.s_addr != any_addr.s_addr) | |
2035 | memBufPrintf(&mb, "-%s", inet_ntoa(ip->addr2)); | |
2036 | if (ip->mask.s_addr != no_addr.s_addr) | |
2037 | memBufPrintf(&mb, "/%s", inet_ntoa(ip->mask)); | |
c68e9c6b | 2038 | wordlistAdd(W, mb.buf); |
f084f6af | 2039 | memBufClean(&mb); |
2040 | } | |
f32789f4 | 2041 | |
56b63fa1 | 2042 | static wordlist * |
e82d6d21 | 2043 | aclDumpIpList(void *data) |
56b63fa1 | 2044 | { |
f084f6af | 2045 | wordlist *w = NULL; |
2046 | splay_walk(data, aclDumpIpListWalkee, &w); | |
2047 | return w; | |
2048 | } | |
2049 | ||
2050 | static void | |
2051 | aclDumpDomainListWalkee(void *node, void *state) | |
2052 | { | |
2053 | char *domain = node; | |
c68e9c6b | 2054 | wordlistAdd(state, domain); |
56b63fa1 | 2055 | } |
2056 | ||
2057 | static wordlist * | |
2058 | aclDumpDomainList(void *data) | |
2059 | { | |
f084f6af | 2060 | wordlist *w = NULL; |
2061 | splay_walk(data, aclDumpDomainListWalkee, &w); | |
2062 | return w; | |
56b63fa1 | 2063 | } |
7342cdc8 | 2064 | |
56b63fa1 | 2065 | static wordlist * |
7342cdc8 | 2066 | aclDumpTimeSpecList(acl_time_data * t) |
f511ab6f | 2067 | { |
f511ab6f | 2068 | wordlist *W = NULL; |
f511ab6f | 2069 | char buf[128]; |
2070 | while (t != NULL) { | |
137ee196 | 2071 | snprintf(buf, sizeof(buf), "%c%c%c%c%c%c%c %02d:%02d-%02d:%02d", |
16300b58 | 2072 | t->weekbits & ACL_SUNDAY ? 'S' : '-', |
2073 | t->weekbits & ACL_MONDAY ? 'M' : '-', | |
2074 | t->weekbits & ACL_TUESDAY ? 'T' : '-', | |
2075 | t->weekbits & ACL_WEDNESDAY ? 'W' : '-', | |
2076 | t->weekbits & ACL_THURSDAY ? 'H' : '-', | |
2077 | t->weekbits & ACL_FRIDAY ? 'F' : '-', | |
2078 | t->weekbits & ACL_SATURDAY ? 'A' : '-', | |
2079 | t->start / 60, | |
2080 | t->start % 60, | |
2081 | t->stop / 60, | |
2082 | t->stop % 60); | |
c68e9c6b | 2083 | wordlistAdd(&W, buf); |
f511ab6f | 2084 | t = t->next; |
16300b58 | 2085 | } |
2086 | return W; | |
56b63fa1 | 2087 | } |
7342cdc8 | 2088 | |
56b63fa1 | 2089 | static wordlist * |
7342cdc8 | 2090 | aclDumpRegexList(relist * data) |
56b63fa1 | 2091 | { |
7342cdc8 | 2092 | wordlist *W = NULL; |
7342cdc8 | 2093 | while (data != NULL) { |
c68e9c6b | 2094 | wordlistAdd(&W, data->pattern); |
7342cdc8 | 2095 | data = data->next; |
2096 | } | |
2097 | return W; | |
56b63fa1 | 2098 | } |
7342cdc8 | 2099 | |
56b63fa1 | 2100 | static wordlist * |
7342cdc8 | 2101 | aclDumpIntlistList(intlist * data) |
56b63fa1 | 2102 | { |
7342cdc8 | 2103 | wordlist *W = NULL; |
7342cdc8 | 2104 | char buf[32]; |
2105 | while (data != NULL) { | |
137ee196 | 2106 | snprintf(buf, sizeof(buf), "%d", data->i); |
c68e9c6b | 2107 | wordlistAdd(&W, buf); |
7342cdc8 | 2108 | data = data->next; |
2109 | } | |
2110 | return W; | |
56b63fa1 | 2111 | } |
7342cdc8 | 2112 | |
8f663d72 | 2113 | static wordlist * |
2114 | aclDumpIntRangeList(intrange * data) | |
2115 | { | |
2116 | wordlist *W = NULL; | |
8f663d72 | 2117 | char buf[32]; |
2118 | while (data != NULL) { | |
8f663d72 | 2119 | if (data->i == data->j) |
2120 | snprintf(buf, sizeof(buf), "%d", data->i); | |
2121 | else | |
2122 | snprintf(buf, sizeof(buf), "%d-%d", data->i, data->j); | |
c68e9c6b | 2123 | wordlistAdd(&W, buf); |
8f663d72 | 2124 | data = data->next; |
2125 | } | |
2126 | return W; | |
2127 | } | |
2128 | ||
56b63fa1 | 2129 | static wordlist * |
7342cdc8 | 2130 | aclDumpProtoList(intlist * data) |
56b63fa1 | 2131 | { |
7342cdc8 | 2132 | wordlist *W = NULL; |
7342cdc8 | 2133 | while (data != NULL) { |
c68e9c6b | 2134 | wordlistAdd(&W, ProtocolStr[data->i]); |
7342cdc8 | 2135 | data = data->next; |
2136 | } | |
2137 | return W; | |
56b63fa1 | 2138 | } |
7342cdc8 | 2139 | |
56b63fa1 | 2140 | static wordlist * |
7342cdc8 | 2141 | aclDumpMethodList(intlist * data) |
56b63fa1 | 2142 | { |
7342cdc8 | 2143 | wordlist *W = NULL; |
7342cdc8 | 2144 | while (data != NULL) { |
c68e9c6b | 2145 | wordlistAdd(&W, RequestMethodStr[data->i]); |
7342cdc8 | 2146 | data = data->next; |
2147 | } | |
2148 | return W; | |
56b63fa1 | 2149 | } |
7342cdc8 | 2150 | |
56b63fa1 | 2151 | wordlist * |
2152 | aclDumpGeneric(const acl * a) | |
2153 | { | |
c68e9c6b | 2154 | debug(28, 3) ("aclDumpGeneric: %s type %d\n", a->name, a->type); |
56b63fa1 | 2155 | switch (a->type) { |
2156 | case ACL_SRC_IP: | |
2157 | case ACL_DST_IP: | |
ae2c08a2 | 2158 | case ACL_MY_IP: |
56b63fa1 | 2159 | return aclDumpIpList(a->data); |
2160 | break; | |
2161 | case ACL_SRC_DOMAIN: | |
2162 | case ACL_DST_DOMAIN: | |
6b8e7481 | 2163 | return aclDumpDomainList(a->data); |
2164 | break; | |
dba79ac5 | 2165 | #if SQUID_SNMP |
2166 | case ACL_SNMP_COMMUNITY: | |
2167 | #endif | |
3898f57f | 2168 | #if USE_IDENT |
c68e9c6b | 2169 | case ACL_IDENT: |
3898f57f | 2170 | #endif |
c68e9c6b | 2171 | case ACL_PROXY_AUTH: |
6b8e7481 | 2172 | return wordlistDup(a->data); |
56b63fa1 | 2173 | break; |
2174 | case ACL_TIME: | |
7342cdc8 | 2175 | return aclDumpTimeSpecList(a->data); |
56b63fa1 | 2176 | break; |
2177 | case ACL_URL_REGEX: | |
2178 | case ACL_URLPATH_REGEX: | |
2179 | case ACL_BROWSER: | |
6b8e7481 | 2180 | case ACL_SRC_DOM_REGEX: |
2181 | case ACL_DST_DOM_REGEX: | |
56b63fa1 | 2182 | return aclDumpRegexList(a->data); |
2183 | break; | |
56b63fa1 | 2184 | case ACL_SRC_ASN: |
9bc73deb | 2185 | case ACL_MAXCONN: |
56b63fa1 | 2186 | case ACL_DST_ASN: |
7342cdc8 | 2187 | return aclDumpIntlistList(a->data); |
56b63fa1 | 2188 | break; |
8f663d72 | 2189 | case ACL_URL_PORT: |
7e3ce7b9 | 2190 | case ACL_MY_PORT: |
8f663d72 | 2191 | return aclDumpIntRangeList(a->data); |
2192 | break; | |
56b63fa1 | 2193 | case ACL_PROTO: |
2194 | return aclDumpProtoList(a->data); | |
2195 | break; | |
2196 | case ACL_METHOD: | |
2197 | return aclDumpMethodList(a->data); | |
2198 | break; | |
56b63fa1 | 2199 | #if USE_ARP_ACL |
2200 | case ACL_SRC_ARP: | |
2201 | return aclDumpArpList(a->data); | |
2202 | break; | |
2203 | #endif | |
2204 | case ACL_NONE: | |
2205 | default: | |
6b8e7481 | 2206 | debug(28, 1) ("aclDumpGeneric: no case for ACL type %d\n", a->type); |
56b63fa1 | 2207 | break; |
2208 | } | |
2209 | return NULL; | |
2210 | } | |
2211 | ||
23351cb2 | 2212 | |
2213 | ||
2214 | ||
2215 | #if USE_ARP_ACL | |
2216 | /* ==== BEGIN ARP ACL SUPPORT ============================================= */ | |
2217 | ||
2218 | /* | |
2219 | * From: dale@server.ctam.bitmcnit.bryansk.su (Dale) | |
2220 | * To: wessels@nlanr.net | |
2221 | * Subject: Another Squid patch... :) | |
2222 | * Date: Thu, 04 Dec 1997 19:55:01 +0300 | |
2223 | * ============================================================================ | |
2224 | * | |
2225 | * Working on setting up a proper firewall for a network containing some | |
2226 | * Win'95 computers at our Univ, I've discovered that some smart students | |
2227 | * avoid the restrictions easily just changing their IP addresses in Win'95 | |
2228 | * Contol Panel... It has been getting boring, so I took Squid-1.1.18 | |
2229 | * sources and added a new acl type for hard-wired access control: | |
2230 | * | |
2231 | * acl <name> arp <Ethernet address> ... | |
2232 | * | |
2233 | * For example, | |
2234 | * | |
2235 | * acl students arp 00:00:21:55:ed:22 00:00:21:ff:55:38 | |
bd0c865a | 2236 | * |
2237 | * NOTE: Linux code by David Luyer <luyer@ucs.uwa.edu.au>. | |
2238 | * Original (BSD-specific) code no longer works. | |
23351cb2 | 2239 | */ |
2240 | ||
23351cb2 | 2241 | #include <sys/sysctl.h> |
a931a29b | 2242 | #ifdef _SQUID_LINUX_ |
2243 | #include <net/if_arp.h> | |
2244 | #include <sys/ioctl.h> | |
2245 | #else | |
23351cb2 | 2246 | #include <net/if_dl.h> |
a931a29b | 2247 | #endif |
23351cb2 | 2248 | #include <net/route.h> |
2249 | #include <net/if.h> | |
2250 | #include <netinet/if_ether.h> | |
2251 | ||
2252 | /* | |
2253 | * Decode an ascii representation (asc) of an ethernet adress, and place | |
2254 | * it in eth[6]. | |
2255 | */ | |
2256 | static int | |
2257 | decode_eth(const char *asc, char *eth) | |
2258 | { | |
2259 | int a1 = 0, a2 = 0, a3 = 0, a4 = 0, a5 = 0, a6 = 0; | |
2260 | if (sscanf(asc, "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6) != 6) { | |
53ad48e6 | 2261 | debug(28, 0) ("decode_eth: Invalid ethernet address '%s'\n", asc); |
23351cb2 | 2262 | return 0; /* This is not valid address */ |
2263 | } | |
2264 | eth[0] = (u_char) a1; | |
2265 | eth[1] = (u_char) a2; | |
2266 | eth[2] = (u_char) a3; | |
2267 | eth[3] = (u_char) a4; | |
2268 | eth[4] = (u_char) a5; | |
2269 | eth[5] = (u_char) a6; | |
2270 | return 1; | |
2271 | } | |
2272 | ||
7342cdc8 | 2273 | static acl_arp_data * |
23351cb2 | 2274 | aclParseArpData(const char *t) |
2275 | { | |
bd0c865a | 2276 | LOCAL_ARRAY(char, eth, 256); |
7342cdc8 | 2277 | acl_arp_data *q = xcalloc(1, sizeof(acl_arp_data)); |
53ad48e6 | 2278 | debug(28, 5) ("aclParseArpData: %s\n", t); |
bd0c865a | 2279 | if (sscanf(t, "%[0-9a-fA-F:]", eth) != 1) { |
53ad48e6 | 2280 | debug(28, 0) ("aclParseArpData: Bad ethernet address: '%s'\n", t); |
23351cb2 | 2281 | safe_free(q); |
2282 | return NULL; | |
2283 | } | |
2284 | if (!decode_eth(eth, q->eth)) { | |
53ad48e6 | 2285 | debug(28, 0) ("%s line %d: %s\n", |
23351cb2 | 2286 | cfg_filename, config_lineno, config_input_line); |
bd0c865a | 2287 | debug(28, 0) ("aclParseArpData: Ignoring invalid ARP acl entry: can't parse '%s'\n", eth); |
23351cb2 | 2288 | safe_free(q); |
2289 | return NULL; | |
2290 | } | |
2291 | return q; | |
2292 | } | |
2293 | ||
2294 | ||
2295 | /*******************/ | |
2296 | /* aclParseArpList */ | |
2297 | /*******************/ | |
23351cb2 | 2298 | static void |
2299 | aclParseArpList(void *curlist) | |
2300 | { | |
2301 | char *t = NULL; | |
2302 | splayNode **Top = curlist; | |
7342cdc8 | 2303 | acl_arp_data *q = NULL; |
23351cb2 | 2304 | while ((t = strtokFile())) { |
2305 | if ((q = aclParseArpData(t)) == NULL) | |
2306 | continue; | |
f084f6af | 2307 | *Top = splay_insert(q, *Top, aclArpCompare); |
23351cb2 | 2308 | } |
2309 | } | |
23351cb2 | 2310 | |
2311 | /***************/ | |
2312 | /* aclMatchArp */ | |
2313 | /***************/ | |
f084f6af | 2314 | #ifdef _SQUID_LINUX_ |
23351cb2 | 2315 | static int |
2316 | aclMatchArp(void *dataptr, struct in_addr c) | |
2317 | { | |
f084f6af | 2318 | struct arpreq arpReq; |
2319 | struct sockaddr_in ipAddr; | |
7e3ce7b9 | 2320 | unsigned char ifbuffer[sizeof(struct ifreq) * 64]; |
2321 | struct ifconf ifc; | |
2322 | struct ifreq *ifr; | |
2323 | int offset; | |
23351cb2 | 2324 | splayNode **Top = dataptr; |
7e3ce7b9 | 2325 | /* |
2326 | * The linux kernel 2.2 maintains per interface ARP caches and | |
2327 | * thus requires an interface name when doing ARP queries. | |
2328 | * | |
2329 | * The older 2.0 kernels appear to use a unified ARP cache, | |
2330 | * and require an empty interface name | |
2331 | * | |
2332 | * To support both, we attempt the lookup with a blank interface | |
2333 | * name first. If that does not succeed, the try each interface | |
2334 | * in turn | |
2335 | */ | |
2336 | /* | |
2337 | * Set up structures for ARP lookup with blank interface name | |
2338 | */ | |
f084f6af | 2339 | ipAddr.sin_family = AF_INET; |
2340 | ipAddr.sin_port = 0; | |
2341 | ipAddr.sin_addr = c; | |
7e3ce7b9 | 2342 | memset(&arpReq, '\0', sizeof(arpReq)); |
f084f6af | 2343 | memcpy(&arpReq.arp_pa, &ipAddr, sizeof(struct sockaddr_in)); |
7e3ce7b9 | 2344 | /* Query ARP table */ |
2345 | if (ioctl(HttpSockets[0], SIOCGARP, &arpReq) != -1) { | |
2346 | /* Skip non-ethernet interfaces */ | |
2347 | if (arpReq.arp_ha.sa_family != ARPHRD_ETHER) { | |
2348 | return 0; | |
2349 | } | |
2350 | debug(28, 4) ("Got address %02x:%02x:%02x:%02x:%02x:%02x\n", | |
2351 | arpReq.arp_ha.sa_data[0] & 0xff, arpReq.arp_ha.sa_data[1] & 0xff, | |
2352 | arpReq.arp_ha.sa_data[2] & 0xff, arpReq.arp_ha.sa_data[3] & 0xff, | |
2353 | arpReq.arp_ha.sa_data[4] & 0xff, arpReq.arp_ha.sa_data[5] & 0xff); | |
2354 | /* Do lookup */ | |
2355 | *Top = splay_splay(&arpReq.arp_ha.sa_data, *Top, aclArpCompare); | |
2356 | debug(28, 3) ("aclMatchArp: '%s' %s\n", | |
2357 | inet_ntoa(c), splayLastResult ? "NOT found" : "found"); | |
2358 | return (0 == splayLastResult); | |
2359 | } | |
2360 | /* lookup list of interface names */ | |
2361 | ifc.ifc_len = sizeof(ifbuffer); | |
2362 | ifc.ifc_buf = ifbuffer; | |
2363 | if (ioctl(HttpSockets[0], SIOCGIFCONF, &ifc) < 0) { | |
2364 | debug(28, 1) ("Attempt to retrieve interface list failed: %s\n", | |
2365 | xstrerror()); | |
f084f6af | 2366 | return 0; |
7e3ce7b9 | 2367 | } |
2368 | if (ifc.ifc_len > sizeof(ifbuffer)) { | |
2369 | debug(28, 1) ("Interface list too long - %d\n", ifc.ifc_len); | |
f084f6af | 2370 | return 0; |
7e3ce7b9 | 2371 | } |
2372 | /* Attempt ARP lookup on each interface */ | |
2373 | offset = 0; | |
2374 | while (offset < ifc.ifc_len) { | |
2375 | ifr = (struct ifreq *) (ifbuffer + offset); | |
2376 | offset += sizeof(*ifr); | |
2377 | /* Skip loopback and aliased interfaces */ | |
2378 | if (0 == strncmp(ifr->ifr_name, "lo", 2)) | |
2379 | continue; | |
2380 | if (NULL != strchr(ifr->ifr_name, ':')) | |
2381 | continue; | |
2382 | debug(28, 4) ("Looking up ARP address for %s on %s\n", inet_ntoa(c), | |
2383 | ifr->ifr_name); | |
2384 | /* Set up structures for ARP lookup */ | |
2385 | ipAddr.sin_family = AF_INET; | |
2386 | ipAddr.sin_port = 0; | |
2387 | ipAddr.sin_addr = c; | |
2388 | memset(&arpReq, '\0', sizeof(arpReq)); | |
2389 | memcpy(&arpReq.arp_pa, &ipAddr, sizeof(struct sockaddr_in)); | |
2390 | strncpy(arpReq.arp_dev, ifr->ifr_name, sizeof(arpReq.arp_dev) - 1); | |
2391 | arpReq.arp_dev[sizeof(arpReq.arp_dev) - 1] = '\0'; | |
2392 | /* Query ARP table */ | |
2393 | if (-1 == ioctl(HttpSockets[0], SIOCGARP, &arpReq)) { | |
2394 | /* | |
2395 | * Query failed. Do not log failed lookups or "device | |
2396 | * not supported" | |
2397 | */ | |
2398 | if (ENXIO == errno) | |
2399 | (void) 0; | |
2400 | else if (ENODEV == errno) | |
2401 | (void) 0; | |
2402 | else | |
2403 | debug(28, 1) ("ARP query failed: %s: %s\n", | |
2404 | ifr->ifr_name, xstrerror()); | |
2405 | continue; | |
2406 | } | |
2407 | /* Skip non-ethernet interfaces */ | |
2408 | if (arpReq.arp_ha.sa_family != ARPHRD_ETHER) | |
2409 | continue; | |
2410 | debug(28, 4) ("Got address %02x:%02x:%02x:%02x:%02x:%02x on %s\n", | |
2411 | arpReq.arp_ha.sa_data[0] & 0xff, | |
2412 | arpReq.arp_ha.sa_data[1] & 0xff, | |
2413 | arpReq.arp_ha.sa_data[2] & 0xff, | |
2414 | arpReq.arp_ha.sa_data[3] & 0xff, | |
2415 | arpReq.arp_ha.sa_data[4] & 0xff, | |
2416 | arpReq.arp_ha.sa_data[5] & 0xff, | |
2417 | ifr->ifr_name); | |
2418 | /* Do lookup */ | |
f084f6af | 2419 | *Top = splay_splay(&arpReq.arp_ha.sa_data, *Top, aclArpCompare); |
7e3ce7b9 | 2420 | /* Return if match, otherwise continue to other interfaces */ |
2421 | if (0 == splayLastResult) { | |
2422 | debug(28, 3) ("aclMatchArp: %s found on %s\n", | |
2423 | inet_ntoa(c), ifr->ifr_name); | |
2424 | return 1; | |
2425 | } | |
2426 | /* | |
2427 | * Should we stop looking here? Can the same IP address | |
2428 | * exist on multiple interfaces? | |
2429 | */ | |
2430 | } | |
2431 | /* | |
2432 | * Address was not found on any interface | |
2433 | */ | |
2434 | debug(28, 3) ("aclMatchArp: %s NOT found\n", inet_ntoa(c)); | |
2435 | return 0; | |
23351cb2 | 2436 | } |
23351cb2 | 2437 | |
f084f6af | 2438 | static int |
9bc73deb | 2439 | aclArpCompare(const void *a, const void *b) |
f084f6af | 2440 | { |
9bc73deb | 2441 | const unsigned short *d1 = a; |
2442 | const unsigned short *d2 = b; | |
e82d6d21 | 2443 | if (d1[0] != d2[0]) |
2444 | return (d1[0] > d2[0]) ? 1 : -1; | |
2445 | if (d1[1] != d2[1]) | |
2446 | return (d1[1] > d2[1]) ? 1 : -1; | |
2447 | if (d1[2] != d2[2]) | |
2448 | return (d1[2] > d2[2]) ? 1 : -1; | |
f084f6af | 2449 | return 0; |
2450 | } | |
a931a29b | 2451 | #else |
f084f6af | 2452 | |
2453 | static int | |
2454 | aclMatchArp(void *dataptr, struct in_addr c) | |
2455 | { | |
e82d6d21 | 2456 | WRITE ME; |
f084f6af | 2457 | } |
2458 | ||
2459 | static int | |
2460 | aclArpCompare(const void *data, splayNode * n) | |
2461 | { | |
e82d6d21 | 2462 | WRITE ME; |
f084f6af | 2463 | } |
2464 | ||
bd0c865a | 2465 | /********************************************************************** |
2466 | * This is from the pre-splay-tree code for BSD | |
2467 | * I suspect the Linux approach will work on most O/S and be much | |
2468 | * better - <luyer@ucs.uwa.edu.au> | |
2469 | *********************************************************************** | |
23351cb2 | 2470 | static int |
2471 | checkARP(u_long ip, char *eth) | |
2472 | { | |
2473 | int mib[6] = | |
2474 | {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO}; | |
2475 | size_t needed; | |
2476 | char *buf, *next, *lim; | |
2477 | struct rt_msghdr *rtm; | |
2478 | struct sockaddr_inarp *sin; | |
2479 | struct sockaddr_dl *sdl; | |
2480 | if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) { | |
ee89e05a | 2481 | debug(28, 0) ("Can't estimate ARP table size!\n"); |
23351cb2 | 2482 | return 0; |
2483 | } | |
ee89e05a | 2484 | if ((buf = xmalloc(needed)) == NULL) { |
2485 | debug(28, 0) ("Can't allocate temporary ARP table!\n"); | |
23351cb2 | 2486 | return 0; |
2487 | } | |
2488 | if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { | |
ee89e05a | 2489 | debug(28, 0) ("Can't retrieve ARP table!\n"); |
2490 | xfree(buf); | |
23351cb2 | 2491 | return 0; |
2492 | } | |
2493 | lim = buf + needed; | |
2494 | for (next = buf; next < lim; next += rtm->rtm_msglen) { | |
2495 | rtm = (struct rt_msghdr *) next; | |
2496 | sin = (struct sockaddr_inarp *) (rtm + 1); | |
2497 | sdl = (struct sockaddr_dl *) (sin + 1); | |
2498 | if (sin->sin_addr.s_addr == ip) { | |
2499 | if (sdl->sdl_alen) | |
ee89e05a | 2500 | if (!memcmp(LLADDR(sdl), eth, 6)) { |
2501 | xfree(buf); | |
23351cb2 | 2502 | return 1; |
ee89e05a | 2503 | } |
2504 | break; | |
23351cb2 | 2505 | } |
2506 | } | |
ee89e05a | 2507 | xfree(buf); |
23351cb2 | 2508 | return 0; |
2509 | } | |
bd0c865a | 2510 | **********************************************************************/ |
a931a29b | 2511 | #endif |
23351cb2 | 2512 | |
bd0c865a | 2513 | static void |
2514 | aclDumpArpListWalkee(void *node, void *state) | |
56b63fa1 | 2515 | { |
bd0c865a | 2516 | acl_arp_data *arp = node; |
f084f6af | 2517 | wordlist **W = state; |
2518 | static char buf[24]; | |
2519 | while (*W != NULL) | |
2520 | W = &(*W)->next; | |
272989d5 | 2521 | snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", |
bd0c865a | 2522 | arp->eth[0], arp->eth[1], arp->eth[2], arp->eth[3], |
2523 | arp->eth[4], arp->eth[5]); | |
c68e9c6b | 2524 | wordlistAdd(state, buf); |
f084f6af | 2525 | } |
2526 | ||
2527 | static wordlist * | |
e82d6d21 | 2528 | aclDumpArpList(void *data) |
f084f6af | 2529 | { |
2530 | wordlist *w = NULL; | |
2531 | splay_walk(data, aclDumpArpListWalkee, &w); | |
2532 | return w; | |
56b63fa1 | 2533 | } |
2534 | ||
23351cb2 | 2535 | /* ==== END ARP ACL SUPPORT =============================================== */ |
2536 | #endif /* USE_ARP_ACL */ |