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