]> git.ipfire.org Git - thirdparty/squid.git/blame - src/acl.cc
DW:
[thirdparty/squid.git] / src / acl.cc
CommitLineData
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 39static int aclFromFile = 0;
40static FILE *aclFile;
c68e9c6b 41static hash_table *proxy_auth_cache = NULL;
7dd57fa2 42
f084f6af 43static void aclParseDomainList(void *curlist);
44static void aclParseIpList(void *curlist);
45static void aclParseIntlist(void *curlist);
46static void aclParseWordList(void *curlist);
47static void aclParseProtoList(void *curlist);
48static void aclParseMethodList(void *curlist);
49static void aclParseTimeSpec(void *curlist);
8f663d72 50static void aclParseIntRange(void *curlist);
f084f6af 51static char *strtokFile(void);
7342cdc8 52static void aclDestroyAclList(acl_list * list);
53static void aclDestroyTimeList(acl_time_data * data);
8f663d72 54static void aclDestroyIntRange(intrange *);
ec878047 55static FREE aclFreeProxyAuthUser;
1cfdbcf0 56static struct _acl *aclFindByName(const char *name);
669fefd4 57static int aclMatchAcl(struct _acl *, aclCheck_t *);
8f663d72 58static int aclMatchIntegerRange(intrange * data, int i);
7342cdc8 59static int aclMatchTime(acl_time_data * data, time_t when);
c68e9c6b 60static int aclMatchUser(wordlist * data, const char *ident);
f5b8bbc4 61static int aclMatchIp(void *dataptr, struct in_addr c);
62static int aclMatchDomainList(void *dataptr, const char *);
8f663d72 63static int aclMatchIntegerRange(intrange * data, int i);
9e160ce2 64#if SQUID_SNMP
dba79ac5 65static int aclMatchWordList(wordlist *, const char *);
9e160ce2 66#endif
56b63fa1 67static squid_acl aclStrToType(const char *s);
f5b8bbc4 68static int decode_addr(const char *, struct in_addr *, struct in_addr *);
69static void aclCheck(aclCheck_t * checklist);
fc5d6f7f 70static void aclCheckCallback(aclCheck_t * checklist, allow_t answer);
3898f57f 71#if USE_IDENT
05832ae1 72static IDCB aclLookupIdentDone;
3898f57f 73#endif
b69f7771 74static IPH aclLookupDstIPDone;
53ad48e6 75static IPH aclLookupDstIPforASNDone;
348b2031 76static FQDNH aclLookupSrcFQDNDone;
77static FQDNH aclLookupDstFQDNDone;
c68e9c6b 78static void aclLookupProxyAuthStart(aclCheck_t * checklist);
79static void aclLookupProxyAuthDone(void *data, char *result);
f084f6af 80static wordlist *aclDumpIpList(void *);
16300b58 81static wordlist *aclDumpDomainList(void *data);
7342cdc8 82static wordlist *aclDumpTimeSpecList(acl_time_data *);
83static wordlist *aclDumpRegexList(relist * data);
84static wordlist *aclDumpIntlistList(intlist * data);
8f663d72 85static wordlist *aclDumpIntRangeList(intrange * data);
7342cdc8 86static wordlist *aclDumpProtoList(intlist * data);
87static wordlist *aclDumpMethodList(intlist * data);
f084f6af 88static SPLAYCMP aclIpNetworkCompare;
89static SPLAYCMP aclHostDomainCompare;
90static SPLAYCMP aclDomainCompare;
91static SPLAYWALKEE aclDumpIpListWalkee;
92static SPLAYWALKEE aclDumpDomainListWalkee;
c68e9c6b 93static SPLAYFREE aclFreeIpData;
f32789f4 94
23351cb2 95#if USE_ARP_ACL
f084f6af 96static void aclParseArpList(void *curlist);
23351cb2 97static int decode_eth(const char *asc, char *eth);
98static int aclMatchArp(void *dataptr, struct in_addr c);
bd0c865a 99static wordlist *aclDumpArpList(void *);
f084f6af 100static SPLAYCMP aclArpCompare;
bd0c865a 101static SPLAYWALKEE aclDumpArpListWalkee;
23351cb2 102#endif
103
e924600d 104static char *
c10aaf05 105strtokFile(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 149static squid_acl
56b63fa1 150aclStrToType(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 211const char *
212aclTypeToStr(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 269static acl *
0ee4272b 270aclFindByName(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 279static void
280aclParseIntlist(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 294static void
295aclParseIntRange(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 314static void
315aclParseProtoList(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 329static void
330aclParseMethodList(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 351static int
0ee4272b 352decode_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 398static acl_ip_data *
dfc52ac5 399aclParseIpData(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 487static void
488aclParseIpList(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 502static void
503aclParseTimeSpec(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 579void
0153d498 580aclParseRegexList(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 615static void
616aclParseWordList(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 627static void
628aclParseDomainList(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 638void
a47b9029 639aclParseAclLine(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 */
772int
773aclGetDenyInfoPage(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 */
796int
797aclIsProxyAuth(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 815void
7342cdc8 816aclParseDenyInfoLine(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 855void
7342cdc8 856aclParseAccessLine(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 930static int
931aclMatchIp(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 944static int
945aclMatchDomainList(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 957int
0ee4272b 958aclMatchRegex(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 984static int
c68e9c6b 985aclMatchUser(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 1001static int
c68e9c6b 1002aclDecodeProxyAuth(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
1053static int
227e68b6 1054aclMatchProxyAuth(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
1146static void
1147aclLookupProxyAuthStart(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 1183static int
1184aclMatchInteger(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 1206static int
1207aclMatchIntegerRange(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 1234static int
7342cdc8 1235aclMatchTime(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 1255static int
c68e9c6b 1256aclMatchWordList(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 1269static int
9ef28b60 1270aclMatchAcl(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 1495int
7342cdc8 1496aclMatchAclList(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 1512int
7342cdc8 1513aclCheckFast(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
1527static void
1528aclCheck(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 1628void
1629aclChecklistFree(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 1649static void
fc5d6f7f 1650aclCheckCallback(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 1662static void
1663aclLookupIdentDone(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 1682static void
03a1ee42 1683aclLookupDstIPDone(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 1690static void
1691aclLookupDstIPforASNDone(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 1698static void
348b2031 1699aclLookupSrcFQDNDone(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
1706static void
348b2031 1707aclLookupDstFQDNDone(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 1714static void
c68e9c6b 1715aclLookupProxyAuthDone(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 1728aclCheck_t *
7342cdc8 1729aclChecklistCreate(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
1758void
1759aclNBCheck(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 1777static void
7342cdc8 1778aclDestroyTimeList(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 1787void
7342cdc8 1788aclDestroyRegexList(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 1799static void
1800aclFreeProxyAuthUser(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 1808static void
c68e9c6b 1809aclFreeIpData(void *p)
afe95a7e 1810{
db1cd23c 1811 memFree(p, MEM_ACL_IP_DATA);
afe95a7e 1812}
1813
8203a132 1814void
a47b9029 1815aclDestroyAcls(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 1875static void
7342cdc8 1876aclDestroyAclList(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 1885void
7342cdc8 1886aclDestroyAccessList(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 1904void
7342cdc8 1905aclDestroyDenyInfoList(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 1924static void
5942e8d4 1925aclDestroyIntRange(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 1940static int
9bc73deb 1941aclDomainCompare(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 1978static int
9bc73deb 1979aclHostDomainCompare(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 1999static int
9bc73deb 2000aclIpNetworkCompare(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 2026static void
2027aclDumpIpListWalkee(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 2042static wordlist *
e82d6d21 2043aclDumpIpList(void *data)
56b63fa1 2044{
f084f6af 2045 wordlist *w = NULL;
2046 splay_walk(data, aclDumpIpListWalkee, &w);
2047 return w;
2048}
2049
2050static void
2051aclDumpDomainListWalkee(void *node, void *state)
2052{
2053 char *domain = node;
c68e9c6b 2054 wordlistAdd(state, domain);
56b63fa1 2055}
2056
2057static wordlist *
2058aclDumpDomainList(void *data)
2059{
f084f6af 2060 wordlist *w = NULL;
2061 splay_walk(data, aclDumpDomainListWalkee, &w);
2062 return w;
56b63fa1 2063}
7342cdc8 2064
56b63fa1 2065static wordlist *
7342cdc8 2066aclDumpTimeSpecList(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 2089static wordlist *
7342cdc8 2090aclDumpRegexList(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 2100static wordlist *
7342cdc8 2101aclDumpIntlistList(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 2113static wordlist *
2114aclDumpIntRangeList(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 2129static wordlist *
7342cdc8 2130aclDumpProtoList(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 2140static wordlist *
7342cdc8 2141aclDumpMethodList(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 2151wordlist *
2152aclDumpGeneric(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 */
2256static int
2257decode_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 2273static acl_arp_data *
23351cb2 2274aclParseArpData(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 2298static void
2299aclParseArpList(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 2315static int
2316aclMatchArp(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 2438static int
9bc73deb 2439aclArpCompare(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
2453static int
2454aclMatchArp(void *dataptr, struct in_addr c)
2455{
e82d6d21 2456 WRITE ME;
f084f6af 2457}
2458
2459static int
2460aclArpCompare(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 2470static int
2471checkARP(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 2513static void
2514aclDumpArpListWalkee(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
2527static wordlist *
e82d6d21 2528aclDumpArpList(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 */