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