3 * $Id: acl.cc,v 1.235 2001/01/07 20:11:16 hno Exp $
5 * DEBUG: section 28 Access Control
6 * AUTHOR: Duane Wessels
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
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.
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.
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.
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.
39 static int aclFromFile
= 0;
41 static hash_table
*proxy_auth_cache
= NULL
;
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
);
66 static int aclMatchWordList(wordlist
*, const char *);
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
);
73 static IDCB aclLookupIdentDone
;
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
;
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
;
109 LOCAL_ARRAY(char, buf
, 256);
113 t
= (strtok(NULL
, w_space
));
114 if (t
&& (*t
== '\"' || *t
== '\'')) {
115 /* quote found, start reading from file */
117 while (*t
&& *t
!= '\"' && *t
!= '\'')
120 if ((aclFile
= fopen(fn
, "r")) == NULL
) {
121 debug(28, 0) ("strtokFile: %s not found\n", fn
);
124 #if defined(_SQUID_CYGWIN_)
125 setmode(fileno(aclFile
), O_TEXT
);
133 if (fgets(buf
, 256, aclFile
) == NULL
) {
134 /* stop reading from file */
140 /* skip leading and trailing white space */
141 t
+= strspn(buf
, w_space
);
142 t
[strcspn(t
, w_space
)] = '\0';
146 /* skip blank lines */
154 aclStrToType(const char *s
)
156 if (!strcmp(s
, "src"))
158 if (!strcmp(s
, "dst"))
160 if (!strcmp(s
, "myip"))
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"))
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"))
182 if (!strcmp(s
, "myport"))
184 if (!strcmp(s
, "maxconn"))
187 if (!strcmp(s
, "ident"))
189 if (!strcmp(s
, "ident_regex"))
190 return ACL_IDENT_REGEX
;
192 if (!strncmp(s
, "proto", 5))
194 if (!strcmp(s
, "method"))
196 if (!strcmp(s
, "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"))
204 if (!strcmp(s
, "dst_as"))
207 if (!strcmp(s
, "snmp_community"))
208 return ACL_SNMP_COMMUNITY
;
210 if (!strcmp(s
, "src_rtt"))
211 return ACL_NETDB_SRC_RTT
;
213 if (!strcmp(s
, "arp"))
216 if (!strcmp(s
, "req_mime_type"))
217 return ACL_REQ_MIME_TYPE
;
222 aclTypeToStr(squid_acl type
)
224 if (type
== ACL_SRC_IP
)
226 if (type
== ACL_DST_IP
)
228 if (type
== ACL_MY_IP
)
230 if (type
== ACL_DST_DOMAIN
)
232 if (type
== ACL_SRC_DOMAIN
)
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
)
240 if (type
== ACL_URLPATH_REGEX
)
241 return "urlpath_regex";
242 if (type
== ACL_URL_REGEX
)
244 if (type
== ACL_URL_PORT
)
246 if (type
== ACL_MY_PORT
)
248 if (type
== ACL_MAXCONN
)
251 if (type
== ACL_IDENT
)
253 if (type
== ACL_IDENT_REGEX
)
254 return "ident_regex";
256 if (type
== ACL_PROTO
)
258 if (type
== ACL_METHOD
)
260 if (type
== ACL_BROWSER
)
262 if (type
== ACL_PROXY_AUTH
)
264 if (type
== ACL_PROXY_AUTH_REGEX
)
265 return "proxy_auth_regex";
266 if (type
== ACL_SRC_ASN
)
268 if (type
== ACL_DST_ASN
)
271 if (type
== ACL_SNMP_COMMUNITY
)
272 return "snmp_community";
274 if (type
== ACL_NETDB_SRC_RTT
)
277 if (type
== ACL_SRC_ARP
)
280 if (type
== ACL_REQ_MIME_TYPE
)
281 return "req_mime_type";
286 aclFindByName(const char *name
)
289 for (a
= Config
.aclList
; a
; a
= a
->next
)
290 if (!strcasecmp(a
->name
, name
))
296 aclParseIntlist(void *curlist
)
301 for (Tail
= curlist
; *Tail
; Tail
= &((*Tail
)->next
));
302 while ((t
= strtokFile())) {
303 q
= memAllocate(MEM_INTLIST
);
311 aclParseIntRange(void *curlist
)
316 for (Tail
= curlist
; *Tail
; Tail
= &((*Tail
)->next
));
317 while ((t
= strtokFile())) {
318 q
= xcalloc(1, sizeof(intrange
));
331 aclParseProtoList(void *curlist
)
336 for (Tail
= curlist
; *Tail
; Tail
= &((*Tail
)->next
));
337 while ((t
= strtokFile())) {
338 q
= memAllocate(MEM_INTLIST
);
339 q
->i
= (int) urlParseProtocol(t
);
346 aclParseMethodList(void *curlist
)
351 for (Tail
= curlist
; *Tail
; Tail
= &((*Tail
)->next
));
352 while ((t
= strtokFile())) {
353 q
= memAllocate(MEM_INTLIST
);
354 q
->i
= (int) urlParseMethod(t
);
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!
366 decode_addr(const char *asc
, struct in_addr
*addr
, struct in_addr
*mask
)
369 int a1
= 0, a2
= 0, a3
= 0, a4
= 0;
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");
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;
384 debug(28, 0) ("decode_addr: Invalid IP address '%s'\n", asc
);
385 return 0; /* This is not valid address */
388 if (mask
!= NULL
) { /* mask == NULL if called to decode a 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
);
401 mask
->s_addr
= htonl(0xFFFFFFFFul
);
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"
413 aclParseIpData(const char *t
)
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
);
424 debug(28, 5) ("aclParseIpData: %s\n", t
);
425 if (!strcasecmp(t
, "all")) {
431 if (sscanf(t
, SCAN_ACL1
, addr1
, addr2
, mask
) == 3) {
433 } else if (sscanf(t
, SCAN_ACL2
, addr1
, addr2
, &c
) == 2) {
435 } else if (sscanf(t
, SCAN_ACL3
, addr1
, mask
) == 2) {
437 } else if (sscanf(t
, SCAN_ACL4
, addr1
, &c
) == 1) {
440 } else if (sscanf(t
, "%[^/]/%s", addr1
, mask
) == 2) {
442 } else if (sscanf(t
, "%s", addr1
) == 1) {
444 * Note, must use plain gethostbyname() here because at startup
445 * ipcache hasn't been initialized
447 if ((hp
= gethostbyname(addr1
)) == NULL
) {
448 debug(28, 0) ("aclParseIpData: Bad host/IP: '%s'\n", t
);
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
));
458 r
->mask
.s_addr
= no_addr
.s_addr
; /* 255.255.255.255 */
460 debug(28, 3) ("%s --> %s\n", addr1
, inet_ntoa(r
->addr1
));
464 debug(28, 0) ("aclParseIpData: Bad host/IP: '%s'\n", t
);
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
);
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
);
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
);
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 */
503 aclParseIpList(void *curlist
)
506 splayNode
**Top
= curlist
;
507 acl_ip_data
*q
= NULL
;
508 while ((t
= strtokFile())) {
509 q
= aclParseIpData(t
);
511 *Top
= splay_insert(q
, *Top
, aclIpNetworkCompare
);
518 aclParseTimeSpec(void *curlist
)
520 acl_time_data
*q
= NULL
;
521 acl_time_data
**Tail
;
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 */
532 q
->weekbits
|= ACL_SUNDAY
;
535 q
->weekbits
|= ACL_MONDAY
;
538 q
->weekbits
|= ACL_TUESDAY
;
541 q
->weekbits
|= ACL_WEDNESDAY
;
544 q
->weekbits
|= ACL_THURSDAY
;
547 q
->weekbits
|= ACL_FRIDAY
;
550 q
->weekbits
|= ACL_SATURDAY
;
553 q
->weekbits
|= ACL_WEEKDAYS
;
556 /* ignore placeholder */
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",
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
);
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
);
586 if (q
->start
== 0 && q
->stop
== 0)
587 q
->stop
= 23 * 60 + 59;
588 if (q
->weekbits
== 0)
589 q
->weekbits
= ACL_ALLWEEK
;
595 aclParseRegexList(void *curlist
)
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) {
609 if (strcmp(t
, "+i") == 0) {
613 if ((errcode
= regcomp(&comp
, t
, flags
)) != 0) {
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",
622 q
= memAllocate(MEM_RELIST
);
623 q
->pattern
= xstrdup(t
);
631 aclParseWordList(void *curlist
)
634 while ((t
= strtokFile()))
635 wordlistAdd(curlist
, t
);
639 aclParseUserList(void **current
)
643 splayNode
*Top
= NULL
;
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 */
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;
660 if (data
->flags
.case_insensitive
)
662 Top
= splay_insert(xstrdup(t
), Top
, (SPLAYCMP
*) strcmp
);
665 debug(28, 3) ("aclParseUserList: Case-insensitive-switch is %d\n",
666 data
->flags
.case_insensitive
);
667 /* we might inherit from a previous declaration */
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
)
674 Top
= splay_insert(xstrdup(t
), Top
, (SPLAYCMP
*) strcmp
);
680 /**********************/
681 /* aclParseDomainList */
682 /**********************/
685 aclParseDomainList(void *curlist
)
688 splayNode
**Top
= curlist
;
689 while ((t
= strtokFile())) {
691 *Top
= splay_insert(xstrdup(t
), *Top
, aclDomainCompare
);
696 aclParseAclLine(acl
** head
)
698 /* we're already using strtok() to grok the line */
701 LOCAL_ARRAY(char, aclname
, ACL_NAME_SZ
);
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");
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");
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
);
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
);
731 A
->cfgline
= xstrdup(config_input_line
);
734 if (acltype
!= A
->type
) {
735 debug(28, 0) ("aclParseAclLine: ACL '%s' already exists with different type, skipping.\n", A
->name
);
738 debug(28, 3) ("aclParseAclLine: Appending to '%s'\n", aclname
);
742 * Here we set AclMatchedName in case we need to use it in a
743 * warning message in aclDomainCompare().
745 AclMatchedName
= aclname
; /* ugly */
750 aclParseIpList(&A
->data
);
754 aclParseDomainList(&A
->data
);
757 aclParseTimeSpec(&A
->data
);
760 case ACL_URLPATH_REGEX
:
762 case ACL_SRC_DOM_REGEX
:
763 case ACL_DST_DOM_REGEX
:
764 aclParseRegexList(&A
->data
);
769 case ACL_NETDB_SRC_RTT
:
770 aclParseIntlist(&A
->data
);
774 aclParseIntRange(&A
->data
);
778 aclParseUserList(&A
->data
);
780 case ACL_IDENT_REGEX
:
781 aclParseRegexList(&A
->data
);
785 aclParseProtoList(&A
->data
);
788 aclParseMethodList(&A
->data
);
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
);
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
);
807 case ACL_SNMP_COMMUNITY
:
808 aclParseWordList(&A
->data
);
813 aclParseArpList(&A
->data
);
816 case ACL_REQ_MIME_TYPE
:
817 aclParseWordList(&A
->data
);
821 fatal("Bad ACL type");
825 * Clear AclMatchedName from our temporary hack
827 AclMatchedName
= NULL
; /* ugly */
830 if (A
->data
== NULL
) {
831 debug(28, 0) ("aclParseAclLine: IGNORING invalid ACL: %s\n",
838 head
= &(*head
)->next
;
842 /* does name lookup, returns page_id */
844 aclGetDenyInfoPage(acl_deny_info_list
** head
, const char *name
)
846 acl_deny_info_list
*A
= NULL
;
847 acl_name_list
*L
= NULL
;
850 if (NULL
== *head
) /* empty list */
854 if (NULL
== L
) /* empty list should never happen, but in case */
857 if (!strcmp(name
, L
->name
))
858 return A
->err_page_id
;
866 /* does name lookup, returns if it is a proxy_auth acl */
868 aclIsProxyAuth(const char *name
)
870 acl
*a
= aclFindByName(name
);
872 return a
->type
== ACL_PROXY_AUTH
;
877 /* maex@space.net (05.09.96)
878 * get the info for redirecting "access denied" to info pages
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
887 aclParseDenyInfoLine(acl_deny_info_list
** head
)
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
;
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");
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 */
909 while ((t
= strtok(NULL
, w_space
))) {
910 L
= memAllocate(MEM_ACL_NAME_LIST
);
911 xstrncpy(L
->name
, t
, ACL_NAME_SZ
);
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
);
922 for (B
= *head
, T
= head
; B
; T
= &B
->next
, B
= B
->next
); /* find the tail */
927 aclParseAccessLine(acl_access
** head
)
930 acl_access
*A
= NULL
;
931 acl_access
*B
= NULL
;
932 acl_access
**T
= NULL
;
934 acl_list
**Tail
= NULL
;
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");
944 A
= CBDATA_ALLOC(acl_access
, NULL
);
946 if (!strcmp(t
, "allow"))
948 else if (!strcmp(t
, "deny"))
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
);
958 /* next expect a list of ACL names, possibly preceeded
959 * by '!' for negation */
961 while ((t
= strtok(NULL
, w_space
))) {
962 L
= memAllocate(MEM_ACL_LIST
);
963 L
->op
= 1; /* defaults to non-negated */
969 debug(28, 3) ("aclParseAccessLine: looking for ACL name '%s'\n", t
);
970 a
= aclFindByName(t
);
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
);
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");
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
);
993 /* We lock _acl_access structures in aclCheck() */
1001 aclMatchIp(void *dataptr
, struct in_addr c
)
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
;
1010 /**********************/
1011 /* aclMatchDomainList */
1012 /**********************/
1015 aclMatchDomainList(void *dataptr
, const char *host
)
1017 splayNode
**Top
= dataptr
;
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
;
1028 aclMatchRegex(relist
* data
, const char *word
)
1030 relist
*first
, *prev
;
1033 debug(28, 3) ("aclMatchRegex: checking '%s'\n", word
);
1037 debug(28, 3) ("aclMatchRegex: looking for '%s'\n", data
->pattern
);
1038 if (regexec(&data
->regex
, word
, 0, 0, 0) == 0) {
1040 /* shift the element just found to the second position
1042 prev
->next
= data
->next
;
1043 data
->next
= first
->next
;
1055 aclMatchUser(void *proxyauth_acl
, char *user
)
1057 acl_user_data
*data
= (acl_user_data
*) proxyauth_acl
;
1058 splayNode
*Top
= data
->names
;
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"));
1068 if (data
->flags
.case_insensitive
)
1071 if (data
->flags
.required
) {
1072 debug(28, 7) ("aclMatchUser: user REQUIRED and auth-info present.\n");
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",
1079 Top
, (Top
? Top
->data
: "Unavailable"));
1081 return !splayLastResult
;
1085 aclDecodeProxyAuth(const char *proxy_auth
, char **user
, char **password
, char *buf
, size_t bufsize
)
1089 if (proxy_auth
== NULL
)
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
);
1096 proxy_auth
+= 6; /* "Basic " */
1097 /* Trim leading whitespace before decoding */
1098 while (xisspace(*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
);
1106 * Don't allow NL or CR in the credentials.
1107 * Oezguer Kesim <oec@codeblau.de>
1109 strtok(cleartext
, "\r\n");
1110 debug(28, 6) ("aclDecodeProxyAuth: cleartext = '%s'\n", cleartext
);
1111 xstrncpy(buf
, cleartext
, bufsize
);
1113 /* Trim leading whitespace after decoding */
1114 while (xisspace(*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
);
1123 if (**password
== '\0') {
1124 debug(28, 1) ("aclDecodeProxyAuth: Disallowing empty password,"
1125 "user is '%s'\n", *user
);
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
1140 aclMatchProxyAuth(void *data
, const char *proxy_auth
, acl_proxy_auth_user
* auth_user
, aclCheck_t
* checklist
, squid_acl acltype
)
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
;
1146 if (!aclDecodeProxyAuth(proxy_auth
, &user
, &password
, login_buf
, sizeof(login_buf
)))
1147 /* No or invalid Proxy-Auth header */
1150 debug(28, 5) ("aclMatchProxyAuth: checking user '%s'\n", user
);
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.
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",
1167 aclFreeProxyAuthUser(auth_user
);
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)
1173 if (!*checklist
->request
->user_ident
)
1174 xstrncpy(checklist
->request
->user_ident
, user
, USER_IDENT_SZ
);
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 */
1188 /* see if we already know this user */
1189 auth_user
= hash_lookup(proxy_auth_cache
, user
);
1192 /* user not yet known, ask external authenticator */
1193 debug(28, 4) ("aclMatchProxyAuth: user '%s' not yet known\n", user
);
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",
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
);
1208 case ACL_PROXY_AUTH
:
1209 return aclMatchUser(data
, user
);
1210 case ACL_PROXY_AUTH_REGEX
:
1211 return aclMatchRegex(data
, user
);
1213 fatal("aclMatchProxyAuth: unknown ACL type");
1214 return 0; /* NOTREACHED */
1217 if (Config
.onoff
.authenticateIpTTLStrict
) {
1218 /* Access from some other IP address than the one owning
1219 * this user ID. Deny access
1221 debug(28, 1) ("aclMatchProxyAuth: user '%s' tries to use multple IP addresses!\n", user
);
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 */
1234 /* password mismatch/timeout */
1235 debug(28, 4) ("aclMatchProxyAuth: user '%s' password mismatch/timeout\n",
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 */
1249 aclLookupProxyAuthStart(aclCheck_t
* checklist
)
1251 LOCAL_ARRAY(char, login_buf
, USER_IDENT_SZ
);
1252 const char *proxy_auth
;
1253 char *user
, *password
;
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
);
1262 /* WWW auth on accelerated requests */
1263 proxy_auth
= httpHeaderGetStr(&checklist
->request
->header
,
1266 ok
= aclDecodeProxyAuth(proxy_auth
, &user
, &password
, login_buf
,
1269 * if aclDecodeProxyAuth() fails, the same call should have failed
1270 * in aclMatchProxyAuth, and we should never get this far.
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
,
1286 aclMatchInteger(intlist
* data
, int i
)
1288 intlist
*first
, *prev
;
1294 /* shift the element just found to the second position
1296 prev
->next
= data
->next
;
1297 data
->next
= first
->next
;
1309 aclMatchIntegerRange(intrange
* data
, int i
)
1311 intrange
*first
, *prev
;
1317 } else if (i
> data
->j
) {
1322 /* shift the element just found to the second position
1324 prev
->next
= data
->next
;
1325 data
->next
= first
->next
;
1337 aclMatchTime(acl_time_data
* data
, time_t when
)
1339 static time_t last_when
= 0;
1340 static struct tm tm
;
1342 assert(data
!= NULL
);
1343 if (when
!= last_when
) {
1345 xmemcpy(&tm
, localtime(&when
), sizeof(struct tm
));
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
);
1351 if (t
< data
->start
|| t
> data
->stop
)
1353 return data
->weekbits
& (1 << tm
.tm_wday
) ? 1 : 0;
1358 aclMatchWordList(wordlist
* w
, const char *word
)
1360 debug(28, 3) ("aclMatchWordList: looking for '%s'\n", word
);
1362 debug(28, 3) ("aclMatchWordList: checking '%s'\n", w
->key
);
1363 if (!strcmp(w
->key
, word
))
1372 aclMatchAcl(acl
* ae
, aclCheck_t
* checklist
)
1374 request_t
*r
= checklist
->request
;
1375 const ipcache_addrs
*ia
= NULL
;
1376 const char *fqdn
= NULL
;
1379 const char *browser
;
1385 case ACL_DST_DOMAIN
:
1386 case ACL_DST_DOM_REGEX
:
1387 case ACL_URLPATH_REGEX
:
1392 /* These ACL types require checklist->request */
1394 debug(28, 1) ("WARNING: '%s' ACL is used but there is no"
1395 " HTTP request -- access denied.\n", ae
->name
);
1402 debug(28, 3) ("aclMatchAcl: checking '%s'\n", ae
->cfgline
);
1405 return aclMatchIp(&ae
->data
, checklist
->src_addr
);
1408 return aclMatchIp(&ae
->data
, checklist
->my_addr
);
1411 ia
= ipcache_gethostbyname(r
->host
, IP_LOOKUP_IF_MISS
);
1413 for (k
= 0; k
< (int) ia
->count
; k
++) {
1414 if (aclMatchIp(&ae
->data
, ia
->in_addrs
[k
]))
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",
1421 checklist
->state
[ACL_DST_IP
] = ACL_LOOKUP_NEEDED
;
1424 return aclMatchIp(&ae
->data
, no_addr
);
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
);
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
;
1439 return aclMatchDomainList(&ae
->data
, "none");
1441 case ACL_SRC_DOMAIN
:
1442 fqdn
= fqdncache_gethostbyaddr(checklist
->src_addr
, FQDN_LOOKUP_IF_MISS
);
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
;
1451 return aclMatchDomainList(&ae
->data
, "none");
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
);
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
;
1465 return aclMatchRegex(ae
->data
, "none");
1467 case ACL_SRC_DOM_REGEX
:
1468 fqdn
= fqdncache_gethostbyaddr(checklist
->src_addr
, FQDN_LOOKUP_IF_MISS
);
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
;
1477 return aclMatchRegex(ae
->data
, "none");
1480 return aclMatchTime(ae
->data
, squid_curtime
);
1482 case ACL_URLPATH_REGEX
:
1483 esc_buf
= xstrdup(strBuf(r
->urlpath
));
1484 rfc1738_unescape(esc_buf
);
1485 k
= aclMatchRegex(ae
->data
, esc_buf
);
1490 esc_buf
= xstrdup(urlCanonical(r
));
1491 rfc1738_unescape(esc_buf
);
1492 k
= aclMatchRegex(ae
->data
, esc_buf
);
1497 k
= clientdbEstablished(checklist
->src_addr
, 0);
1498 return ((k
> ((intlist
*) ae
->data
)->i
) ? 1 : 0);
1501 return aclMatchIntegerRange(ae
->data
, (int) r
->port
);
1504 return aclMatchIntegerRange(ae
->data
, (int) checklist
->my_port
);
1508 if (checklist
->ident
[0]) {
1509 return aclMatchUser(ae
->data
, checklist
->ident
);
1511 checklist
->state
[ACL_IDENT
] = ACL_LOOKUP_NEEDED
;
1515 case ACL_IDENT_REGEX
:
1516 if (checklist
->ident
[0]) {
1517 return aclMatchRegex(ae
->data
, checklist
->ident
);
1519 checklist
->state
[ACL_IDENT
] = ACL_LOOKUP_NEEDED
;
1525 return aclMatchInteger(ae
->data
, r
->protocol
);
1528 return aclMatchInteger(ae
->data
, r
->method
);
1531 browser
= httpHeaderGetStr(&checklist
->request
->header
, HDR_USER_AGENT
);
1532 if (NULL
== browser
)
1534 return aclMatchRegex(ae
->data
, browser
);
1536 case ACL_PROXY_AUTH
:
1537 case ACL_PROXY_AUTH_REGEX
:
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
,
1549 #if AUTH_ON_ACCELERATION
1550 /* WWW authorization on accelerated requests */
1551 header
= httpHeaderGetStr(&checklist
->request
->header
,
1554 debug(28, 1) ("aclMatchAcl: proxy_auth %s not applicable on accelerated requests.\n", ae
->name
);
1559 * Register that we used the proxy authentication header so that
1560 * it is not forwarded to the next proxy
1562 r
->flags
.used_proxy_auth
= 1;
1563 /* Check the password */
1564 switch (aclMatchProxyAuth(ae
->data
,
1566 checklist
->auth_user
,
1570 /* Correct password, but was not allowed in this ACL */
1573 /* user validated OK */
1576 /* no such user OR we need a proxy authentication header */
1577 checklist
->state
[ACL_PROXY_AUTH
] = ACL_PROXY_AUTH_NEEDED
;
1579 * XXX This is a bit oddly done.. should perhaps use different
1585 * we need to validate the password
1587 checklist
->state
[ACL_PROXY_AUTH
] = ACL_LOOKUP_NEEDED
;
1592 case ACL_SNMP_COMMUNITY
:
1593 return aclMatchWordList(ae
->data
, checklist
->snmp_community
);
1596 return asnMatchIp(ae
->data
, checklist
->src_addr
);
1598 ia
= ipcache_gethostbyname(r
->host
, IP_LOOKUP_IF_MISS
);
1600 for (k
= 0; k
< (int) ia
->count
; k
++) {
1601 if (asnMatchIp(ae
->data
, ia
->in_addrs
[k
]))
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",
1608 checklist
->state
[ACL_DST_ASN
] = ACL_LOOKUP_NEEDED
;
1610 return asnMatchIp(ae
->data
, no_addr
);
1615 return aclMatchArp(&ae
->data
, checklist
->src_addr
);
1617 case ACL_REQ_MIME_TYPE
:
1618 header
= httpHeaderGetStr(&checklist
->request
->header
,
1622 return aclMatchRegex(ae
->data
, header
);
1626 debug(28, 0) ("aclMatchAcl: '%s' has bad type %d\n",
1627 ae
->name
, ae
->type
);
1634 aclMatchAclList(const acl_list
* list
, aclCheck_t
* checklist
)
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");
1646 debug(28, 3) ("aclMatchAclList: returning 1\n");
1651 aclCheckFast(const acl_access
* A
, aclCheck_t
* checklist
)
1654 debug(28, 5) ("aclCheckFast: list: %p\n", A
);
1657 if (aclMatchAclList(A
->acl_list
, checklist
))
1661 debug(28, 5) ("aclCheckFast: no matches, returning: %d\n", !allow
);
1666 aclCheck(aclCheck_t
* checklist
)
1668 allow_t allow
= ACCESS_DENIED
;
1669 const acl_access
*A
;
1672 while ((A
= checklist
->access_list
) != NULL
) {
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.
1678 if (!cbdataValid(A
)) {
1682 debug(28, 3) ("aclCheck: checking '%s'\n", A
->cfgline
);
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
,
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
,
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
,
1703 } else if (checklist
->state
[ACL_DST_DOMAIN
] == ACL_LOOKUP_NEEDED
) {
1704 ia
= ipcacheCheckNumeric(checklist
->request
->host
);
1706 checklist
->state
[ACL_DST_DOMAIN
] = ACL_LOOKUP_DONE
;
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
,
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
;
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.
1724 allow
= ACCESS_REQ_PROXY_AUTH
;
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
;
1736 debug(28, 1) ("aclCheck: Can't start ident lookup. No client connection\n");
1737 cbdataUnlock(checklist
->conn
);
1738 checklist
->conn
= NULL
;
1745 * We are done with this _acl_access entry. Either the request
1746 * is allowed, denied, requires authentication, or we move on to
1751 debug(28, 3) ("aclCheck: match found, returning %d\n", allow
);
1752 aclCheckCallback(checklist
, allow
);
1755 checklist
->access_list
= A
->next
;
1757 * Lock the next _acl_access entry
1760 cbdataLock(A
->next
);
1762 debug(28, 3) ("aclCheck: NO match found, returning %d\n", !allow
);
1763 aclCheckCallback(checklist
, !allow
);
1767 aclChecklistFree(aclCheck_t
* checklist
)
1769 if (checklist
->request
)
1770 requestUnlink(checklist
->request
);
1771 checklist
->request
= NULL
;
1773 if (checklist
->conn
) {
1774 cbdataUnlock(checklist
->conn
);
1775 checklist
->conn
= NULL
;
1778 cbdataFree(checklist
);
1782 aclCheckCallback(aclCheck_t
* checklist
, allow_t answer
)
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
);
1795 aclLookupIdentDone(const char *ident
, void *data
)
1797 aclCheck_t
*checklist
= data
;
1799 xstrncpy(checklist
->ident
, ident
, sizeof(checklist
->ident
));
1800 xstrncpy(checklist
->request
->user_ident
, ident
, sizeof(checklist
->request
->user_ident
));
1802 xstrncpy(checklist
->ident
, "-", sizeof(checklist
->ident
));
1805 * Cache the ident result in the connection, to avoid redoing ident lookup
1806 * over and over on persistent connections
1808 if (cbdataValid(checklist
->conn
) && !checklist
->conn
->ident
[0])
1809 xstrncpy(checklist
->conn
->ident
, checklist
->ident
, sizeof(checklist
->conn
->ident
));
1810 aclCheck(checklist
);
1815 aclLookupDstIPDone(const ipcache_addrs
* ia
, void *data
)
1817 aclCheck_t
*checklist
= data
;
1818 checklist
->state
[ACL_DST_IP
] = ACL_LOOKUP_DONE
;
1819 aclCheck(checklist
);
1823 aclLookupDstIPforASNDone(const ipcache_addrs
* ia
, void *data
)
1825 aclCheck_t
*checklist
= data
;
1826 checklist
->state
[ACL_DST_ASN
] = ACL_LOOKUP_DONE
;
1827 aclCheck(checklist
);
1831 aclLookupSrcFQDNDone(const char *fqdn
, void *data
)
1833 aclCheck_t
*checklist
= data
;
1834 checklist
->state
[ACL_SRC_DOMAIN
] = ACL_LOOKUP_DONE
;
1835 aclCheck(checklist
);
1839 aclLookupDstFQDNDone(const char *fqdn
, void *data
)
1841 aclCheck_t
*checklist
= data
;
1842 checklist
->state
[ACL_DST_DOMAIN
] = ACL_LOOKUP_DONE
;
1843 aclCheck(checklist
);
1847 aclLookupProxyAuthDone(void *data
, char *result
)
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");
1854 checklist
->auth_user
->passwd_ok
= 0;
1855 else if (0 == strncasecmp(result
, "OK", 2))
1856 checklist
->auth_user
->passwd_ok
= 1;
1858 if (strlen(result
) > sizeof("ERR "))
1859 checklist
->auth_user
->message
= xstrdup(result
+ 4);
1860 checklist
->auth_user
->passwd_ok
= 0;
1862 aclCheck(checklist
);
1866 aclChecklistCreate(const acl_access
* A
,
1867 request_t
* request
,
1871 aclCheck_t
*checklist
;
1872 checklist
= CBDATA_ALLOC(aclCheck_t
, NULL
);
1873 checklist
->access_list
= A
;
1875 * aclCheck() makes sure checklist->access_list is a valid
1876 * pointer, so lock it.
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
;
1885 for (i
= 0; i
< ACL_ENUM_MAX
; i
++)
1886 checklist
->state
[i
] = ACL_LOOKUP_NONE
;
1889 xstrncpy(checklist
->ident
, ident
, USER_IDENT_SZ
);
1891 checklist
->auth_user
= NULL
; /* init to NULL */
1896 aclNBCheck(aclCheck_t
* checklist
, PF callback
, void *callback_data
)
1898 checklist
->callback
= callback
;
1899 checklist
->callback_data
= callback_data
;
1900 cbdataLock(callback_data
);
1901 aclCheck(checklist
);
1910 /*********************/
1911 /* Destroy functions */
1912 /*********************/
1915 aclDestroyTimeList(acl_time_data
* data
)
1917 acl_time_data
*next
= NULL
;
1918 for (; data
; data
= next
) {
1920 memFree(data
, MEM_ACL_TIME_DATA
);
1925 aclDestroyRegexList(relist
* data
)
1927 relist
*next
= NULL
;
1928 for (; data
; data
= next
) {
1930 regfree(&data
->regex
);
1931 safe_free(data
->pattern
);
1932 memFree(data
, MEM_RELIST
);
1937 aclFreeProxyAuthUser(void *data
)
1939 acl_proxy_auth_user
*u
= data
;
1942 memFree(u
, MEM_ACL_PROXY_AUTH_USER
);
1946 aclFreeIpData(void *p
)
1948 memFree(p
, MEM_ACL_IP_DATA
);
1952 aclFreeUserData(void *data
)
1954 acl_user_data
*d
= data
;
1955 splay_destroy(d
->names
, xfree
);
1956 memFree(d
, MEM_ACL_USER_DATA
);
1961 aclDestroyAcls(acl
** head
)
1965 for (a
= *head
; a
; a
= next
) {
1967 debug(28, 3) ("aclDestroyAcls: '%s'\n", a
->cfgline
);
1972 splay_destroy(a
->data
, aclFreeIpData
);
1975 case ACL_DST_DOMAIN
:
1976 case ACL_SRC_DOMAIN
:
1977 splay_destroy(a
->data
, xfree
);
1980 case ACL_SNMP_COMMUNITY
:
1981 wordlistDestroy((wordlist
**) & a
->data
);
1986 aclFreeUserData(a
->data
);
1989 case ACL_PROXY_AUTH
:
1990 aclFreeUserData(a
->data
);
1993 aclDestroyTimeList(a
->data
);
1996 case ACL_IDENT_REGEX
:
1998 case ACL_PROXY_AUTH_REGEX
:
2000 case ACL_URLPATH_REGEX
:
2002 case ACL_SRC_DOM_REGEX
:
2003 case ACL_DST_DOM_REGEX
:
2004 aclDestroyRegexList(a
->data
);
2010 case ACL_NETDB_SRC_RTT
:
2012 intlistDestroy((intlist
**) & a
->data
);
2016 aclDestroyIntRange(a
->data
);
2020 debug(28, 1) ("aclDestroyAcls: no case for ACL type %d\n", a
->type
);
2023 safe_free(a
->cfgline
);
2024 memFree(a
, MEM_ACL
);
2030 aclDestroyAclList(acl_list
* list
)
2032 acl_list
*next
= NULL
;
2033 for (; list
; list
= next
) {
2035 memFree(list
, MEM_ACL_LIST
);
2040 aclDestroyAccessList(acl_access
** list
)
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
);
2047 aclDestroyAclList(l
->acl_list
);
2049 safe_free(l
->cfgline
);
2055 /* maex@space.net (06.09.1996)
2056 * destroy an _acl_deny_info_list */
2059 aclDestroyDenyInfoList(acl_deny_info_list
** list
)
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
;
2066 for (a
= *list
; a
; a
= a_next
) {
2067 for (l
= a
->acl_list
; l
; l
= l_next
) {
2072 xfree(a
->err_page_name
);
2073 memFree(a
, MEM_ACL_DENY_INFO_LIST
);
2079 aclDestroyIntRange(intrange
* list
)
2083 for (w
= list
; w
; w
= n
) {
2089 /* general compare functions, these are used for tree search algorithms
2090 * so they return <0, 0 or >0 */
2092 /* compare two domains */
2095 aclDomainCompare(const void *a
, const void *b
)
2102 ret
= aclHostDomainCompare(d1
, d2
);
2106 ret
= aclHostDomainCompare(d1
, d2
);
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
);
2116 /* compare a host and a domain */
2119 aclHostDomainCompare(const void *a
, const void *b
)
2123 return matchDomainName(h
, d
);
2126 /* compare two network specs
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:
2132 * 128.1.2.0/255.255.255.128 == 128.1.2.0/255.255.255.0 ?
2134 * Currently only the first address of the first network is used.
2137 /* compare an address and a network spec */
2140 aclIpNetworkCompare(const void *a
, const void *b
)
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
;
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
))
2151 else if (ntohl(A
.s_addr
) < ntohl(B
.s_addr
))
2155 } else { /* range address check */
2156 if (ntohl(A
.s_addr
) > ntohl(C
.s_addr
))
2158 else if (ntohl(A
.s_addr
) < ntohl(B
.s_addr
))
2167 aclDumpUserListWalkee(void *node_data
, void *outlist
)
2169 /* outlist is really a wordlist ** */
2170 wordlistAdd(outlist
, node_data
);
2174 aclDumpUserList(acl_user_data
* data
)
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.
2183 splay_walk(data
->names
, aclDumpUserListWalkee
, &wl
);
2188 aclDumpIpListWalkee(void *node
, void *state
)
2190 acl_ip_data
*ip
= node
;
2192 wordlist
**W
= state
;
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
);
2204 aclDumpIpList(void *data
)
2207 splay_walk(data
, aclDumpIpListWalkee
, &w
);
2212 aclDumpDomainListWalkee(void *node
, void *state
)
2214 char *domain
= node
;
2215 wordlistAdd(state
, domain
);
2219 aclDumpDomainList(void *data
)
2222 splay_walk(data
, aclDumpDomainListWalkee
, &w
);
2227 aclDumpTimeSpecList(acl_time_data
* t
)
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' : '-',
2244 wordlistAdd(&W
, buf
);
2251 aclDumpRegexList(relist
* data
)
2254 while (data
!= NULL
) {
2255 wordlistAdd(&W
, data
->pattern
);
2262 aclDumpIntlistList(intlist
* data
)
2266 while (data
!= NULL
) {
2267 snprintf(buf
, sizeof(buf
), "%d", data
->i
);
2268 wordlistAdd(&W
, buf
);
2275 aclDumpIntRangeList(intrange
* data
)
2279 while (data
!= NULL
) {
2280 if (data
->i
== data
->j
)
2281 snprintf(buf
, sizeof(buf
), "%d", data
->i
);
2283 snprintf(buf
, sizeof(buf
), "%d-%d", data
->i
, data
->j
);
2284 wordlistAdd(&W
, buf
);
2291 aclDumpProtoList(intlist
* data
)
2294 while (data
!= NULL
) {
2295 wordlistAdd(&W
, ProtocolStr
[data
->i
]);
2302 aclDumpMethodList(intlist
* data
)
2305 while (data
!= NULL
) {
2306 wordlistAdd(&W
, RequestMethodStr
[data
->i
]);
2313 aclDumpGeneric(const acl
* a
)
2315 debug(28, 3) ("aclDumpGeneric: %s type %d\n", a
->name
, a
->type
);
2320 return aclDumpIpList(a
->data
);
2322 case ACL_SRC_DOMAIN
:
2323 case ACL_DST_DOMAIN
:
2324 return aclDumpDomainList(a
->data
);
2327 case ACL_SNMP_COMMUNITY
:
2328 return wordlistDup(a
->data
);
2333 return aclDumpUserList(a
->data
);
2335 case ACL_IDENT_REGEX
:
2336 return aclDumpRegexList(a
->data
);
2339 case ACL_PROXY_AUTH
:
2340 return aclDumpUserList(a
->data
);
2343 return aclDumpTimeSpecList(a
->data
);
2345 case ACL_PROXY_AUTH_REGEX
:
2347 case ACL_URLPATH_REGEX
:
2349 case ACL_SRC_DOM_REGEX
:
2350 case ACL_DST_DOM_REGEX
:
2351 return aclDumpRegexList(a
->data
);
2356 return aclDumpIntlistList(a
->data
);
2360 return aclDumpIntRangeList(a
->data
);
2363 return aclDumpProtoList(a
->data
);
2366 return aclDumpMethodList(a
->data
);
2370 return aclDumpArpList(a
->data
);
2375 debug(28, 1) ("aclDumpGeneric: no case for ACL type %d\n", a
->type
);
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,
2388 aclPurgeMethodInUse(acl_access
* a
)
2391 for (; a
; a
= a
->next
) {
2392 for (b
= a
->acl_list
; b
; b
= b
->next
) {
2393 if (ACL_METHOD
!= b
->acl
->type
)
2395 if (aclMatchInteger(b
->acl
->data
, METHOD_PURGE
))
2404 /* ==== BEGIN ARP ACL SUPPORT ============================================= */
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 * ============================================================================
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:
2419 * acl <name> arp <Ethernet address> ...
2423 * acl students arp 00:00:21:55:ed:22 00:00:21:ff:55:38
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>
2430 #ifdef _SQUID_SOLARIS_
2431 #include <sys/sockio.h>
2433 #include <sys/sysctl.h>
2435 #ifdef _SQUID_LINUX_
2436 #include <net/if_arp.h>
2437 #include <sys/ioctl.h>
2439 #include <net/if_dl.h>
2441 #include <net/route.h>
2443 #if HAVE_NETINET_IF_ETHER_H
2444 #include <netinet/if_ether.h>
2448 * Decode an ascii representation (asc) of an ethernet adress, and place
2452 decode_eth(const char *asc
, char *eth
)
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 */
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
;
2468 static acl_arp_data
*
2469 aclParseArpData(const char *t
)
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
);
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
);
2490 /*******************/
2491 /* aclParseArpList */
2492 /*******************/
2494 aclParseArpList(void *curlist
)
2497 splayNode
**Top
= curlist
;
2498 acl_arp_data
*q
= NULL
;
2499 while ((t
= strtokFile())) {
2500 if ((q
= aclParseArpData(t
)) == NULL
)
2502 *Top
= splay_insert(q
, *Top
, aclArpCompare
);
2510 aclMatchArp(void *dataptr
, struct in_addr c
)
2512 #if defined(_SQUID_LINUX_)
2513 struct arpreq arpReq
;
2514 struct sockaddr_in ipAddr
;
2515 unsigned char ifbuffer
[sizeof(struct ifreq
) * 64];
2519 splayNode
**Top
= dataptr
;
2521 * The linux kernel 2.2 maintains per interface ARP caches and
2522 * thus requires an interface name when doing ARP queries.
2524 * The older 2.0 kernels appear to use a unified ARP cache,
2525 * and require an empty interface name
2527 * To support both, we attempt the lookup with a blank interface
2528 * name first. If that does not succeed, the try each interface
2532 * Set up structures for ARP lookup with blank interface name
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
) {
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);
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
);
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",
2563 if (ifc
.ifc_len
> sizeof(ifbuffer
)) {
2564 debug(28, 1) ("Interface list too long - %d\n", ifc
.ifc_len
);
2567 /* Attempt ARP lookup on each interface */
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))
2575 if (NULL
!= strchr(ifr
->ifr_name
, ':'))
2577 debug(28, 4) ("Looking up ARP address for %s on %s\n", inet_ntoa(c
),
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
)) {
2590 * Query failed. Do not log failed lookups or "device
2595 else if (ENODEV
== errno
)
2598 debug(28, 1) ("ARP query failed: %s: %s\n",
2599 ifr
->ifr_name
, xstrerror());
2602 /* Skip non-ethernet interfaces */
2603 if (arpReq
.arp_ha
.sa_family
!= ARPHRD_ETHER
)
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,
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
);
2622 * Should we stop looking here? Can the same IP address
2623 * exist on multiple interfaces?
2626 #elif defined(_SQUID_SOLARIS_)
2627 struct arpreq arpReq
;
2628 struct sockaddr_in ipAddr
;
2629 unsigned char ifbuffer
[sizeof(struct ifreq
) * 64];
2633 splayNode
**Top
= dataptr
;
2635 * Set up structures for ARP lookup with blank interface name
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) {
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
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)
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);
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
);
2669 * Address was not found on any interface
2671 debug(28, 3) ("aclMatchArp: %s NOT found\n", inet_ntoa(c
));
2676 aclArpCompare(const void *a
, const void *b
)
2678 #if defined(_SQUID_LINUX_)
2679 const unsigned short *d1
= a
;
2680 const unsigned short *d2
= b
;
2682 return (d1
[0] > d2
[0]) ? 1 : -1;
2684 return (d1
[1] > d2
[1]) ? 1 : -1;
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
;
2691 return (d1
[0] > d2
[0]) ? 1 : -1;
2693 return (d1
[1] > d2
[1]) ? 1 : -1;
2695 return (d1
[2] > d2
[2]) ? 1 : -1;
2697 return (d1
[3] > d2
[3]) ? 1 : -1;
2699 return (d1
[4] > d2
[4]) ? 1 : -1;
2701 return (d1
[5] > d2
[5]) ? 1 : -1;
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 ***********************************************************************
2715 checkARP(u_long ip, char *eth)
2718 {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO};
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");
2728 if ((buf = xmalloc(needed)) == NULL) {
2729 debug(28, 0) ("Can't allocate temporary ARP table!\n");
2732 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
2733 debug(28, 0) ("Can't retrieve ARP table!\n");
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) {
2744 if (!memcmp(LLADDR(sdl), eth, 6)) {
2754 **********************************************************************/
2758 aclDumpArpListWalkee(void *node
, void *state
)
2760 acl_arp_data
*arp
= node
;
2761 wordlist
**W
= state
;
2762 static char buf
[24];
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
);
2772 aclDumpArpList(void *data
)
2775 splay_walk(data
, aclDumpArpListWalkee
, &w
);
2779 /* ==== END ARP ACL SUPPORT =============================================== */
2780 #endif /* USE_ARP_ACL */