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