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