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