]> git.ipfire.org Git - thirdparty/squid.git/blame - src/acl.cc
typo
[thirdparty/squid.git] / src / acl.cc
CommitLineData
8213067d 1/*
b1ef1220 2 * $Id: acl.cc,v 1.78 1997/02/01 00:28:07 wessels Exp $
30a4f2a8 3 *
4 * DEBUG: section 28 Access Control
5 * AUTHOR: Duane Wessels
6 *
42c04c16 7 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
30a4f2a8 8 * --------------------------------------------------------
9 *
10 * Squid is the result of efforts by numerous individuals from the
11 * Internet community. Development is led by Duane Wessels of the
12 * National Laboratory for Applied Network Research and funded by
13 * the National Science Foundation.
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 *
8213067d 29 */
7dd57fa2 30
31#include "squid.h"
32
e92d33a5 33/* Global */
0ee4272b 34const char *AclMatchedName = NULL;
e92d33a5 35
c10aaf05 36/* for reading ACL's from files */
37int aclFromFile = 0;
38FILE *aclFile;
39
e92d33a5 40/* These three should never be referenced directly in this file! */
41struct _acl_deny_info_list *DenyInfoList = NULL;
92a6f4b1 42struct _acl_access *HTTPAccessList = NULL;
43struct _acl_access *ICPAccessList = NULL;
48270233 44struct _acl_access *MISSAccessList = NULL;
caebbe00 45#if DELAY_HACK
46struct _acl_access *DelayAccessList = NULL;
47#endif
92a6f4b1 48
7dd57fa2 49static struct _acl *AclList = NULL;
50static struct _acl **AclListTail = &AclList;
4db43fab 51static const char *const w_space = " \t\n\r"; /* Jasper sez so */
7dd57fa2 52
67508012 53static void aclDestroyAclList _PARAMS((struct _acl_list * list));
54static void aclDestroyIpList _PARAMS((struct _acl_ip_data * data));
b1ef1220 55static struct _acl_ip_data *aclSplayInsertIp _PARAMS((struct in_addr, struct in_addr, struct in_addr, struct _acl_ip_data *));
56static struct _acl_ip_data *aclSplayIp _PARAMS((struct in_addr, struct _acl_ip_data *));
67508012 57static void aclDestroyTimeList _PARAMS((struct _acl_time_data * data));
0ee4272b 58static int aclMatchDomainList _PARAMS((wordlist *, const char *));
59static int aclMatchAclList _PARAMS((const struct _acl_list *, aclCheck_t *));
67508012 60static int aclMatchInteger _PARAMS((intlist * data, int i));
61static int aclMatchIp _PARAMS((struct _acl_ip_data * data, struct in_addr c));
67508012 62static int aclMatchTime _PARAMS((struct _acl_time_data * data, time_t when));
94103840 63static int aclMatchIdent _PARAMS((wordlist * data, const char *ident));
0ee4272b 64static squid_acl aclType _PARAMS((const char *s));
65static int decode_addr _PARAMS((const char *, struct in_addr *, struct in_addr *));
fda94b88 66static void aclParseIpList _PARAMS((void *curlist));
67static void aclParseDomainList _PARAMS((void *curlist));
68static void aclParseIntlist _PARAMS((void *curlist));
69static void aclParseWordList _PARAMS((void *curlist));
70static void aclParseProtoList _PARAMS((void *curlist));
71static void aclParseMethodList _PARAMS((void *curlist));
72static void aclParseTimeSpec _PARAMS((void *curlist));
8203a132 73
c10aaf05 74char *
75strtokFile(void)
76{
77 char *t, *fn;
78 LOCAL_ARRAY(char, buf, 256);
79
e5f6c5c2 80 strtok_again:
c10aaf05 81 if (!aclFromFile) {
82 t = (strtok(NULL, w_space));
83 if (t && (*t == '\"' || *t == '\'')) {
84 /* quote found, start reading from file */
85 fn = ++t;
e5f6c5c2 86 while (*t && *t != '\"' && *t != '\'')
87 t++;
c10aaf05 88 *t = '\0';
89 if ((aclFile = fopen(fn, "r")) == NULL) {
90 debug(28, 0, "strtokFile: %s not found\n", fn);
e5f6c5c2 91 return (NULL);
c10aaf05 92 }
93 aclFromFile = 1;
94 } else {
e5f6c5c2 95 return (t);
c10aaf05 96 }
97 }
98 /* aclFromFile */
99 if (fgets(buf, 256, aclFile) == NULL) {
100 /* stop reading from file */
101 fclose(aclFile);
102 aclFromFile = 0;
103 goto strtok_again;
104 } else {
105 t = buf;
106 /* skip leading and trailing white space */
107 t += strspn(buf, w_space);
108 t[strcspn(t, w_space)] = '\0';
e5f6c5c2 109 return (t);
c10aaf05 110 }
111}
112
8203a132 113static squid_acl
0ee4272b 114aclType(const char *s)
7dd57fa2 115{
116 if (!strcmp(s, "src"))
117 return ACL_SRC_IP;
30a4f2a8 118 if (!strcmp(s, "dst"))
119 return ACL_DST_IP;
7dd57fa2 120 if (!strcmp(s, "domain"))
121 return ACL_DST_DOMAIN;
f88bb09c 122 if (!strcmp(s, "dstdomain"))
123 return ACL_DST_DOMAIN;
124 if (!strcmp(s, "srcdomain"))
125 return ACL_SRC_DOMAIN;
7dd57fa2 126 if (!strcmp(s, "time"))
127 return ACL_TIME;
128 if (!strcmp(s, "pattern"))
6e40f263 129 return ACL_URLPATH_REGEX;
130 if (!strcmp(s, "urlpath_regex"))
131 return ACL_URLPATH_REGEX;
132 if (!strcmp(s, "url_regex"))
7dd57fa2 133 return ACL_URL_REGEX;
134 if (!strcmp(s, "port"))
135 return ACL_URL_PORT;
136 if (!strcmp(s, "user"))
137 return ACL_USER;
30a4f2a8 138 if (!strncmp(s, "proto", 5))
7dd57fa2 139 return ACL_PROTO;
92a6f4b1 140 if (!strcmp(s, "method"))
141 return ACL_METHOD;
d3416189 142 if (!strcmp(s, "browser"))
143 return ACL_BROWSER;
7dd57fa2 144 return ACL_NONE;
145}
146
8203a132 147struct _acl *
0ee4272b 148aclFindByName(const char *name)
7dd57fa2 149{
1969665d 150 struct _acl *a;
151 for (a = AclList; a; a = a->next)
152 if (!strcasecmp(a->name, name))
153 return a;
154 return NULL;
7dd57fa2 155}
156
157
fda94b88 158static void
159aclParseIntlist(void *curlist)
7dd57fa2 160{
fda94b88 161 intlist **Tail;
7dd57fa2 162 intlist *q = NULL;
163 char *t = NULL;
fda94b88 164 for (Tail = curlist; *Tail; Tail = &((*Tail)->next));
c10aaf05 165 while ((t = strtokFile())) {
30a4f2a8 166 q = xcalloc(1, sizeof(intlist));
7dd57fa2 167 q->i = atoi(t);
168 *(Tail) = q;
169 Tail = &q->next;
170 }
7dd57fa2 171}
172
fda94b88 173static void
174aclParseProtoList(void *curlist)
7dd57fa2 175{
fda94b88 176 intlist **Tail;
7dd57fa2 177 intlist *q = NULL;
178 char *t = NULL;
fda94b88 179 for (Tail = curlist; *Tail; Tail = &((*Tail)->next));
c10aaf05 180 while ((t = strtokFile())) {
30a4f2a8 181 q = xcalloc(1, sizeof(intlist));
92a6f4b1 182 q->i = (int) urlParseProtocol(t);
1969665d 183 *(Tail) = q;
184 Tail = &q->next;
7dd57fa2 185 }
7dd57fa2 186}
92a6f4b1 187
fda94b88 188static void
189aclParseMethodList(void *curlist)
92a6f4b1 190{
fda94b88 191 intlist **Tail;
92a6f4b1 192 intlist *q = NULL;
193 char *t = NULL;
fda94b88 194 for (Tail = curlist; *Tail; Tail = &((*Tail)->next));
c10aaf05 195 while ((t = strtokFile())) {
30a4f2a8 196 q = xcalloc(1, sizeof(intlist));
92a6f4b1 197 q->i = (int) urlParseMethod(t);
198 *(Tail) = q;
199 Tail = &q->next;
200 }
92a6f4b1 201}
202
30a4f2a8 203/* Decode a ascii representation (asc) of a IP adress, and place
204 * adress and netmask information in addr and mask.
205 */
8203a132 206static int
0ee4272b 207decode_addr(const char *asc, struct in_addr *addr, struct in_addr *mask)
7dd57fa2 208{
5ad764e2 209 u_num32 a = 0;
ef2d27ff 210 int a1 = 0, a2 = 0, a3 = 0, a4 = 0;
5ad764e2 211 struct hostent *hp = NULL;
30a4f2a8 212
213 switch (sscanf(asc, "%d.%d.%d.%d", &a1, &a2, &a3, &a4)) {
214 case 4: /* a dotted quad */
451bf90b 215 if ((a = (u_num32) inet_addr(asc)) != inaddr_none ||
30a4f2a8 216 !strcmp(asc, "255.255.255.255")) {
217 addr->s_addr = a;
218 /* inet_addr() outputs in network byte order */
219 }
220 break;
221 case 1: /* a significant bits value for a mask */
222 if (a1 >= 0 && a1 < 33) {
ca98227c 223 addr->s_addr = a1 ? htonl(0xfffffffful << (32 - a1)) : 0;
30a4f2a8 224 break;
225 }
226 default:
e668f67c 227 /* Note, must use plain gethostbyname() here because at startup
8eb58c9c 228 * ipcache hasn't been initialized */
e668f67c 229 if ((hp = gethostbyname(asc)) != NULL) {
230 *addr = inaddrFromHostent(hp);
30a4f2a8 231 } else {
232 /* XXX: Here we could use getnetbyname */
e668f67c 233 debug(28, 0, "decode_addr: Invalid IP address or hostname '%s'\n", asc);
30a4f2a8 234 return 0; /* This is not valid address */
235 }
236 break;
237 }
238
239 if (mask != NULL) { /* mask == NULL if called to decode a netmask */
240
241 /* Guess netmask */
ff8d0ea6 242 a = (u_num32) ntohl(addr->s_addr);
86ee2017 243 if (!(a & 0xFFFFFFFFul))
244 mask->s_addr = htonl(0x00000000ul);
30a4f2a8 245 else if (!(a & 0x00FFFFFF))
86ee2017 246 mask->s_addr = htonl(0xFF000000ul);
30a4f2a8 247 else if (!(a & 0x0000FFFF))
86ee2017 248 mask->s_addr = htonl(0xFFFF0000ul);
30a4f2a8 249 else if (!(a & 0x000000FF))
86ee2017 250 mask->s_addr = htonl(0xFFFFFF00ul);
30a4f2a8 251 else
86ee2017 252 mask->s_addr = htonl(0xFFFFFFFFul);
30a4f2a8 253 }
254 return 1;
255}
256
dfc52ac5 257static struct _acl_ip_data *
258aclParseIpData(const char *t)
259{
260 LOCAL_ARRAY(char, addr1, 256);
261 LOCAL_ARRAY(char, addr2, 256);
262 LOCAL_ARRAY(char, mask, 256);
263 struct _acl_ip_data *q = xcalloc(1, sizeof(struct _acl_ip_data));
264 if (!strcasecmp(t, "all")) {
265 q->addr1.s_addr = 0;
266 q->addr2.s_addr = 0;
267 q->mask.s_addr = 0;
268 return q;
269 }
dfc52ac5 270 if (sscanf(t, "%[0-9.]-%[0-9.]/%[0-9.]", addr1, addr2, mask) == 3) {
271 (void) 0;
272 } else if (sscanf(t, "%[0-9.]-%[0-9.]", addr1, addr2) == 2) {
273 mask[0] = '\0';
274 } else if (sscanf(t, "%[0-9.]/%[0-9.]", addr1, mask) == 2) {
275 addr2[0] = '\0';
276 } else if (sscanf(t, "%[0-9.]", addr1) == 1) {
277 addr2[0] = '\0';
278 mask[0] = '\0';
279 } else if (sscanf(t, "%[^/]/%s", addr1, mask) == 2) {
280 addr2[0] = '\0';
281 } else if (sscanf(t, "%s", addr1) == 1) {
282 addr2[0] = '\0';
283 mask[0] = '\0';
284 } else {
fda94b88 285 debug(28, 0, "aclParseIpData: Bad host/IP: '%s'\n", t);
dfc52ac5 286 safe_free(q);
287 return NULL;
288 }
289 /* Decode addr1 */
290 if (!decode_addr(addr1, &q->addr1, &q->mask)) {
291 debug(28, 0, "%s line %d: %s\n",
292 cfg_filename, config_lineno, config_input_line);
fda94b88 293 debug(28, 0, "aclParseIpData: Ignoring invalid IP acl entry: unknown first address '%s'\n", addr1);
dfc52ac5 294 safe_free(q);
295 return NULL;
296 }
297 /* Decode addr2 */
298 if (*addr2 && !decode_addr(addr2, &q->addr2, &q->mask)) {
299 debug(28, 0, "%s line %d: %s\n",
300 cfg_filename, config_lineno, config_input_line);
fda94b88 301 debug(28, 0, "aclParseIpData: Ignoring invalid IP acl entry: unknown second address '%s'\n", addr2);
dfc52ac5 302 safe_free(q);
303 return NULL;
304 }
305 /* Decode mask */
306 if (*mask && !decode_addr(mask, &q->mask, NULL)) {
307 debug(28, 0, "%s line %d: %s\n",
308 cfg_filename, config_lineno, config_input_line);
fda94b88 309 debug(28, 0, "aclParseIpData: Ignoring invalid IP acl entry: unknown netmask '%s'\n", mask);
dfc52ac5 310 safe_free(q);
311 return NULL;
312 }
313 q->addr1.s_addr &= q->mask.s_addr;
314 q->addr2.s_addr &= q->mask.s_addr;
315 /* 1.2.3.4/255.255.255.0 --> 1.2.3.0 */
316 return q;
317}
30a4f2a8 318
fda94b88 319static void
320aclParseIpList(void *curlist)
30a4f2a8 321{
cb0486c3 322 char *t = NULL;
b1ef1220 323 struct _acl_ip_data **ip_data = curlist;
7dd57fa2 324 struct _acl_ip_data *q = NULL;
c10aaf05 325 while ((t = strtokFile())) {
dfc52ac5 326 if ((q = aclParseIpData(t)) == NULL)
9e205701 327 continue;
b1ef1220 328 *ip_data = aclSplayInsertIp(q->addr1, q->addr2, q->mask, *ip_data);
329 }
330
331}
332
333static struct _acl_ip_data *
334aclSplayInsertIp(struct in_addr addr1, struct in_addr addr2, struct in_addr mask, struct _acl_ip_data * t)
335{
336 struct _acl_ip_data *new;
337
338 new = xmalloc(sizeof(struct _acl_ip_data));
339 new->addr1 = addr1;
340 new->addr2 = addr2;
341 new->mask = mask;
342 if (t == NULL) {
343 new->left = new->right = NULL;
344 return new;
345 }
346 t = aclSplayIp(addr1, t);
347 if (addr1.s_addr < t->addr1.s_addr) {
348 new->left = t->left;
349 new->right = t;
350 t->left = NULL;
351 return new;
352 } else if (addr1.s_addr > t->addr1.s_addr) {
353 new->right = t->right;
354 new->left = t;
355 t->right = NULL;
356 return new;
357 } else {
358 debug(28, 0, "aclSplayInsertIp: Address is already in the tree, is this going to be a problem?");
359 safe_free(new);
360 return t;
361 }
362}
363
364static struct _acl_ip_data *
365aclSplayIp(struct in_addr addr1, struct _acl_ip_data * t)
366{
367 struct _acl_ip_data N, *l, *r, *y;
368 if (t == NULL)
369 return t;
370 N.left = N.right = NULL;
371 l = r = &N;
372
373 for (;;) {
374 if (addr1.s_addr < t->addr1.s_addr) {
375 if (t->left == NULL)
376 break;
377 if (addr1.s_addr < t->left->addr1.s_addr) {
378 y = t->left; /* rotate right */
379 t->left = y->right;
380 y->right = t;
381 t = y;
382 if (t->left == NULL)
383 break;
384 }
385 r->left = t; /* link right */
386 r = t;
387 t = t->left;
388 } else if (addr1.s_addr > t->addr1.s_addr) {
389 if (t->right == NULL)
390 break;
391 if (addr1.s_addr > t->right->addr1.s_addr) {
392 y = t->right; /* rotate left */
393 t->right = y->left;
394 y->left = t;
395 t = y;
396 if (t->right == NULL)
397 break;
398 }
399 l->right = t; /* link left */
400 l = t;
401 t = t->right;
402 } else {
403 break;
404 }
7dd57fa2 405 }
b1ef1220 406 l->right = t->left; /* assemble */
407 r->left = t->right;
408 t->left = N.right;
409 t->right = N.left;
410 return t;
7dd57fa2 411}
412
fda94b88 413static void
414aclParseTimeSpec(void *curlist)
7dd57fa2 415{
fda94b88 416 struct _acl_time_data *q = NULL;
417 struct _acl_time_data **Tail;
92a6f4b1 418 int h1, m1, h2, m2;
419 char *t = NULL;
fda94b88 420 for (Tail = curlist; *Tail; Tail = &((*Tail)->next));
421 q = xcalloc(1, sizeof(struct _acl_time_data));
c10aaf05 422 while ((t = strtokFile())) {
92a6f4b1 423 if (*t < '0' || *t > '9') {
424 /* assume its day-of-week spec */
425 while (*t) {
426 switch (*t++) {
427 case 'S':
fda94b88 428 q->weekbits |= ACL_SUNDAY;
92a6f4b1 429 break;
430 case 'M':
fda94b88 431 q->weekbits |= ACL_MONDAY;
92a6f4b1 432 break;
433 case 'T':
fda94b88 434 q->weekbits |= ACL_TUESDAY;
92a6f4b1 435 break;
436 case 'W':
fda94b88 437 q->weekbits |= ACL_WEDNESDAY;
92a6f4b1 438 break;
439 case 'H':
fda94b88 440 q->weekbits |= ACL_THURSDAY;
92a6f4b1 441 break;
442 case 'F':
fda94b88 443 q->weekbits |= ACL_FRIDAY;
92a6f4b1 444 break;
445 case 'A':
fda94b88 446 q->weekbits |= ACL_SATURDAY;
92a6f4b1 447 break;
30a4f2a8 448 case 'D':
fda94b88 449 q->weekbits |= ACL_WEEKDAYS;
30a4f2a8 450 break;
92a6f4b1 451 default:
b8de7ebe 452 debug(28, 0, "%s line %d: %s\n",
453 cfg_filename, config_lineno, config_input_line);
92a6f4b1 454 debug(28, 0, "aclParseTimeSpec: Bad Day '%c'\n",
455 *t);
456 break;
457 }
458 }
459 } else {
460 /* assume its time-of-day spec */
461 if (sscanf(t, "%d:%d-%d:%d", &h1, &m1, &h2, &m2) < 4) {
b8de7ebe 462 debug(28, 0, "%s line %d: %s\n",
463 cfg_filename, config_lineno, config_input_line);
92a6f4b1 464 debug(28, 0, "aclParseTimeSpec: Bad time range '%s'\n",
465 t);
fda94b88 466 xfree(q);
467 return;
92a6f4b1 468 }
fda94b88 469 q->start = h1 * 60 + m1;
470 q->stop = h2 * 60 + m2;
471 if (q->start > q->stop) {
b8de7ebe 472 debug(28, 0, "%s line %d: %s\n",
473 cfg_filename, config_lineno, config_input_line);
92a6f4b1 474 debug(28, 0, "aclParseTimeSpec: Reversed time range '%s'\n",
475 t);
fda94b88 476 xfree(q);
477 return;
92a6f4b1 478 }
479 }
480 }
fda94b88 481 if (q->start == 0 && q->stop == 0)
482 q->stop = 23 * 60 + 59;
483 if (q->weekbits == 0)
484 q->weekbits = ACL_ALLWEEK;
485 *(Tail) = q;
486 Tail = &q->next;
7dd57fa2 487}
488
fda94b88 489void
490aclParseRegexList(void *curlist, int icase)
7dd57fa2 491{
fda94b88 492 relist **Tail;
7dd57fa2 493 relist *q = NULL;
494 char *t = NULL;
1969665d 495 regex_t comp;
008fcc7b 496 int errcode;
497 int flags = REG_EXTENDED | REG_NOSUB;
fda94b88 498 for (Tail = curlist; *Tail; Tail = &((*Tail)->next));
33cdd606 499 if (icase)
500 flags |= REG_ICASE;
c10aaf05 501 while ((t = strtokFile())) {
008fcc7b 502 if ((errcode = regcomp(&comp, t, flags)) != 0) {
503 char errbuf[256];
504 regerror(errcode, &comp, errbuf, sizeof errbuf);
b8de7ebe 505 debug(28, 0, "%s line %d: %s\n",
506 cfg_filename, config_lineno, config_input_line);
008fcc7b 507 debug(28, 0, "aclParseRegexList: Invalid regular expression '%s': %s\n",
508 t, errbuf);
1969665d 509 continue;
510 }
30a4f2a8 511 q = xcalloc(1, sizeof(relist));
1969665d 512 q->pattern = xstrdup(t);
513 q->regex = comp;
514 *(Tail) = q;
515 Tail = &q->next;
7dd57fa2 516 }
7dd57fa2 517}
518
fda94b88 519static void
520aclParseWordList(void *curlist)
7dd57fa2 521{
fda94b88 522 wordlist **Tail;
7dd57fa2 523 wordlist *q = NULL;
524 char *t = NULL;
fda94b88 525 for (Tail = curlist; *Tail; Tail = &((*Tail)->next));
c10aaf05 526 while ((t = strtokFile())) {
30a4f2a8 527 q = xcalloc(1, sizeof(wordlist));
7dd57fa2 528 q->key = xstrdup(t);
1969665d 529 *(Tail) = q;
530 Tail = &q->next;
7dd57fa2 531 }
7dd57fa2 532}
533
fda94b88 534
535static void
536aclParseDomainList(void *curlist)
f88bb09c 537{
fda94b88 538 wordlist **Tail;
f88bb09c 539 wordlist *q = NULL;
540 char *t = NULL;
fda94b88 541 for (Tail = curlist; *Tail; Tail = &((*Tail)->next));
c10aaf05 542 while ((t = strtokFile())) {
f88bb09c 543 Tolower(t);
544 q = xcalloc(1, sizeof(wordlist));
545 q->key = xstrdup(t);
546 *(Tail) = q;
547 Tail = &q->next;
548 }
f88bb09c 549}
7dd57fa2 550
551
8203a132 552void
0673c0ba 553aclParseAclLine(void)
7dd57fa2 554{
555 /* we're already using strtok() to grok the line */
556 char *t = NULL;
557 struct _acl *A = NULL;
fda94b88 558 LOCAL_ARRAY(char, aclname, ACL_NAME_SZ);
559 squid_acl acltype;
7dd57fa2 560
7dd57fa2 561 /* snarf the ACL name */
562 if ((t = strtok(NULL, w_space)) == NULL) {
b8de7ebe 563 debug(28, 0, "%s line %d: %s\n",
564 cfg_filename, config_lineno, config_input_line);
92a6f4b1 565 debug(28, 0, "aclParseAclLine: missing ACL name.\n");
92a6f4b1 566 return;
567 }
fda94b88 568 xstrncpy(aclname, t, ACL_NAME_SZ);
569 /* snarf the ACL type */
570 if ((t = strtok(NULL, w_space)) == NULL) {
b8de7ebe 571 debug(28, 0, "%s line %d: %s\n",
572 cfg_filename, config_lineno, config_input_line);
fda94b88 573 debug(28, 0, "aclParseAclLine: missing ACL type.\n");
7dd57fa2 574 return;
575 }
fda94b88 576 if ((acltype = aclType(t)) == ACL_NONE) {
b8de7ebe 577 debug(28, 0, "%s line %d: %s\n",
578 cfg_filename, config_lineno, config_input_line);
fda94b88 579 debug(28, 0, "aclParseAclLine: Invalid ACL type '%s'\n", t);
7dd57fa2 580 return;
581 }
9963e5a3 582 if ((A = aclFindByName(aclname)) == NULL) {
b1ef1220 583 debug(28, 3, "aclParseAclLine: Creating ACL '%s'\n", aclname);
52cd89fd 584 A = xcalloc(1, sizeof(struct _acl));
fda94b88 585 xstrncpy(A->name, aclname, ACL_NAME_SZ);
586 A->type = acltype;
52cd89fd 587 A->cfgline = xstrdup(config_input_line);
588 *AclListTail = A;
589 AclListTail = &A->next;
fda94b88 590 } else {
52cd89fd 591 if (acltype != A->type) {
fda94b88 592 debug(28, 0, "aclParseAclLine: ACL '%s' already exists with different type, skipping.\n", A->name);
593 return;
594 }
9963e5a3 595 debug(28, 3, "aclParseAclLine: Appending to '%s'\n", aclname);
fda94b88 596 }
597 switch (A->type) {
7dd57fa2 598 case ACL_SRC_IP:
30a4f2a8 599 case ACL_DST_IP:
fda94b88 600 aclParseIpList(&(A->data));
7dd57fa2 601 break;
f88bb09c 602 case ACL_SRC_DOMAIN:
7dd57fa2 603 case ACL_DST_DOMAIN:
fda94b88 604 aclParseDomainList(&A->data);
7dd57fa2 605 break;
606 case ACL_TIME:
fda94b88 607 aclParseTimeSpec(&A->data);
7dd57fa2 608 break;
609 case ACL_URL_REGEX:
6e40f263 610 case ACL_URLPATH_REGEX:
fda94b88 611 aclParseRegexList(&A->data, 0);
7dd57fa2 612 break;
613 case ACL_URL_PORT:
fda94b88 614 aclParseIntlist(&A->data);
7dd57fa2 615 break;
616 case ACL_USER:
94103840 617 Config.identLookup = 1;
fda94b88 618 aclParseWordList(&A->data);
7dd57fa2 619 break;
620 case ACL_PROTO:
fda94b88 621 aclParseProtoList(&A->data);
7dd57fa2 622 break;
92a6f4b1 623 case ACL_METHOD:
fda94b88 624 aclParseMethodList(&A->data);
92a6f4b1 625 break;
d3416189 626 case ACL_BROWSER:
fda94b88 627 aclParseRegexList(&A->data, 0);
d3416189 628 break;
7dd57fa2 629 case ACL_NONE:
630 default:
fda94b88 631 debug_trap("Bad ACL type");
632 break;
7dd57fa2 633 }
7dd57fa2 634}
635
e92d33a5 636/* maex@space.net (06.09.96)
5e79098a 637 * get (if any) the URL from deny_info for a certain acl
e92d33a5 638 */
8203a132 639char *
0ee4272b 640aclGetDenyInfoUrl(struct _acl_deny_info_list **head, const char *name)
e92d33a5 641{
642 struct _acl_deny_info_list *A = NULL;
643 struct _acl_name_list *L = NULL;
644
645 A = *head;
646 if (NULL == *head) /* empty list */
647 return (NULL);
648 while (A) {
649 L = A->acl_list;
650 if (NULL == L) /* empty list should never happen, but in case */
651 continue;
652 while (L) {
653 if (!strcmp(name, L->name))
654 return (A->url);
655 L = L->next;
656 }
657 A = A->next;
658 }
659 return (NULL);
660}
661/* maex@space.net (05.09.96)
5e79098a 662 * get the info for redirecting "access denied" to info pages
663 * TODO (probably ;-)
664 * currently there is no optimization for
665 * - more than one deny_info line with the same url
666 * - a check, whether the given acl really is defined
667 * - a check, whether an acl is added more than once for the same url
e92d33a5 668 */
8203a132 669void
670aclParseDenyInfoLine(struct _acl_deny_info_list **head)
e92d33a5 671{
672 char *t = NULL;
673 struct _acl_deny_info_list *A = NULL;
674 struct _acl_deny_info_list *B = NULL;
675 struct _acl_deny_info_list **T = NULL;
676 struct _acl_name_list *L = NULL;
677 struct _acl_name_list **Tail = NULL;
678
679 /* first expect an url */
680 if ((t = strtok(NULL, w_space)) == NULL) {
681 debug(28, 0, "%s line %d: %s\n",
682 cfg_filename, config_lineno, config_input_line);
683 debug(28, 0, "aclParseDenyInfoLine: missing 'url' parameter.\n");
684 return;
685 }
686 A = xcalloc(1, sizeof(struct _acl_deny_info_list));
d5aa0e3b 687 xstrncpy(A->url, t, MAX_URL);
e92d33a5 688 A->next = (struct _acl_deny_info_list *) NULL;
689 /* next expect a list of ACL names */
690 Tail = &A->acl_list;
691 while ((t = strtok(NULL, w_space))) {
692 L = xcalloc(1, sizeof(struct _acl_name_list));
d5aa0e3b 693 xstrncpy(L->name, t, ACL_NAME_SZ);
e92d33a5 694 *Tail = L;
695 Tail = &L->next;
696 }
697 if (A->acl_list == NULL) {
698 debug(28, 0, "%s line %d: %s\n",
699 cfg_filename, config_lineno, config_input_line);
700 debug(28, 0, "aclParseDenyInfoLine: deny_info line contains no ACL's, skipping\n");
701 xfree(A);
702 return;
703 }
704 for (B = *head, T = head; B; T = &B->next, B = B->next); /* find the tail */
705 *T = A;
706}
707
8203a132 708void
709aclParseAccessLine(struct _acl_access **head)
7dd57fa2 710{
711 char *t = NULL;
712 struct _acl_access *A = NULL;
92a6f4b1 713 struct _acl_access *B = NULL;
714 struct _acl_access **T = NULL;
7dd57fa2 715 struct _acl_list *L = NULL;
8213067d 716 struct _acl_list **Tail = NULL;
7dd57fa2 717 struct _acl *a = NULL;
718
719 /* first expect either 'allow' or 'deny' */
720 if ((t = strtok(NULL, w_space)) == NULL) {
b8de7ebe 721 debug(28, 0, "%s line %d: %s\n",
722 cfg_filename, config_lineno, config_input_line);
92a6f4b1 723 debug(28, 0, "aclParseAccessLine: missing 'allow' or 'deny'.\n");
7dd57fa2 724 return;
725 }
30a4f2a8 726 A = xcalloc(1, sizeof(struct _acl_access));
7dd57fa2 727 if (!strcmp(t, "allow"))
728 A->allow = 1;
729 else if (!strcmp(t, "deny"))
730 A->allow = 0;
731 else {
b8de7ebe 732 debug(28, 0, "%s line %d: %s\n",
733 cfg_filename, config_lineno, config_input_line);
92a6f4b1 734 debug(28, 0, "aclParseAccessLine: expecting 'allow' or 'deny', got '%s'.\n", t);
7dd57fa2 735 xfree(A);
736 return;
737 }
738
739 /* next expect a list of ACL names, possibly preceeded
740 * by '!' for negation */
8213067d 741 Tail = &A->acl_list;
7dd57fa2 742 while ((t = strtok(NULL, w_space))) {
30a4f2a8 743 L = xcalloc(1, sizeof(struct _acl_list));
1969665d 744 L->op = 1; /* defaults to non-negated */
7dd57fa2 745 if (*t == '!') {
1969665d 746 /* negated ACL */
747 L->op = 0;
748 t++;
7dd57fa2 749 }
b954a582 750 debug(28, 3, "aclParseAccessLine: looking for ACL name '%s'\n", t);
7dd57fa2 751 a = aclFindByName(t);
752 if (a == NULL) {
b8de7ebe 753 debug(28, 0, "%s line %d: %s\n",
754 cfg_filename, config_lineno, config_input_line);
837dcbb9 755 debug(28, 0, "aclParseAccessLine: ACL name '%s' not found.\n", t);
1969665d 756 xfree(L);
757 continue;
7dd57fa2 758 }
759 L->acl = a;
760 *Tail = L;
761 Tail = &L->next;
762 }
837dcbb9 763 if (A->acl_list == NULL) {
b8de7ebe 764 debug(28, 0, "%s line %d: %s\n",
765 cfg_filename, config_lineno, config_input_line);
89b37f2a 766 debug(28, 0, "aclParseAccessLine: Access line contains no ACL's, skipping\n");
837dcbb9 767 xfree(A);
768 return;
769 }
3003c0f3 770 A->cfgline = xstrdup(config_input_line);
540830c4 771 for (B = *head, T = head; B; T = &B->next, B = B->next); /* find the tail */
92a6f4b1 772 *T = A;
7dd57fa2 773}
774
8203a132 775static int
b1ef1220 776aclMatchIp(struct _acl_ip_data * data, struct in_addr c)
abf6b4de 777{
778 struct in_addr h;
30a4f2a8 779 unsigned long lh, la1, la2;
b1ef1220 780 struct _acl_ip_data *root;
30a4f2a8 781
b1ef1220 782 h.s_addr = c.s_addr & data->mask.s_addr;
783 debug(28, 3, "aclMatchIp: h = %s\n", inet_ntoa(h));
784 debug(28, 3, "aclMatchIp: addr1 = %s\n", inet_ntoa(data->addr1));
785 debug(28, 3, "aclMatchIp: addr2 = %s\n", inet_ntoa(data->addr2));
786 root = aclSplayIp(h, data);
787 if (h.s_addr == root->addr1.s_addr) {
30a4f2a8 788 if (!data->addr2.s_addr) {
b1ef1220 789 debug(28, 3, "aclMatchIp: returning 1\n");
790 return 1;
30a4f2a8 791 } else {
792 /* This is a range check */
793 lh = ntohl(h.s_addr);
794 la1 = ntohl(data->addr1.s_addr);
795 la2 = ntohl(data->addr2.s_addr);
796 if (lh >= la1 && lh <= la2) {
797 debug(28, 3, "aclMatchIp: returning 1\n");
798 return 1;
799 }
837dcbb9 800 }
abf6b4de 801 }
b954a582 802 debug(28, 3, "aclMatchIp: returning 0\n");
abf6b4de 803 return 0;
804}
805
8203a132 806static int
fe4e214f 807aclMatchDomainList(wordlist * data, const char *host)
f88bb09c 808{
c10aaf05 809 wordlist *first, *prev;
810
f88bb09c 811 if (host == NULL)
812 return 0;
813 debug(28, 3, "aclMatchDomainList: checking '%s'\n", host);
c10aaf05 814 first = data;
815 prev = NULL;
f88bb09c 816 for (; data; data = data->next) {
817 debug(28, 3, "aclMatchDomainList: looking for '%s'\n", data->key);
c10aaf05 818 if (matchDomainName(data->key, host)) {
819 if (prev != NULL) {
820 /* shift the element just found to the second position
e5f6c5c2 821 * in the list */
c10aaf05 822 prev->next = data->next;
823 data->next = first->next;
824 first->next = data;
825 }
f88bb09c 826 return 1;
e5f6c5c2 827 }
c10aaf05 828 prev = data;
f88bb09c 829 }
830 return 0;
831}
30a4f2a8 832
33cdd606 833int
0ee4272b 834aclMatchRegex(relist * data, const char *word)
abf6b4de 835{
c10aaf05 836 relist *first, *prev;
234967c9 837 if (word == NULL)
838 return 0;
b954a582 839 debug(28, 3, "aclMatchRegex: checking '%s'\n", word);
c10aaf05 840 first = data;
841 prev = NULL;
abf6b4de 842 while (data) {
b954a582 843 debug(28, 3, "aclMatchRegex: looking for '%s'\n", data->pattern);
c10aaf05 844 if (regexec(&data->regex, word, 0, 0, 0) == 0) {
845 if (prev != NULL) {
846 /* shift the element just found to the second position
e5f6c5c2 847 * in the list */
c10aaf05 848 prev->next = data->next;
849 data->next = first->next;
850 first->next = data;
851 }
abf6b4de 852 return 1;
c10aaf05 853 }
854 prev = data;
abf6b4de 855 data = data->next;
856 }
857 return 0;
858}
30a4f2a8 859
94103840 860static int
861aclMatchIdent(wordlist * data, const char *ident)
862{
863 if (ident == NULL)
864 return 0;
865 debug(28, 3, "aclMatchIdent: checking '%s'\n", ident);
866 while (data) {
867 debug(28, 3, "aclMatchIdent: looking for '%s'\n", data->key);
868 if (strcmp(data->key, "REQUIRED") == 0 && *ident != '\0')
869 return 1;
870 if (strcmp(data->key, ident) == 0)
871 return 1;
872 data = data->next;
873 }
874 return 0;
875}
876
8203a132 877static int
878aclMatchInteger(intlist * data, int i)
abf6b4de 879{
c10aaf05 880 intlist *first, *prev;
881 first = data;
882 prev = NULL;
abf6b4de 883 while (data) {
c10aaf05 884 if (data->i == i) {
885 if (prev != NULL) {
886 /* shift the element just found to the second position
e5f6c5c2 887 * in the list */
c10aaf05 888 prev->next = data->next;
889 data->next = first->next;
890 first->next = data;
891 }
abf6b4de 892 return 1;
c10aaf05 893 }
894 prev = data;
abf6b4de 895 data = data->next;
896 }
897 return 0;
898}
899
8203a132 900static int
901aclMatchTime(struct _acl_time_data *data, time_t when)
92a6f4b1 902{
540830c4 903 static time_t last_when = 0;
904 static struct tm tm;
905 time_t t;
92a6f4b1 906
540830c4 907 if (when != last_when) {
908 last_when = when;
30a4f2a8 909 xmemcpy(&tm, localtime(&when), sizeof(struct tm));
540830c4 910 }
540830c4 911 t = (time_t) (tm.tm_hour * 60 + tm.tm_min);
9bf12439 912 debug(28, 3, "aclMatchTime: checking %d in %d-%d, weekbits=%x\n",
913 (int) t, (int) data->start, (int) data->stop, data->weekbits);
914
540830c4 915 if (t < data->start || t > data->stop)
916 return 0;
234967c9 917 return data->weekbits & (1 << tm.tm_wday) ? 1 : 0;
92a6f4b1 918}
919
8203a132 920int
921aclMatchAcl(struct _acl *acl, aclCheck_t * checklist)
1969665d 922{
0ee4272b 923 const request_t *r = checklist->request;
924 const ipcache_addrs *ia = NULL;
925 const char *fqdn = NULL;
30a4f2a8 926 int k;
1969665d 927 if (!acl)
928 return 0;
b954a582 929 debug(28, 3, "aclMatchAcl: checking '%s'\n", acl->cfgline);
1969665d 930 switch (acl->type) {
931 case ACL_SRC_IP:
99d829f9 932 return aclMatchIp(acl->data, checklist->src_addr);
983061ed 933 /* NOTREACHED */
30a4f2a8 934 case ACL_DST_IP:
e5f6c5c2 935 ia = ipcache_gethostbyname(r->host, IP_LOOKUP_IF_MISS);
936 if (ia) {
937 for (k = 0; k < (int) ia->count; k++) {
938 checklist->dst_addr = ia->in_addrs[k];
665d36de 939 if (aclMatchIp(acl->data, checklist->dst_addr))
940 return 1;
941 }
942 return 0;
943 } else if (checklist->state[ACL_DST_IP] == ACL_LOOKUP_NONE) {
30a4f2a8 944 debug(28, 3, "aclMatchAcl: Can't yet compare '%s' ACL for '%s'\n",
99d829f9 945 acl->name, r->host);
665d36de 946 checklist->state[ACL_DST_IP] = ACL_LOOKUP_NEED;
f88bb09c 947 return 0;
665d36de 948 } else {
949 return aclMatchIp(acl->data, no_addr);
30a4f2a8 950 }
30a4f2a8 951 /* NOTREACHED */
1969665d 952 case ACL_DST_DOMAIN:
4d650936 953 if ((ia = ipcacheCheckNumeric(r->host)) == NULL)
954 return aclMatchDomainList(acl->data, r->host);
955 fqdn = fqdncache_gethostbyaddr(ia->in_addrs[0], FQDN_LOOKUP_IF_MISS);
956 if (fqdn)
957 return aclMatchDomainList(acl->data, fqdn);
958 if (checklist->state[ACL_DST_DOMAIN] == ACL_LOOKUP_NONE) {
959 debug(28, 3, "aclMatchAcl: Can't yet compare '%s' ACL for '%s'\n",
960 acl->name, inet_ntoa(ia->in_addrs[0]));
961 checklist->state[ACL_DST_DOMAIN] = ACL_LOOKUP_NEED;
962 return 0;
963 }
964 return aclMatchDomainList(acl->data, "none");
f88bb09c 965 /* NOTREACHED */
966 case ACL_SRC_DOMAIN:
967 fqdn = fqdncache_gethostbyaddr(checklist->src_addr, FQDN_LOOKUP_IF_MISS);
665d36de 968 if (fqdn) {
969 return aclMatchDomainList(acl->data, fqdn);
970 } else if (checklist->state[ACL_SRC_DOMAIN] == ACL_LOOKUP_NONE) {
f88bb09c 971 debug(28, 3, "aclMatchAcl: Can't yet compare '%s' ACL for '%s'\n",
972 acl->name, inet_ntoa(checklist->src_addr));
665d36de 973 checklist->state[ACL_SRC_DOMAIN] = ACL_LOOKUP_NEED;
f88bb09c 974 return 0;
665d36de 975 } else {
9d48b9aa 976 return aclMatchDomainList(acl->data, "none");
f88bb09c 977 }
983061ed 978 /* NOTREACHED */
1969665d 979 case ACL_TIME:
b8de7ebe 980 return aclMatchTime(acl->data, squid_curtime);
983061ed 981 /* NOTREACHED */
6e40f263 982 case ACL_URLPATH_REGEX:
99d829f9 983 return aclMatchRegex(acl->data, r->urlpath);
983061ed 984 /* NOTREACHED */
6e40f263 985 case ACL_URL_REGEX:
986 return aclMatchRegex(acl->data, urlCanonical(r, NULL));
987 /* NOTREACHED */
1969665d 988 case ACL_URL_PORT:
99d829f9 989 return aclMatchInteger(acl->data, r->port);
983061ed 990 /* NOTREACHED */
1969665d 991 case ACL_USER:
94103840 992 /* debug(28, 0, "aclMatchAcl: ACL_USER unimplemented\n"); */
993 /* return 0; */
994 return aclMatchIdent(acl->data, checklist->ident);
983061ed 995 /* NOTREACHED */
1969665d 996 case ACL_PROTO:
99d829f9 997 return aclMatchInteger(acl->data, r->protocol);
983061ed 998 /* NOTREACHED */
92a6f4b1 999 case ACL_METHOD:
99d829f9 1000 return aclMatchInteger(acl->data, r->method);
983061ed 1001 /* NOTREACHED */
d3416189 1002 case ACL_BROWSER:
1003 return aclMatchRegex(acl->data, checklist->browser);
1004 /* NOTREACHED */
1969665d 1005 case ACL_NONE:
1006 default:
1007 debug(28, 0, "aclMatchAcl: '%s' has bad type %d\n",
1008 acl->name, acl->type);
1009 return 0;
1010 }
30a4f2a8 1011 /* NOTREACHED */
1969665d 1012}
1013
8203a132 1014static int
fe4e214f 1015aclMatchAclList(const struct _acl_list *list, aclCheck_t * checklist)
1969665d 1016{
1969665d 1017 while (list) {
e92d33a5 1018 AclMatchedName = list->acl->name;
f88bb09c 1019 debug(28, 3, "aclMatchAclList: checking %s%s\n",
ffa8aa23 1020 list->op ? null_string : "!", list->acl->name);
99d829f9 1021 if (aclMatchAcl(list->acl, checklist) != list->op) {
b954a582 1022 debug(28, 3, "aclMatchAclList: returning 0\n");
abf6b4de 1023 return 0;
837dcbb9 1024 }
abf6b4de 1025 list = list->next;
1969665d 1026 }
b954a582 1027 debug(28, 3, "aclMatchAclList: returning 1\n");
1969665d 1028 return 1;
1029}
1030
8203a132 1031int
fe4e214f 1032aclCheck(const struct _acl_access *A, aclCheck_t * checklist)
1969665d 1033{
1969665d 1034 int allow = 0;
7dd57fa2 1035
92a6f4b1 1036 while (A) {
b954a582 1037 debug(28, 3, "aclCheck: checking '%s'\n", A->cfgline);
1969665d 1038 allow = A->allow;
99d829f9 1039 if (aclMatchAclList(A->acl_list, checklist)) {
b954a582 1040 debug(28, 3, "aclCheck: match found, returning %d\n", allow);
1969665d 1041 return allow;
8213067d 1042 }
92a6f4b1 1043 A = A->next;
7dd57fa2 1044 }
1969665d 1045 return !allow;
1046}
92a6f4b1 1047
8203a132 1048static void
b1ef1220 1049aclDestroyIpList(struct _acl_ip_data * data)
92a6f4b1 1050{
b1ef1220 1051 if (data == NULL)
1052 return;
1053 aclDestroyIpList(data->left);
1054 aclDestroyIpList(data->right);
1055 safe_free(data);
92a6f4b1 1056}
1057
8203a132 1058static void
1059aclDestroyTimeList(struct _acl_time_data *data)
92a6f4b1 1060{
06c55413 1061 struct _acl_time_data *next = NULL;
540830c4 1062 for (; data; data = next) {
1063 next = data->next;
1064 safe_free(data);
1065 }
92a6f4b1 1066}
1067
33cdd606 1068void
8203a132 1069aclDestroyRegexList(struct _relist *data)
92a6f4b1 1070{
06c55413 1071 struct _relist *next = NULL;
540830c4 1072 for (; data; data = next) {
1073 next = data->next;
1074 regfree(&data->regex);
1075 safe_free(data->pattern);
1076 safe_free(data);
1077 }
92a6f4b1 1078}
1079
8203a132 1080void
0673c0ba 1081aclDestroyAcls(void)
540830c4 1082{
1083 struct _acl *a = NULL;
1084 struct _acl *next = NULL;
1085 for (a = AclList; a; a = next) {
1086 next = a->next;
b954a582 1087 debug(28, 3, "aclDestroyAcls: '%s'\n", a->cfgline);
540830c4 1088 switch (a->type) {
1089 case ACL_SRC_IP:
30a4f2a8 1090 case ACL_DST_IP:
540830c4 1091 aclDestroyIpList(a->data);
1092 break;
1093 case ACL_DST_DOMAIN:
c20e73a0 1094 case ACL_SRC_DOMAIN:
540830c4 1095 case ACL_USER:
1096 wordlistDestroy((wordlist **) & a->data);
1097 break;
1098 case ACL_TIME:
1099 aclDestroyTimeList(a->data);
1100 break;
1101 case ACL_URL_REGEX:
6e40f263 1102 case ACL_URLPATH_REGEX:
d3416189 1103 case ACL_BROWSER:
540830c4 1104 aclDestroyRegexList(a->data);
1105 break;
1106 case ACL_URL_PORT:
1107 case ACL_PROTO:
1108 case ACL_METHOD:
1109 intlistDestroy((intlist **) & a->data);
1110 break;
1111 case ACL_NONE:
1112 default:
1113 fatal_dump("aclDestroyAcls: Found ACL_NONE?");
1114 break;
92a6f4b1 1115 }
540830c4 1116 safe_free(a->cfgline);
1117 safe_free(a);
1118 }
1119 AclList = NULL;
1120 AclListTail = &AclList;
92a6f4b1 1121}
1122
8203a132 1123static void
1124aclDestroyAclList(struct _acl_list *list)
92a6f4b1 1125{
540830c4 1126 struct _acl_list *next = NULL;
1127 for (; list; list = next) {
1128 next = list->next;
1129 safe_free(list);
1130 }
92a6f4b1 1131}
1132
8203a132 1133void
1134aclDestroyAccessList(struct _acl_access **list)
92a6f4b1 1135{
540830c4 1136 struct _acl_access *l = NULL;
1137 struct _acl_access *next = NULL;
1138 for (l = *list; l; l = next) {
b954a582 1139 debug(28, 3, "aclDestroyAccessList: '%s'\n", l->cfgline);
540830c4 1140 next = l->next;
1141 aclDestroyAclList(l->acl_list);
1142 l->acl_list = NULL;
1143 safe_free(l->cfgline);
1144 safe_free(l);
1145 }
1146 *list = NULL;
92a6f4b1 1147}
e92d33a5 1148
1149/* maex@space.net (06.09.1996)
f4296e99 1150 * destroy an _acl_deny_info_list */
8203a132 1151void
1152aclDestroyDenyInfoList(struct _acl_deny_info_list **list)
e92d33a5 1153{
1154 struct _acl_deny_info_list *a = NULL;
1155 struct _acl_deny_info_list *a_next = NULL;
1156 struct _acl_name_list *l = NULL;
1157 struct _acl_name_list *l_next = NULL;
1158
1159 for (a = *list; a; a = a_next) {
1160 for (l = a->acl_list; l; l = l_next) {
1161 l_next = l->next;
1162 safe_free(l);
1163 }
1164 a_next = a->next;
1165 safe_free(a);
1166 }
f4296e99 1167 *list = NULL;
e92d33a5 1168}