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