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