]> git.ipfire.org Git - thirdparty/squid.git/blob - src/external_acl.cc
Merged from parent (trunk r10600).
[thirdparty/squid.git] / src / external_acl.cc
1
2 /*
3 * $Id$
4 *
5 * DEBUG: section 82 External ACL
6 * AUTHOR: Henrik Nordstrom, MARA Systems AB
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * The contents of this file is Copyright (C) 2002 by MARA Systems AB,
12 * Sweden, unless otherwise is indicated in the specific function. The
13 * author gives his full permission to include this file into the Squid
14 * software product under the terms of the GNU General Public License as
15 * published by the Free Software Foundation; either version 2 of the
16 * License, or (at your option) any later version.
17 *
18 * Squid is the result of efforts by numerous individuals from
19 * the Internet community; see the CONTRIBUTORS file for full
20 * details. Many organizations have provided support for Squid's
21 * development; see the SPONSORS file for full details. Squid is
22 * Copyrighted (C) 2001 by the Regents of the University of
23 * California; see the COPYRIGHT file for full details. Squid
24 * incorporates software developed and/or copyrighted by other
25 * sources; see the CREDITS file for full details.
26 *
27 * This program is free software; you can redistribute it and/or modify
28 * it under the terms of the GNU General Public License as published by
29 * the Free Software Foundation; either version 2 of the License, or
30 * (at your option) any later version.
31 *
32 * This program is distributed in the hope that it will be useful,
33 * but WITHOUT ANY WARRANTY; without even the implied warranty of
34 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 * GNU General Public License for more details.
36 *
37 * You should have received a copy of the GNU General Public License
38 * along with this program; if not, write to the Free Software
39 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
40 *
41 */
42
43 #include "squid.h"
44 #include "CacheManager.h"
45 #include "ExternalACL.h"
46 #include "ExternalACLEntry.h"
47 #include "auth/UserRequest.h"
48 #include "SquidTime.h"
49 #include "Store.h"
50 #include "fde.h"
51 #include "acl/FilledChecklist.h"
52 #include "acl/Acl.h"
53 #if USE_IDENT
54 #include "ident/AclIdent.h"
55 #endif
56 #include "client_side.h"
57 #include "HttpRequest.h"
58 #include "HttpReply.h"
59 #include "auth/Acl.h"
60 #include "auth/Gadgets.h"
61 #include "helper.h"
62 #include "MemBuf.h"
63 #include "rfc1738.h"
64 #include "URLScheme.h"
65 #include "wordlist.h"
66
67 #ifndef DEFAULT_EXTERNAL_ACL_TTL
68 #define DEFAULT_EXTERNAL_ACL_TTL 1 * 60 * 60
69 #endif
70 #ifndef DEFAULT_EXTERNAL_ACL_CHILDREN
71 #define DEFAULT_EXTERNAL_ACL_CHILDREN 5
72 #endif
73
74 typedef struct _external_acl_format external_acl_format;
75
76 static char *makeExternalAclKey(ACLFilledChecklist * ch, external_acl_data * acl_data);
77 static void external_acl_cache_delete(external_acl * def, external_acl_entry * entry);
78 static int external_acl_entry_expired(external_acl * def, external_acl_entry * entry);
79 static int external_acl_grace_expired(external_acl * def, external_acl_entry * entry);
80 static void external_acl_cache_touch(external_acl * def, external_acl_entry * entry);
81 static external_acl_entry *external_acl_cache_add(external_acl * def, const char *key, ExternalACLEntryData const &data);
82
83 /******************************************************************
84 * external_acl directive
85 */
86
87 class external_acl
88 {
89
90 public:
91 external_acl *next;
92
93 void add
94 (ExternalACLEntry *);
95
96 void trimCache();
97
98 int ttl;
99
100 int negative_ttl;
101
102 int grace;
103
104 char *name;
105
106 external_acl_format *format;
107
108 wordlist *cmdline;
109
110 HelperChildConfig children;
111
112 helper *theHelper;
113
114 hash_table *cache;
115
116 dlink_list lru_list;
117
118 int cache_size;
119
120 int cache_entries;
121
122 dlink_list queue;
123
124 /**
125 * Configuration flag. May only be altered by the configuration parser.
126 *
127 * Indicates that all uses of this external_acl_type helper require authentication
128 * details to be processed. If none are available its a fail match.
129 */
130 bool require_auth;
131
132 enum {
133 QUOTE_METHOD_SHELL = 1,
134 QUOTE_METHOD_URL
135 } quote;
136
137 Ip::Address local_addr;
138 };
139
140 struct _external_acl_format {
141 enum format_type {
142 EXT_ACL_UNKNOWN,
143 EXT_ACL_LOGIN,
144 #if USE_IDENT
145 EXT_ACL_IDENT,
146 #endif
147 EXT_ACL_SRC,
148 EXT_ACL_SRCPORT,
149 #if USE_SQUID_EUI
150 EXT_ACL_SRCEUI48,
151 EXT_ACL_SRCEUI64,
152 #endif
153 EXT_ACL_MYADDR,
154 EXT_ACL_MYPORT,
155 EXT_ACL_URI,
156 EXT_ACL_DST,
157 EXT_ACL_PROTO,
158 EXT_ACL_PORT,
159 EXT_ACL_PATH,
160 EXT_ACL_METHOD,
161
162 EXT_ACL_HEADER_REQUEST,
163 EXT_ACL_HEADER_REQUEST_MEMBER,
164 EXT_ACL_HEADER_REQUEST_ID,
165 EXT_ACL_HEADER_REQUEST_ID_MEMBER,
166
167 EXT_ACL_HEADER_REPLY,
168 EXT_ACL_HEADER_REPLY_MEMBER,
169 EXT_ACL_HEADER_REPLY_ID,
170 EXT_ACL_HEADER_REPLY_ID_MEMBER,
171
172 #if USE_SSL
173 EXT_ACL_USER_CERT,
174 EXT_ACL_CA_CERT,
175 EXT_ACL_USER_CERT_RAW,
176 EXT_ACL_USER_CERTCHAIN_RAW,
177 #endif
178 EXT_ACL_EXT_USER,
179 EXT_ACL_END
180 } type;
181 external_acl_format *next;
182 char *header;
183 char *member;
184 char separator;
185 http_hdr_type header_id;
186 };
187
188 /* FIXME: These are not really cbdata, but it is an easy way
189 * to get them pooled, refcounted, accounted and freed properly...
190 */
191 CBDATA_TYPE(external_acl);
192 CBDATA_TYPE(external_acl_format);
193
194 static void
195 free_external_acl_format(void *data)
196 {
197 external_acl_format *p = static_cast<external_acl_format *>(data);
198 safe_free(p->header);
199 }
200
201 static void
202 free_external_acl(void *data)
203 {
204 external_acl *p = static_cast<external_acl *>(data);
205 safe_free(p->name);
206
207 while (p->format) {
208 external_acl_format *f = p->format;
209 p->format = f->next;
210 cbdataFree(f);
211 }
212
213 wordlistDestroy(&p->cmdline);
214
215 if (p->theHelper) {
216 helperShutdown(p->theHelper);
217 delete p->theHelper;
218 p->theHelper = NULL;
219 }
220
221 while (p->lru_list.tail)
222 external_acl_cache_delete(p, static_cast<external_acl_entry *>(p->lru_list.tail->data));
223 if (p->cache)
224 hashFreeMemory(p->cache);
225 }
226
227 /**
228 * Parse the External ACL format %<{.*} and %>{.*} token(s) to pass a specific
229 * request or reply header to external helper.
230 *
231 \param header - the token being parsed (without the identifying prefix)
232 \param type - format enum identifier for this element, pulled from identifying prefix
233 \param format - structure to contain all the info about this format element.
234 */
235 void
236 parse_header_token(external_acl_format *format, char *header, const _external_acl_format::format_type type)
237 {
238 /* header format */
239 char *member, *end;
240
241 /** Cut away the closing brace */
242 end = strchr(header, '}');
243 if (end && strlen(end) == 1)
244 *end = '\0';
245 else
246 self_destruct();
247
248 member = strchr(header, ':');
249
250 if (member) {
251 /* Split in header and member */
252 *member++ = '\0';
253
254 if (!xisalnum(*member))
255 format->separator = *member++;
256 else
257 format->separator = ',';
258
259 format->member = xstrdup(member);
260
261 if (type == _external_acl_format::EXT_ACL_HEADER_REQUEST)
262 format->type = _external_acl_format::EXT_ACL_HEADER_REQUEST_MEMBER;
263 else
264 format->type = _external_acl_format::EXT_ACL_HEADER_REQUEST_MEMBER;
265 } else {
266 format->type = type;
267 }
268
269 format->header = xstrdup(header);
270 format->header_id = httpHeaderIdByNameDef(header, strlen(header));
271
272 if (format->header_id != -1) {
273 if (member) {
274 if (type == _external_acl_format::EXT_ACL_HEADER_REQUEST)
275 format->type = _external_acl_format::EXT_ACL_HEADER_REQUEST_ID_MEMBER;
276 else
277 format->type = _external_acl_format::EXT_ACL_HEADER_REPLY_ID_MEMBER;
278 } else {
279 if (type == _external_acl_format::EXT_ACL_HEADER_REQUEST)
280 format->type = _external_acl_format::EXT_ACL_HEADER_REQUEST_ID;
281 else
282 format->type = _external_acl_format::EXT_ACL_HEADER_REPLY_ID;
283 }
284 }
285 }
286
287 void
288 parse_externalAclHelper(external_acl ** list)
289 {
290 external_acl *a;
291 char *token;
292 external_acl_format **p;
293
294 CBDATA_INIT_TYPE_FREECB(external_acl, free_external_acl);
295 CBDATA_INIT_TYPE_FREECB(external_acl_format, free_external_acl_format);
296
297 a = cbdataAlloc(external_acl);
298
299 /* set defaults */
300 a->ttl = DEFAULT_EXTERNAL_ACL_TTL;
301 a->negative_ttl = -1;
302 a->cache_size = 256*1024;
303 a->children.n_max = DEFAULT_EXTERNAL_ACL_CHILDREN;
304 a->children.n_startup = a->children.n_max;
305 a->children.n_idle = 1;
306 a->local_addr.SetLocalhost();
307 a->quote = external_acl::QUOTE_METHOD_URL;
308
309 token = strtok(NULL, w_space);
310
311 if (!token)
312 self_destruct();
313
314 a->name = xstrdup(token);
315
316 token = strtok(NULL, w_space);
317
318 /* Parse options */
319 while (token) {
320 if (strncmp(token, "ttl=", 4) == 0) {
321 a->ttl = atoi(token + 4);
322 } else if (strncmp(token, "negative_ttl=", 13) == 0) {
323 a->negative_ttl = atoi(token + 13);
324 } else if (strncmp(token, "children=", 9) == 0) {
325 a->children.n_max = atoi(token + 9);
326 debugs(0, 0, "WARNING: external_acl_type option children=N has been deprecated in favor of children-max=N and children-startup=N");
327 } else if (strncmp(token, "children-max=", 13) == 0) {
328 a->children.n_max = atoi(token + 13);
329 } else if (strncmp(token, "children-startup=", 17) == 0) {
330 a->children.n_startup = atoi(token + 17);
331 } else if (strncmp(token, "children-idle=", 14) == 0) {
332 a->children.n_idle = atoi(token + 14);
333 } else if (strncmp(token, "concurrency=", 12) == 0) {
334 a->children.concurrency = atoi(token + 12);
335 } else if (strncmp(token, "cache=", 6) == 0) {
336 a->cache_size = atoi(token + 6);
337 } else if (strncmp(token, "grace=", 6) == 0) {
338 a->grace = atoi(token + 6);
339 } else if (strcmp(token, "protocol=2.5") == 0) {
340 a->quote = external_acl::QUOTE_METHOD_SHELL;
341 } else if (strcmp(token, "protocol=3.0") == 0) {
342 a->quote = external_acl::QUOTE_METHOD_URL;
343 } else if (strcmp(token, "quote=url") == 0) {
344 a->quote = external_acl::QUOTE_METHOD_URL;
345 } else if (strcmp(token, "quote=shell") == 0) {
346 a->quote = external_acl::QUOTE_METHOD_SHELL;
347
348 /* INET6: allow admin to configure some helpers explicitly to
349 bind to IPv4/v6 localhost port. */
350 } else if (strcmp(token, "ipv4") == 0) {
351 if ( !a->local_addr.SetIPv4() ) {
352 debugs(3, 0, "WARNING: Error converting " << a->local_addr << " to IPv4 in " << a->name );
353 }
354 } else if (strcmp(token, "ipv6") == 0) {
355 #if !USE_IPV6
356 debugs(3, 0, "WARNING: --enable-ipv6 required for external ACL helpers to use IPv6: " << a->name );
357 #else
358 (void)0;
359 #endif
360 } else {
361 break;
362 }
363
364 token = strtok(NULL, w_space);
365 }
366
367 /* check that child startup value is sane. */
368 if (a->children.n_startup > a->children.n_max)
369 a->children.n_startup = a->children.n_max;
370
371 /* check that child idle value is sane. */
372 if (a->children.n_idle > a->children.n_max)
373 a->children.n_idle = a->children.n_max;
374 if (a->children.n_idle < 1)
375 a->children.n_idle = 1;
376
377 if (a->negative_ttl == -1)
378 a->negative_ttl = a->ttl;
379
380 /* Parse format */
381 p = &a->format;
382
383 while (token) {
384 external_acl_format *format;
385
386 /* stop on first non-format token found */
387
388 if (*token != '%')
389 break;
390
391 format = cbdataAlloc(external_acl_format);
392
393 if (strncmp(token, "%{", 2) == 0) {
394 // deprecated. but assume the old configs all referred to request headers.
395 debugs(82, DBG_IMPORTANT, "WARNING: external_acl_type format %{...} is being replaced by %>{...} for : " << token);
396 parse_header_token(format, (token+2), _external_acl_format::EXT_ACL_HEADER_REQUEST);
397 } else if (strncmp(token, "%>{", 3) == 0) {
398 parse_header_token(format, (token+3), _external_acl_format::EXT_ACL_HEADER_REQUEST);
399 } else if (strncmp(token, "%<{", 3) == 0) {
400 parse_header_token(format, (token+3), _external_acl_format::EXT_ACL_HEADER_REPLY);
401 } else if (strcmp(token, "%LOGIN") == 0) {
402 format->type = _external_acl_format::EXT_ACL_LOGIN;
403 a->require_auth = true;
404 }
405
406 #if USE_IDENT
407 else if (strcmp(token, "%IDENT") == 0)
408 format->type = _external_acl_format::EXT_ACL_IDENT;
409
410 #endif
411
412 else if (strcmp(token, "%SRC") == 0)
413 format->type = _external_acl_format::EXT_ACL_SRC;
414 else if (strcmp(token, "%SRCPORT") == 0)
415 format->type = _external_acl_format::EXT_ACL_SRCPORT;
416 #if USE_SQUID_EUI
417 else if (strcmp(token, "%SRCEUI48") == 0)
418 format->type = _external_acl_format::EXT_ACL_SRCEUI48;
419 else if (strcmp(token, "%SRCEUI64") == 0)
420 format->type = _external_acl_format::EXT_ACL_SRCEUI64;
421 #endif
422 else if (strcmp(token, "%MYADDR") == 0)
423 format->type = _external_acl_format::EXT_ACL_MYADDR;
424 else if (strcmp(token, "%MYPORT") == 0)
425 format->type = _external_acl_format::EXT_ACL_MYPORT;
426 else if (strcmp(token, "%URI") == 0)
427 format->type = _external_acl_format::EXT_ACL_URI;
428 else if (strcmp(token, "%DST") == 0)
429 format->type = _external_acl_format::EXT_ACL_DST;
430 else if (strcmp(token, "%PROTO") == 0)
431 format->type = _external_acl_format::EXT_ACL_PROTO;
432 else if (strcmp(token, "%PORT") == 0)
433 format->type = _external_acl_format::EXT_ACL_PORT;
434 else if (strcmp(token, "%PATH") == 0)
435 format->type = _external_acl_format::EXT_ACL_PATH;
436 else if (strcmp(token, "%METHOD") == 0)
437 format->type = _external_acl_format::EXT_ACL_METHOD;
438
439 #if USE_SSL
440 else if (strcmp(token, "%USER_CERT") == 0)
441 format->type = _external_acl_format::EXT_ACL_USER_CERT_RAW;
442 else if (strcmp(token, "%USER_CERTCHAIN") == 0)
443 format->type = _external_acl_format::EXT_ACL_USER_CERTCHAIN_RAW;
444 else if (strncmp(token, "%USER_CERT_", 11) == 0) {
445 format->type = _external_acl_format::EXT_ACL_USER_CERT;
446 format->header = xstrdup(token + 11);
447 } else if (strncmp(token, "%CA_CERT_", 11) == 0) {
448 format->type = _external_acl_format::EXT_ACL_USER_CERT;
449 format->header = xstrdup(token + 11);
450 }
451 #endif
452 else if (strcmp(token, "%EXT_USER") == 0)
453 format->type = _external_acl_format::EXT_ACL_EXT_USER;
454 else {
455 debugs(0,0, "ERROR: Unknown Format token " << token);
456 self_destruct();
457 }
458
459 *p = format;
460 p = &format->next;
461 token = strtok(NULL, w_space);
462 }
463
464 /* There must be at least one format token */
465 if (!a->format)
466 self_destruct();
467
468 /* helper */
469 if (!token)
470 self_destruct();
471
472 wordlistAdd(&a->cmdline, token);
473
474 /* arguments */
475 parse_wordlist(&a->cmdline);
476
477 while (*list)
478 list = &(*list)->next;
479
480 *list = a;
481 }
482
483 void
484 dump_externalAclHelper(StoreEntry * sentry, const char *name, const external_acl * list)
485 {
486 const external_acl *node;
487 const external_acl_format *format;
488 const wordlist *word;
489
490 for (node = list; node; node = node->next) {
491 storeAppendPrintf(sentry, "%s %s", name, node->name);
492
493 if (!node->local_addr.IsIPv6())
494 storeAppendPrintf(sentry, " ipv4");
495 else
496 storeAppendPrintf(sentry, " ipv6");
497
498 if (node->ttl != DEFAULT_EXTERNAL_ACL_TTL)
499 storeAppendPrintf(sentry, " ttl=%d", node->ttl);
500
501 if (node->negative_ttl != node->ttl)
502 storeAppendPrintf(sentry, " negative_ttl=%d", node->negative_ttl);
503
504 if (node->grace)
505 storeAppendPrintf(sentry, " grace=%d", node->grace);
506
507 if (node->children.n_max != DEFAULT_EXTERNAL_ACL_CHILDREN)
508 storeAppendPrintf(sentry, " children-max=%d", node->children.n_max);
509
510 if (node->children.n_startup != 1)
511 storeAppendPrintf(sentry, " children-startup=%d", node->children.n_startup);
512
513 if (node->children.n_idle != (node->children.n_max + node->children.n_startup) )
514 storeAppendPrintf(sentry, " children-idle=%d", node->children.n_idle);
515
516 if (node->children.concurrency)
517 storeAppendPrintf(sentry, " concurrency=%d", node->children.concurrency);
518
519 if (node->cache)
520 storeAppendPrintf(sentry, " cache=%d", node->cache_size);
521
522 for (format = node->format; format; format = format->next) {
523 switch (format->type) {
524
525 case _external_acl_format::EXT_ACL_HEADER_REQUEST:
526 case _external_acl_format::EXT_ACL_HEADER_REQUEST_ID:
527 storeAppendPrintf(sentry, " %%>{%s}", format->header);
528 break;
529
530 case _external_acl_format::EXT_ACL_HEADER_REQUEST_MEMBER:
531 case _external_acl_format::EXT_ACL_HEADER_REQUEST_ID_MEMBER:
532 storeAppendPrintf(sentry, " %%>{%s:%s}", format->header, format->member);
533 break;
534
535 case _external_acl_format::EXT_ACL_HEADER_REPLY:
536 case _external_acl_format::EXT_ACL_HEADER_REPLY_ID:
537 storeAppendPrintf(sentry, " %%<{%s}", format->header);
538 break;
539
540 case _external_acl_format::EXT_ACL_HEADER_REPLY_MEMBER:
541 case _external_acl_format::EXT_ACL_HEADER_REPLY_ID_MEMBER:
542 storeAppendPrintf(sentry, " %%<{%s:%s}", format->header, format->member);
543 break;
544 #define DUMP_EXT_ACL_TYPE(a) \
545 case _external_acl_format::EXT_ACL_##a: \
546 storeAppendPrintf(sentry, " %%%s", #a); \
547 break
548
549 DUMP_EXT_ACL_TYPE(LOGIN);
550 #if USE_IDENT
551
552 DUMP_EXT_ACL_TYPE(IDENT);
553 #endif
554
555 DUMP_EXT_ACL_TYPE(SRC);
556 DUMP_EXT_ACL_TYPE(SRCPORT);
557 #if USE_SQUID_EUI
558 DUMP_EXT_ACL_TYPE(SRCEUI48);
559 DUMP_EXT_ACL_TYPE(SRCEUI64);
560 #endif
561
562 DUMP_EXT_ACL_TYPE(MYADDR);
563 DUMP_EXT_ACL_TYPE(MYPORT);
564 DUMP_EXT_ACL_TYPE(URI);
565 DUMP_EXT_ACL_TYPE(DST);
566 DUMP_EXT_ACL_TYPE(PROTO);
567 DUMP_EXT_ACL_TYPE(PORT);
568 DUMP_EXT_ACL_TYPE(PATH);
569 DUMP_EXT_ACL_TYPE(METHOD);
570 #if USE_SSL
571
572 case _external_acl_format::EXT_ACL_USER_CERT_RAW:
573 storeAppendPrintf(sentry, " %%USER_CERT");
574 break;
575
576 case _external_acl_format::EXT_ACL_USER_CERTCHAIN_RAW:
577 storeAppendPrintf(sentry, " %%USER_CERTCHAIN");
578 break;
579
580 case _external_acl_format::EXT_ACL_USER_CERT:
581 storeAppendPrintf(sentry, " %%USER_CERT_%s", format->header);
582 break;
583
584 case _external_acl_format::EXT_ACL_CA_CERT:
585 storeAppendPrintf(sentry, " %%USER_CERT_%s", format->header);
586 break;
587 #endif
588
589 DUMP_EXT_ACL_TYPE(EXT_USER);
590
591 default:
592 fatal("unknown external_acl format error");
593 break;
594 }
595 }
596
597 for (word = node->cmdline; word; word = word->next)
598 storeAppendPrintf(sentry, " %s", word->key);
599
600 storeAppendPrintf(sentry, "\n");
601 }
602 }
603
604 void
605 free_externalAclHelper(external_acl ** list)
606 {
607 while (*list) {
608 external_acl *node = *list;
609 *list = node->next;
610 node->next = NULL;
611 cbdataFree(node);
612 }
613 }
614
615 static external_acl *
616 find_externalAclHelper(const char *name)
617 {
618 external_acl *node;
619
620 for (node = Config.externalAclHelperList; node; node = node->next) {
621 if (strcmp(node->name, name) == 0)
622 return node;
623 }
624
625 return NULL;
626 }
627
628 void
629
630 external_acl::add
631 (ExternalACLEntry *anEntry)
632 {
633 trimCache();
634 assert (anEntry->def == NULL);
635 anEntry->def = this;
636 hash_join(cache, anEntry);
637 dlinkAdd(anEntry, &anEntry->lru, &lru_list);
638 cache_entries++;
639 }
640
641 void
642 external_acl::trimCache()
643 {
644 if (cache_size && cache_entries >= cache_size)
645 external_acl_cache_delete(this, static_cast<external_acl_entry *>(lru_list.tail->data));
646 }
647
648
649 /******************************************************************
650 * external acl type
651 */
652
653 struct _external_acl_data {
654 external_acl *def;
655 wordlist *arguments;
656 };
657
658 CBDATA_TYPE(external_acl_data);
659 static void
660 free_external_acl_data(void *data)
661 {
662 external_acl_data *p = static_cast<external_acl_data *>(data);
663 wordlistDestroy(&p->arguments);
664 cbdataReferenceDone(p->def);
665 }
666
667 void
668 ACLExternal::parse()
669 {
670 char *token;
671
672 if (data)
673 self_destruct();
674
675 CBDATA_INIT_TYPE_FREECB(external_acl_data, free_external_acl_data);
676
677 data = cbdataAlloc(external_acl_data);
678
679 token = strtok(NULL, w_space);
680
681 if (!token)
682 self_destruct();
683
684 data->def = cbdataReference(find_externalAclHelper(token));
685
686 if (!data->def)
687 self_destruct();
688
689 while ((token = strtokFile())) {
690 wordlistAdd(&data->arguments, token);
691 }
692 }
693
694 bool
695 ACLExternal::valid () const
696 {
697 if (data->def->require_auth) {
698 if (authenticateSchemeCount() == 0) {
699 debugs(28, 0, "Can't use proxy auth because no authentication schemes were compiled.");
700 return false;
701 }
702
703 if (authenticateActiveSchemeCount() == 0) {
704 debugs(28, 0, "Can't use proxy auth because no authentication schemes are fully configured.");
705 return false;
706 }
707 }
708
709 return true;
710 }
711
712 bool
713 ACLExternal::empty () const
714 {
715 return false;
716 }
717
718 ACLExternal::~ACLExternal()
719 {
720 cbdataFree(data);
721 safe_free (class_);
722 }
723
724 static int
725 aclMatchExternal(external_acl_data *acl, ACLFilledChecklist *ch)
726 {
727 int result;
728 external_acl_entry *entry;
729 const char *key = "";
730 debugs(82, 9, "aclMatchExternal: acl=\"" << acl->def->name << "\"");
731 entry = ch->extacl_entry;
732
733 if (entry) {
734 if (cbdataReferenceValid(entry) && entry->def == acl->def &&
735 strcmp((char *)entry->key, key) == 0) {
736 /* Ours, use it.. */
737 } else {
738 /* Not valid, or not ours.. get rid of it */
739 cbdataReferenceDone(ch->extacl_entry);
740 entry = NULL;
741 }
742 }
743
744 external_acl_message = "MISSING REQUIRED INFORMATION";
745
746 if (!entry) {
747 if (acl->def->require_auth) {
748 int ti;
749 /* Make sure the user is authenticated */
750 debugs(82, 3, "aclMatchExternal: " << acl->def->name << " check user authenticated.");
751
752 if ((ti = AuthenticateAcl(ch)) != 1) {
753 debugs(82, 2, "aclMatchExternal: " << acl->def->name << " user not authenticated (" << ti << ")");
754 return ti;
755 }
756 debugs(82, 3, "aclMatchExternal: " << acl->def->name << " user is authenticated.");
757 }
758
759 key = makeExternalAclKey(ch, acl);
760
761 if (!key) {
762 /* Not sufficient data to process */
763 return -1;
764 }
765
766 entry = static_cast<external_acl_entry *>(hash_lookup(acl->def->cache, key));
767
768 if (!entry || external_acl_grace_expired(acl->def, entry)) {
769 debugs(82, 2, "aclMatchExternal: " << acl->def->name << "(\"" << key << "\") = lookup needed");
770 debugs(82, 2, "aclMatchExternal: \"" << key << "\": entry=@" <<
771 entry << ", age=" << (entry ? (long int) squid_curtime - entry->date : 0));
772
773 if (acl->def->theHelper->stats.queue_size <= (int)acl->def->theHelper->childs.n_active) {
774 debugs(82, 2, "aclMatchExternal: \"" << key << "\": queueing a call.");
775 ch->changeState (ExternalACLLookup::Instance());
776
777 if (entry == NULL) {
778 debugs(82, 2, "aclMatchExternal: \"" << key << "\": return -1.");
779 return -1;
780 }
781 } else {
782 if (!entry) {
783 debugs(82, 1, "aclMatchExternal: '" << acl->def->name <<
784 "' queue overload. Request rejected '" << key << "'.");
785 external_acl_message = "SYSTEM TOO BUSY, TRY AGAIN LATER";
786 return -1;
787 } else {
788 debugs(82, 1, "aclMatchExternal: '" << acl->def->name <<
789 "' queue overload. Using stale result. '" << key << "'.");
790 /* Fall thru to processing below */
791 }
792 }
793 }
794 }
795
796 external_acl_cache_touch(acl->def, entry);
797 result = entry->result;
798 external_acl_message = entry->message.termedBuf();
799
800 debugs(82, 2, "aclMatchExternal: " << acl->def->name << " = " << result);
801
802 if (ch->request) {
803 if (entry->user.size())
804 ch->request->extacl_user = entry->user;
805
806 if (entry->password.size())
807 ch->request->extacl_passwd = entry->password;
808
809 if (!ch->request->tag.size())
810 ch->request->tag = entry->tag;
811
812 if (entry->log.size())
813 ch->request->extacl_log = entry->log;
814
815 if (entry->message.size())
816 ch->request->extacl_message = entry->message;
817 }
818
819 return result;
820 }
821
822 int
823 ACLExternal::match(ACLChecklist *checklist)
824 {
825 return aclMatchExternal (data, Filled(checklist));
826 }
827
828 wordlist *
829 ACLExternal::dump() const
830 {
831 external_acl_data const *acl = data;
832 wordlist *result = NULL;
833 wordlist *arg;
834 MemBuf mb;
835 mb.init();
836 mb.Printf("%s", acl->def->name);
837
838 for (arg = acl->arguments; arg; arg = arg->next) {
839 mb.Printf(" %s", arg->key);
840 }
841
842 wordlistAdd(&result, mb.buf);
843 mb.clean();
844 return result;
845 }
846
847 /******************************************************************
848 * external_acl cache
849 */
850
851 static void
852 external_acl_cache_touch(external_acl * def, external_acl_entry * entry)
853 {
854 dlinkDelete(&entry->lru, &def->lru_list);
855 dlinkAdd(entry, &entry->lru, &def->lru_list);
856 }
857
858 static char *
859 makeExternalAclKey(ACLFilledChecklist * ch, external_acl_data * acl_data)
860 {
861 static MemBuf mb;
862 char buf[256];
863 int first = 1;
864 wordlist *arg;
865 external_acl_format *format;
866 HttpRequest *request = ch->request;
867 HttpReply *reply = ch->reply;
868 mb.reset();
869
870 for (format = acl_data->def->format; format; format = format->next) {
871 const char *str = NULL;
872 String sb;
873
874 switch (format->type) {
875
876 case _external_acl_format::EXT_ACL_LOGIN:
877 assert (ch->auth_user_request != NULL);
878 str = ch->auth_user_request->username();
879 break;
880 #if USE_IDENT
881
882 case _external_acl_format::EXT_ACL_IDENT:
883 str = ch->rfc931;
884
885 if (!str || !*str) {
886 ch->changeState(IdentLookup::Instance());
887 return NULL;
888 }
889
890 break;
891 #endif
892
893 case _external_acl_format::EXT_ACL_SRC:
894 str = ch->src_addr.NtoA(buf,sizeof(buf));
895 break;
896
897 case _external_acl_format::EXT_ACL_SRCPORT:
898 snprintf(buf, sizeof(buf), "%d", request->client_addr.GetPort());
899 str = buf;
900 break;
901
902 #if USE_SQUID_EUI
903 case _external_acl_format::EXT_ACL_SRCEUI48:
904 if (request->client_eui48.encode(buf, sizeof(buf)))
905 str = buf;
906 break;
907
908 case _external_acl_format::EXT_ACL_SRCEUI64:
909 if (request->client_eui64.encode(buf, sizeof(buf)))
910 str = buf;
911 break;
912 #endif
913
914 case _external_acl_format::EXT_ACL_MYADDR:
915 str = request->my_addr.NtoA(buf, sizeof(buf));
916 break;
917
918 case _external_acl_format::EXT_ACL_MYPORT:
919 snprintf(buf, sizeof(buf), "%d", request->my_addr.GetPort());
920 str = buf;
921 break;
922
923 case _external_acl_format::EXT_ACL_URI:
924 str = urlCanonical(request);
925 break;
926
927 case _external_acl_format::EXT_ACL_DST:
928 str = request->GetHost();
929 break;
930
931 case _external_acl_format::EXT_ACL_PROTO:
932 str = ProtocolStr[request->protocol];
933 break;
934
935 case _external_acl_format::EXT_ACL_PORT:
936 snprintf(buf, sizeof(buf), "%d", request->port);
937 str = buf;
938 break;
939
940 case _external_acl_format::EXT_ACL_PATH:
941 str = request->urlpath.termedBuf();
942 break;
943
944 case _external_acl_format::EXT_ACL_METHOD:
945 str = RequestMethodStr(request->method);
946 break;
947
948 case _external_acl_format::EXT_ACL_HEADER_REQUEST:
949 sb = request->header.getByName(format->header);
950 str = sb.termedBuf();
951 break;
952
953 case _external_acl_format::EXT_ACL_HEADER_REQUEST_ID:
954 sb = request->header.getStrOrList(format->header_id);
955 str = sb.termedBuf();
956 break;
957
958 case _external_acl_format::EXT_ACL_HEADER_REQUEST_MEMBER:
959 sb = request->header.getByNameListMember(format->header, format->member, format->separator);
960 str = sb.termedBuf();
961 break;
962
963 case _external_acl_format::EXT_ACL_HEADER_REQUEST_ID_MEMBER:
964 sb = request->header.getListMember(format->header_id, format->member, format->separator);
965 str = sb.termedBuf();
966 break;
967
968 case _external_acl_format::EXT_ACL_HEADER_REPLY:
969 if (reply) {
970 sb = reply->header.getByName(format->header);
971 str = sb.termedBuf();
972 }
973 break;
974
975 case _external_acl_format::EXT_ACL_HEADER_REPLY_ID:
976 if (reply) {
977 sb = reply->header.getStrOrList(format->header_id);
978 str = sb.termedBuf();
979 }
980 break;
981
982 case _external_acl_format::EXT_ACL_HEADER_REPLY_MEMBER:
983 if (reply) {
984 sb = reply->header.getByNameListMember(format->header, format->member, format->separator);
985 str = sb.termedBuf();
986 }
987 break;
988
989 case _external_acl_format::EXT_ACL_HEADER_REPLY_ID_MEMBER:
990 if (reply) {
991 sb = reply->header.getListMember(format->header_id, format->member, format->separator);
992 str = sb.termedBuf();
993 }
994 break;
995 #if USE_SSL
996
997 case _external_acl_format::EXT_ACL_USER_CERT_RAW:
998
999 if (ch->conn() != NULL) {
1000 SSL *ssl = fd_table[ch->conn()->fd].ssl;
1001
1002 if (ssl)
1003 str = sslGetUserCertificatePEM(ssl);
1004 }
1005
1006 break;
1007
1008 case _external_acl_format::EXT_ACL_USER_CERTCHAIN_RAW:
1009
1010 if (ch->conn() != NULL) {
1011 SSL *ssl = fd_table[ch->conn()->fd].ssl;
1012
1013 if (ssl)
1014 str = sslGetUserCertificateChainPEM(ssl);
1015 }
1016
1017 break;
1018
1019 case _external_acl_format::EXT_ACL_USER_CERT:
1020
1021 if (ch->conn() != NULL) {
1022 SSL *ssl = fd_table[ch->conn()->fd].ssl;
1023
1024 if (ssl)
1025 str = sslGetUserAttribute(ssl, format->header);
1026 }
1027
1028 break;
1029
1030 case _external_acl_format::EXT_ACL_CA_CERT:
1031
1032 if (ch->conn() != NULL) {
1033 SSL *ssl = fd_table[ch->conn()->fd].ssl;
1034
1035 if (ssl)
1036 str = sslGetCAAttribute(ssl, format->header);
1037 }
1038
1039 break;
1040 #endif
1041
1042 case _external_acl_format::EXT_ACL_EXT_USER:
1043 str = request->extacl_user.termedBuf();
1044 break;
1045
1046 case _external_acl_format::EXT_ACL_UNKNOWN:
1047
1048 case _external_acl_format::EXT_ACL_END:
1049 fatal("unknown external_acl format error");
1050 break;
1051 }
1052
1053 if (str)
1054 if (!*str)
1055 str = NULL;
1056
1057 if (!str)
1058 str = "-";
1059
1060 if (!first)
1061 mb.append(" ", 1);
1062
1063 if (acl_data->def->quote == external_acl::QUOTE_METHOD_URL) {
1064 const char *quoted = rfc1738_escape(str);
1065 mb.append(quoted, strlen(quoted));
1066 } else {
1067 strwordquote(&mb, str);
1068 }
1069
1070 sb.clean();
1071
1072 first = 0;
1073 }
1074
1075 for (arg = acl_data->arguments; arg; arg = arg->next) {
1076 if (!first)
1077 mb.append(" ", 1);
1078
1079 if (acl_data->def->quote == external_acl::QUOTE_METHOD_URL) {
1080 const char *quoted = rfc1738_escape(arg->key);
1081 mb.append(quoted, strlen(quoted));
1082 } else {
1083 strwordquote(&mb, arg->key);
1084 }
1085
1086 first = 0;
1087 }
1088
1089 return mb.buf;
1090 }
1091
1092 static int
1093 external_acl_entry_expired(external_acl * def, external_acl_entry * entry)
1094 {
1095 if (entry->date + (entry->result == 1 ? def->ttl : def->negative_ttl) < squid_curtime)
1096 return 1;
1097 else
1098 return 0;
1099 }
1100
1101 static int
1102 external_acl_grace_expired(external_acl * def, external_acl_entry * entry)
1103 {
1104 int ttl;
1105 ttl = entry->result == 1 ? def->ttl : def->negative_ttl;
1106 ttl = (ttl * (100 - def->grace)) / 100;
1107
1108 if (entry->date + ttl < squid_curtime)
1109 return 1;
1110 else
1111 return 0;
1112 }
1113
1114 static external_acl_entry *
1115 external_acl_cache_add(external_acl * def, const char *key, ExternalACLEntryData const & data)
1116 {
1117 ExternalACLEntry *entry = static_cast<ExternalACLEntry *>(hash_lookup(def->cache, key));
1118 debugs(82, 2, "external_acl_cache_add: Adding '" << key << "' = " << data.result);
1119
1120 if (entry) {
1121 debugs(82, 3, "ExternalACLEntry::update: updating existing entry");
1122 entry->update (data);
1123 external_acl_cache_touch(def, entry);
1124
1125 return entry;
1126 }
1127
1128 entry = new ExternalACLEntry;
1129 entry->key = xstrdup(key);
1130 entry->update (data);
1131
1132 def->add
1133 (entry);
1134
1135 return entry;
1136 }
1137
1138 static void
1139 external_acl_cache_delete(external_acl * def, external_acl_entry * entry)
1140 {
1141 assert (entry->def == def);
1142 hash_remove_link(def->cache, entry);
1143 dlinkDelete(&entry->lru, &def->lru_list);
1144 def->cache_entries -= 1;
1145 delete entry;
1146 }
1147
1148 /******************************************************************
1149 * external_acl helpers
1150 */
1151
1152 typedef struct _externalAclState externalAclState;
1153
1154 struct _externalAclState {
1155 EAH *callback;
1156 void *callback_data;
1157 char *key;
1158 external_acl *def;
1159 dlink_node list;
1160 externalAclState *queue;
1161 };
1162
1163 CBDATA_TYPE(externalAclState);
1164 static void
1165 free_externalAclState(void *data)
1166 {
1167 externalAclState *state = static_cast<externalAclState *>(data);
1168 safe_free(state->key);
1169 cbdataReferenceDone(state->callback_data);
1170 cbdataReferenceDone(state->def);
1171 }
1172
1173 /*
1174 * The helper program receives queries on stdin, one
1175 * per line, and must return the result on on stdout
1176 *
1177 * General result syntax:
1178 *
1179 * OK/ERR keyword=value ...
1180 *
1181 * Keywords:
1182 *
1183 * user= The users name (login)
1184 * message= Message describing the reason
1185 * tag= A string tag to be applied to the request that triggered the acl match.
1186 * applies to both OK and ERR responses.
1187 * Won't override existing request tags.
1188 * log= A string to be used in access logging
1189 *
1190 * Other keywords may be added to the protocol later
1191 *
1192 * value needs to be enclosed in quotes if it may contain whitespace, or
1193 * the whitespace escaped using \ (\ escaping obviously also applies to
1194 * any " characters)
1195 */
1196
1197 static void
1198 externalAclHandleReply(void *data, char *reply)
1199 {
1200 externalAclState *state = static_cast<externalAclState *>(data);
1201 externalAclState *next;
1202 char *status;
1203 char *token;
1204 char *value;
1205 char *t;
1206 ExternalACLEntryData entryData;
1207 entryData.result = 0;
1208 external_acl_entry *entry = NULL;
1209
1210 debugs(82, 2, "externalAclHandleReply: reply=\"" << reply << "\"");
1211
1212 if (reply) {
1213 status = strwordtok(reply, &t);
1214
1215 if (status && strcmp(status, "OK") == 0)
1216 entryData.result = 1;
1217
1218 while ((token = strwordtok(NULL, &t))) {
1219 value = strchr(token, '=');
1220
1221 if (value) {
1222 *value++ = '\0'; /* terminate the token, and move up to the value */
1223
1224 if (state->def->quote == external_acl::QUOTE_METHOD_URL)
1225 rfc1738_unescape(value);
1226
1227 if (strcmp(token, "user") == 0)
1228 entryData.user = value;
1229 else if (strcmp(token, "message") == 0)
1230 entryData.message = value;
1231 else if (strcmp(token, "error") == 0)
1232 entryData.message = value;
1233 else if (strcmp(token, "tag") == 0)
1234 entryData.tag = value;
1235 else if (strcmp(token, "log") == 0)
1236 entryData.log = value;
1237 else if (strcmp(token, "password") == 0)
1238 entryData.password = value;
1239 else if (strcmp(token, "passwd") == 0)
1240 entryData.password = value;
1241 else if (strcmp(token, "login") == 0)
1242 entryData.user = value;
1243 }
1244 }
1245 }
1246
1247 dlinkDelete(&state->list, &state->def->queue);
1248
1249 if (cbdataReferenceValid(state->def)) {
1250 if (reply)
1251 entry = external_acl_cache_add(state->def, state->key, entryData);
1252 else {
1253 external_acl_entry *oldentry = (external_acl_entry *)hash_lookup(state->def->cache, state->key);
1254
1255 if (oldentry)
1256 external_acl_cache_delete(state->def, oldentry);
1257 }
1258 }
1259
1260 do {
1261 void *cbdata;
1262 cbdataReferenceDone(state->def);
1263
1264 if (state->callback && cbdataReferenceValidDone(state->callback_data, &cbdata))
1265 state->callback(cbdata, entry);
1266
1267 next = state->queue;
1268
1269 cbdataFree(state);
1270
1271 state = next;
1272 } while (state);
1273 }
1274
1275 void
1276 ACLExternal::ExternalAclLookup(ACLChecklist *checklist, ACLExternal * me, EAH * callback, void *callback_data)
1277 {
1278 MemBuf buf;
1279 external_acl_data *acl = me->data;
1280 external_acl *def = acl->def;
1281 externalAclState *state;
1282 dlink_node *node;
1283 externalAclState *oldstate = NULL;
1284 bool graceful = 0;
1285
1286 ACLFilledChecklist *ch = Filled(checklist);
1287 if (acl->def->require_auth) {
1288 int ti;
1289 /* Make sure the user is authenticated */
1290 debugs(82, 3, "aclMatchExternal: " << acl->def->name << " check user authenticated.");
1291
1292 if ((ti = AuthenticateAcl(ch)) != 1) {
1293 debugs(82, 1, "externalAclLookup: " << acl->def->name <<
1294 " user authentication failure (" << ti << ", ch=" << ch << ")");
1295 callback(callback_data, NULL);
1296 return;
1297 }
1298 debugs(82, 3, "aclMatchExternal: " << acl->def->name << " user is authenticated.");
1299 }
1300
1301 const char *key = makeExternalAclKey(ch, acl);
1302
1303 if (!key) {
1304 debugs(82, 1, "externalAclLookup: lookup in '" << def->name <<
1305 "', prerequisit failure (ch=" << ch << ")");
1306 callback(callback_data, NULL);
1307 return;
1308 }
1309
1310 debugs(82, 2, "externalAclLookup: lookup in '" << def->name << "' for '" << key << "'");
1311
1312 external_acl_entry *entry = static_cast<external_acl_entry *>(hash_lookup(def->cache, key));
1313
1314 if (entry && external_acl_entry_expired(def, entry))
1315 entry = NULL;
1316
1317 /* Check for a pending lookup to hook into */
1318 for (node = def->queue.head; node; node = node->next) {
1319 externalAclState *oldstatetmp = static_cast<externalAclState *>(node->data);
1320
1321 if (strcmp(key, oldstatetmp->key) == 0) {
1322 oldstate = oldstatetmp;
1323 break;
1324 }
1325 }
1326
1327 if (entry && external_acl_grace_expired(def, entry)) {
1328 if (oldstate) {
1329 debugs(82, 4, "externalAclLookup: in grace period, but already pending lookup ('" << key << "', ch=" << ch << ")");
1330 callback(callback_data, entry);
1331 return;
1332 } else {
1333 graceful = 1; // grace expired, (neg)ttl did not, and we must start a new lookup.
1334 }
1335 }
1336
1337 // The entry is in the cache, grace_ttl did not expired.
1338 if (!graceful && entry && !external_acl_grace_expired(def, entry)) {
1339 /* Should not really happen, but why not.. */
1340 callback(callback_data, entry);
1341 debugs(82, 4, "externalAclLookup: no lookup pending for '" << key << "', and grace not expired");
1342 debugs(82, 4, "externalAclLookup: (what tha' hell?)");
1343 return;
1344 }
1345
1346 /* No pending lookup found. Sumbit to helper */
1347 state = cbdataAlloc(externalAclState);
1348
1349 state->def = cbdataReference(def);
1350
1351 state->key = xstrdup(key);
1352
1353 if (!graceful) {
1354 state->callback = callback;
1355 state->callback_data = cbdataReference(callback_data);
1356 }
1357
1358 if (oldstate) {
1359 /* Hook into pending lookup */
1360 state->queue = oldstate->queue;
1361 oldstate->queue = state;
1362 } else {
1363 /* Check for queue overload */
1364
1365 if (def->theHelper->stats.queue_size >= (int)def->theHelper->childs.n_running) {
1366 debugs(82, 1, "externalAclLookup: '" << def->name << "' queue overload (ch=" << ch << ")");
1367 cbdataFree(state);
1368 callback(callback_data, entry);
1369 return;
1370 }
1371
1372 /* Send it off to the helper */
1373 buf.init();
1374
1375 buf.Printf("%s\n", key);
1376
1377 debugs(82, 4, "externalAclLookup: looking up for '" << key << "' in '" << def->name << "'.");
1378
1379 helperSubmit(def->theHelper, buf.buf, externalAclHandleReply, state);
1380
1381 dlinkAdd(state, &state->list, &def->queue);
1382
1383 buf.clean();
1384 }
1385
1386 if (graceful) {
1387 /* No need to wait during grace period */
1388 debugs(82, 4, "externalAclLookup: no need to wait for the result of '" <<
1389 key << "' in '" << def->name << "' (ch=" << ch << ").");
1390 debugs(82, 4, "externalAclLookup: using cached entry " << entry);
1391
1392 if (entry != NULL) {
1393 debugs(82, 4, "externalAclLookup: entry = { date=" <<
1394 (long unsigned int) entry->date << ", result=" <<
1395 entry->result << ", user=" << entry->user << " tag=" <<
1396 entry->tag << " log=" << entry->log << " }");
1397
1398 }
1399
1400 callback(callback_data, entry);
1401 return;
1402 }
1403
1404 debugs(82, 4, "externalAclLookup: will wait for the result of '" << key <<
1405 "' in '" << def->name << "' (ch=" << ch << ").");
1406 }
1407
1408 static void
1409 externalAclStats(StoreEntry * sentry)
1410 {
1411 external_acl *p;
1412
1413 for (p = Config.externalAclHelperList; p; p = p->next) {
1414 storeAppendPrintf(sentry, "External ACL Statistics: %s\n", p->name);
1415 storeAppendPrintf(sentry, "Cache size: %d\n", p->cache->count);
1416 helperStats(sentry, p->theHelper);
1417 storeAppendPrintf(sentry, "\n");
1418 }
1419 }
1420
1421 static void
1422 externalAclRegisterWithCacheManager(void)
1423 {
1424 CacheManager::GetInstance()->
1425 registerAction("external_acl",
1426 "External ACL stats",
1427 externalAclStats, 0, 1);
1428 }
1429
1430 void
1431 externalAclInit(void)
1432 {
1433 static int firstTimeInit = 1;
1434 external_acl *p;
1435
1436 for (p = Config.externalAclHelperList; p; p = p->next) {
1437 if (!p->cache)
1438 p->cache = hash_create((HASHCMP *) strcmp, hashPrime(1024), hash4);
1439
1440 if (!p->theHelper)
1441 p->theHelper = new helper(p->name);
1442
1443 p->theHelper->cmdline = p->cmdline;
1444
1445 p->theHelper->childs = p->children;
1446
1447 p->theHelper->ipc_type = IPC_TCP_SOCKET;
1448
1449 p->theHelper->addr = p->local_addr;
1450
1451 helperOpenServers(p->theHelper);
1452 }
1453
1454 if (firstTimeInit) {
1455 firstTimeInit = 0;
1456 CBDATA_INIT_TYPE_FREECB(externalAclState, free_externalAclState);
1457 }
1458
1459 externalAclRegisterWithCacheManager();
1460 }
1461
1462 void
1463 externalAclShutdown(void)
1464 {
1465 external_acl *p;
1466
1467 for (p = Config.externalAclHelperList; p; p = p->next) {
1468 helperShutdown(p->theHelper);
1469 }
1470 }
1471
1472 ExternalACLLookup ExternalACLLookup::instance_;
1473 ExternalACLLookup *
1474 ExternalACLLookup::Instance()
1475 {
1476 return &instance_;
1477 }
1478
1479 void
1480 ExternalACLLookup::checkForAsync(ACLChecklist *checklist)const
1481 {
1482 /* TODO: optimise this - we probably have a pointer to this
1483 * around somewhere */
1484 ACL *acl = ACL::FindByName(AclMatchedName);
1485 assert(acl);
1486 ACLExternal *me = dynamic_cast<ACLExternal *> (acl);
1487 assert (me);
1488 checklist->asyncInProgress(true);
1489 ACLExternal::ExternalAclLookup(checklist, me, LookupDone, checklist);
1490 }
1491
1492 void
1493 ExternalACLLookup::LookupDone(void *data, void *result)
1494 {
1495 ACLFilledChecklist *checklist = Filled(static_cast<ACLChecklist*>(data));
1496 checklist->extacl_entry = cbdataReference((external_acl_entry *)result);
1497 checklist->asyncInProgress(false);
1498 checklist->changeState (ACLChecklist::NullState::Instance());
1499 checklist->check();
1500 }
1501
1502 /* This registers "external" in the registry. To do dynamic definitions
1503 * of external ACL's, rather than a static prototype, have a Prototype instance
1504 * prototype in the class that defines each external acl 'class'.
1505 * Then, then the external acl instance is created, it self registers under
1506 * it's name.
1507 * Be sure that clone is fully functional for that acl class though!
1508 */
1509 ACL::Prototype ACLExternal::RegistryProtoype(&ACLExternal::RegistryEntry_, "external");
1510
1511 ACLExternal ACLExternal::RegistryEntry_("external");
1512
1513 ACL *
1514 ACLExternal::clone() const
1515 {
1516 return new ACLExternal(*this);
1517 }
1518
1519 ACLExternal::ACLExternal (char const *theClass) : data (NULL), class_ (xstrdup (theClass))
1520 {}
1521
1522 ACLExternal::ACLExternal (ACLExternal const & old) : data (NULL), class_ (old.class_ ? xstrdup (old.class_) : NULL)
1523 {
1524 /* we don't have copy constructors for the data yet */
1525 assert (!old.data);
1526 }
1527
1528 char const *
1529 ACLExternal::typeString() const
1530 {
1531 return class_;
1532 }
1533
1534 bool
1535 ACLExternal::isProxyAuth() const
1536 {
1537 return data->def->require_auth;
1538 }