]> git.ipfire.org Git - thirdparty/squid.git/blame - src/external_acl.cc
peek-and-splice: Fix stare mode
[thirdparty/squid.git] / src / external_acl.cc
CommitLineData
d9572179 1
2/*
d9572179 3 * DEBUG: section 82 External ACL
4 * AUTHOR: Henrik Nordstrom, MARA Systems AB
5 *
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
8 *
9 * The contents of this file is Copyright (C) 2002 by MARA Systems AB,
10 * Sweden, unless otherwise is indicated in the specific function. The
11 * author gives his full permission to include this file into the Squid
12 * software product under the terms of the GNU General Public License as
13 * published by the Free Software Foundation; either version 2 of the
14 * License, or (at your option) any later version.
15 *
16 * Squid is the result of efforts by numerous individuals from
17 * the Internet community; see the CONTRIBUTORS file for full
18 * details. Many organizations have provided support for Squid's
19 * development; see the SPONSORS file for full details. Squid is
20 * Copyrighted (C) 2001 by the Regents of the University of
21 * California; see the COPYRIGHT file for full details. Squid
22 * incorporates software developed and/or copyrighted by other
23 * sources; see the CREDITS file for full details.
24 *
25 * This program is free software; you can redistribute it and/or modify
26 * it under the terms of the GNU General Public License as published by
27 * the Free Software Foundation; either version 2 of the License, or
28 * (at your option) any later version.
26ac0430 29 *
d9572179 30 * This program is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 * GNU General Public License for more details.
26ac0430 34 *
d9572179 35 * You should have received a copy of the GNU General Public License
36 * along with this program; if not, write to the Free Software
37 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
38 *
39 */
40
582c2af2 41#include "squid.h"
c0941a6a 42#include "acl/Acl.h"
582c2af2 43#include "acl/FilledChecklist.h"
8a01b99e 44#include "cache_cf.h"
a46d2c0e 45#include "client_side.h"
5c336a3b 46#include "comm/Connection.h"
2eceb328 47#include "ConfigParser.h"
582c2af2 48#include "ExternalACL.h"
f9b6ff6e 49#include "ExternalACLEntry.h"
582c2af2 50#include "fde.h"
5aca9cf2 51#include "format/ByteCode.h"
aa839030 52#include "helper.h"
a5bac1d2 53#include "HttpHeaderTools.h"
582c2af2
FC
54#include "HttpReply.h"
55#include "HttpRequest.h"
56#include "ip/tools.h"
0eb49b6d 57#include "MemBuf.h"
582c2af2 58#include "mgr/Registration.h"
1fa9b1a7 59#include "rfc1738.h"
4d5904f7 60#include "SquidConfig.h"
f9b6ff6e 61#include "SquidString.h"
582c2af2
FC
62#include "SquidTime.h"
63#include "Store.h"
4e540555 64#include "tools.h"
b1bd952a 65#include "URL.h"
d295d770 66#include "wordlist.h"
cb4f4424 67#if USE_OPENSSL
cedca6e7 68#include "ssl/ServerBump.h"
4db984be
CT
69#include "ssl/support.h"
70#endif
582c2af2
FC
71#if USE_AUTH
72#include "auth/Acl.h"
73#include "auth/Gadgets.h"
74#include "auth/UserRequest.h"
75#endif
76#if USE_IDENT
77#include "ident/AclIdent.h"
78#endif
d9572179 79
80#ifndef DEFAULT_EXTERNAL_ACL_TTL
81#define DEFAULT_EXTERNAL_ACL_TTL 1 * 60 * 60
82#endif
d4f2f353 83#ifndef DEFAULT_EXTERNAL_ACL_CHILDREN
84#define DEFAULT_EXTERNAL_ACL_CHILDREN 5
d9572179 85#endif
86
87typedef struct _external_acl_format external_acl_format;
62e76326 88
c0941a6a 89static char *makeExternalAclKey(ACLFilledChecklist * ch, external_acl_data * acl_data);
d9572179 90static void external_acl_cache_delete(external_acl * def, external_acl_entry * entry);
91static int external_acl_entry_expired(external_acl * def, external_acl_entry * entry);
47b0c1fa 92static int external_acl_grace_expired(external_acl * def, external_acl_entry * entry);
d9572179 93static void external_acl_cache_touch(external_acl * def, external_acl_entry * entry);
1e5562e3 94static external_acl_entry *external_acl_cache_add(external_acl * def, const char *key, ExternalACLEntryData const &data);
d9572179 95
96/******************************************************************
97 * external_acl directive
98 */
62e76326 99
1e5562e3 100class external_acl
62e76326 101{
1e5562e3 102
103public:
d9572179 104 external_acl *next;
1e5562e3 105
6ca34f6f 106 void add(ExternalACLEntry *);
1e5562e3 107
108 void trimCache();
109
d9572179 110 int ttl;
1e5562e3 111
d9572179 112 int negative_ttl;
1e5562e3 113
47b0c1fa 114 int grace;
115
d9572179 116 char *name;
1e5562e3 117
d9572179 118 external_acl_format *format;
1e5562e3 119
d9572179 120 wordlist *cmdline;
1e5562e3 121
48d54e4d 122 HelperChildConfig children;
07eca7e0 123
e6ccf245 124 helper *theHelper;
1e5562e3 125
d9572179 126 hash_table *cache;
1e5562e3 127
d9572179 128 dlink_list lru_list;
1e5562e3 129
d9572179 130 int cache_size;
1e5562e3 131
d9572179 132 int cache_entries;
1e5562e3 133
d9572179 134 dlink_list queue;
1e5562e3 135
2f1431ea 136#if USE_AUTH
3265364b
AJ
137 /**
138 * Configuration flag. May only be altered by the configuration parser.
139 *
140 * Indicates that all uses of this external_acl_type helper require authentication
141 * details to be processed. If none are available its a fail match.
142 */
e870b1f8 143 bool require_auth;
2f1431ea 144#endif
dc1af3cf 145
26ac0430 146 enum {
dc1af3cf 147 QUOTE_METHOD_SHELL = 1,
148 QUOTE_METHOD_URL
2fadd50d 149 } quote;
cc192b50 150
b7ac5457 151 Ip::Address local_addr;
d9572179 152};
153
26ac0430 154struct _external_acl_format {
5aca9cf2 155 Format::ByteCode_t type;
d9572179 156 external_acl_format *next;
157 char *header;
158 char *member;
159 char separator;
160 http_hdr_type header_id;
161};
162
163/* FIXME: These are not really cbdata, but it is an easy way
164 * to get them pooled, refcounted, accounted and freed properly...
165 */
166CBDATA_TYPE(external_acl);
167CBDATA_TYPE(external_acl_format);
168
169static void
170free_external_acl_format(void *data)
171{
e6ccf245 172 external_acl_format *p = static_cast<external_acl_format *>(data);
d9572179 173 safe_free(p->header);
174}
175
176static void
177free_external_acl(void *data)
178{
e6ccf245 179 external_acl *p = static_cast<external_acl *>(data);
d9572179 180 safe_free(p->name);
62e76326 181
d9572179 182 while (p->format) {
62e76326 183 external_acl_format *f = p->format;
184 p->format = f->next;
185 cbdataFree(f);
d9572179 186 }
62e76326 187
d9572179 188 wordlistDestroy(&p->cmdline);
62e76326 189
e6ccf245 190 if (p->theHelper) {
62e76326 191 helperShutdown(p->theHelper);
48d54e4d 192 delete p->theHelper;
62e76326 193 p->theHelper = NULL;
d9572179 194 }
62e76326 195
d9572179 196 while (p->lru_list.tail)
62e76326 197 external_acl_cache_delete(p, static_cast<external_acl_entry *>(p->lru_list.tail->data));
d9572179 198 if (p->cache)
62e76326 199 hashFreeMemory(p->cache);
d9572179 200}
201
7b0ca1e8
AJ
202/**
203 * Parse the External ACL format %<{.*} and %>{.*} token(s) to pass a specific
204 * request or reply header to external helper.
205 *
206 \param header - the token being parsed (without the identifying prefix)
207 \param type - format enum identifier for this element, pulled from identifying prefix
208 \param format - structure to contain all the info about this format element.
209 */
210void
5aca9cf2 211parse_header_token(external_acl_format *format, char *header, const Format::ByteCode_t type)
7b0ca1e8
AJ
212{
213 /* header format */
214 char *member, *end;
215
216 /** Cut away the closing brace */
217 end = strchr(header, '}');
218 if (end && strlen(end) == 1)
219 *end = '\0';
220 else
221 self_destruct();
222
223 member = strchr(header, ':');
224
225 if (member) {
226 /* Split in header and member */
a38ec4b1
FC
227 *member = '\0';
228 ++member;
7b0ca1e8 229
a38ec4b1
FC
230 if (!xisalnum(*member)) {
231 format->separator = *member;
232 ++member;
233 } else {
7b0ca1e8 234 format->separator = ',';
a38ec4b1 235 }
7b0ca1e8
AJ
236
237 format->member = xstrdup(member);
238
5aca9cf2
AJ
239 if (type == Format::LFT_ADAPTED_REQUEST_HEADER)
240 format->type = Format::LFT_ADAPTED_REQUEST_HEADER_ELEM;
7b0ca1e8 241 else
5aca9cf2
AJ
242 format->type = Format::LFT_REPLY_HEADER_ELEM;
243
7b0ca1e8
AJ
244 } else {
245 format->type = type;
246 }
247
248 format->header = xstrdup(header);
249 format->header_id = httpHeaderIdByNameDef(header, strlen(header));
7b0ca1e8
AJ
250}
251
d9572179 252void
253parse_externalAclHelper(external_acl ** list)
254{
255 external_acl *a;
256 char *token;
257 external_acl_format **p;
258
259 CBDATA_INIT_TYPE_FREECB(external_acl, free_external_acl);
260 CBDATA_INIT_TYPE_FREECB(external_acl_format, free_external_acl_format);
261
262 a = cbdataAlloc(external_acl);
263
cc192b50 264 /* set defaults */
d9572179 265 a->ttl = DEFAULT_EXTERNAL_ACL_TTL;
266 a->negative_ttl = -1;
84f795a5 267 a->cache_size = 256*1024;
48d54e4d
AJ
268 a->children.n_max = DEFAULT_EXTERNAL_ACL_CHILDREN;
269 a->children.n_startup = a->children.n_max;
404cfda1 270 a->children.n_idle = 1;
4dd643d5 271 a->local_addr.setLocalhost();
cc192b50 272 a->quote = external_acl::QUOTE_METHOD_URL;
273
2eceb328 274 token = ConfigParser::NextToken();
62e76326 275
d9572179 276 if (!token)
62e76326 277 self_destruct();
278
d9572179 279 a->name = xstrdup(token);
280
2eceb328
CT
281 // Allow supported %macros inside quoted tokens
282 ConfigParser::EnableMacros();
283 token = ConfigParser::NextToken();
62e76326 284
d9572179 285 /* Parse options */
286 while (token) {
62e76326 287 if (strncmp(token, "ttl=", 4) == 0) {
288 a->ttl = atoi(token + 4);
289 } else if (strncmp(token, "negative_ttl=", 13) == 0) {
290 a->negative_ttl = atoi(token + 13);
07eca7e0 291 } else if (strncmp(token, "children=", 9) == 0) {
48d54e4d 292 a->children.n_max = atoi(token + 9);
fa84c01d 293 debugs(0, DBG_CRITICAL, "WARNING: external_acl_type option children=N has been deprecated in favor of children-max=N and children-startup=N");
48d54e4d
AJ
294 } else if (strncmp(token, "children-max=", 13) == 0) {
295 a->children.n_max = atoi(token + 13);
296 } else if (strncmp(token, "children-startup=", 17) == 0) {
297 a->children.n_startup = atoi(token + 17);
298 } else if (strncmp(token, "children-idle=", 14) == 0) {
299 a->children.n_idle = atoi(token + 14);
07eca7e0 300 } else if (strncmp(token, "concurrency=", 12) == 0) {
48d54e4d 301 a->children.concurrency = atoi(token + 12);
62e76326 302 } else if (strncmp(token, "cache=", 6) == 0) {
303 a->cache_size = atoi(token + 6);
47b0c1fa 304 } else if (strncmp(token, "grace=", 6) == 0) {
305 a->grace = atoi(token + 6);
dc1af3cf 306 } else if (strcmp(token, "protocol=2.5") == 0) {
307 a->quote = external_acl::QUOTE_METHOD_SHELL;
308 } else if (strcmp(token, "protocol=3.0") == 0) {
05e52854
AJ
309 debugs(3, DBG_PARSE_NOTE(2), "WARNING: external_acl_type option protocol=3.0 is deprecated. Remove this from your config.");
310 a->quote = external_acl::QUOTE_METHOD_URL;
dc1af3cf 311 } else if (strcmp(token, "quote=url") == 0) {
05e52854 312 debugs(3, DBG_PARSE_NOTE(2), "WARNING: external_acl_type option quote=url is deprecated. Remove this from your config.");
dc1af3cf 313 a->quote = external_acl::QUOTE_METHOD_URL;
314 } else if (strcmp(token, "quote=shell") == 0) {
05e52854 315 debugs(3, DBG_PARSE_NOTE(2), "WARNING: external_acl_type option quote=shell is deprecated. Use protocol=2.5 if still needed.");
dc1af3cf 316 a->quote = external_acl::QUOTE_METHOD_SHELL;
cc192b50 317
26ac0430
AJ
318 /* INET6: allow admin to configure some helpers explicitly to
319 bind to IPv4/v6 localhost port. */
cc192b50 320 } else if (strcmp(token, "ipv4") == 0) {
4dd643d5 321 if ( !a->local_addr.setIPv4() ) {
fa84c01d 322 debugs(3, DBG_CRITICAL, "WARNING: Error converting " << a->local_addr << " to IPv4 in " << a->name );
cc192b50 323 }
324 } else if (strcmp(token, "ipv6") == 0) {
055421ee 325 if (!Ip::EnableIpv6)
fa84c01d 326 debugs(3, DBG_CRITICAL, "WARNING: --enable-ipv6 required for external ACL helpers to use IPv6: " << a->name );
055421ee 327 // else nothing to do.
62e76326 328 } else {
329 break;
330 }
331
2eceb328 332 token = ConfigParser::NextToken();
d9572179 333 }
2eceb328 334 ConfigParser::DisableMacros();
62e76326 335
6f78f0ed 336 /* check that child startup value is sane. */
a3cecd6c
AJ
337 if (a->children.n_startup > a->children.n_max)
338 a->children.n_startup = a->children.n_max;
48d54e4d 339
6f78f0ed 340 /* check that child idle value is sane. */
a3cecd6c
AJ
341 if (a->children.n_idle > a->children.n_max)
342 a->children.n_idle = a->children.n_max;
343 if (a->children.n_idle < 1)
344 a->children.n_idle = 1;
48d54e4d 345
d9572179 346 if (a->negative_ttl == -1)
62e76326 347 a->negative_ttl = a->ttl;
d9572179 348
349 /* Parse format */
350 p = &a->format;
62e76326 351
d9572179 352 while (token) {
62e76326 353 external_acl_format *format;
354
355 /* stop on first non-format token found */
356
357 if (*token != '%')
358 break;
359
360 format = cbdataAlloc(external_acl_format);
361
362 if (strncmp(token, "%{", 2) == 0) {
7b0ca1e8 363 // deprecated. but assume the old configs all referred to request headers.
3650c90e 364 debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: external_acl_type format %{...} is being replaced by %>ha{...} for : " << token);
5aca9cf2 365 parse_header_token(format, (token+2), Format::LFT_ADAPTED_REQUEST_HEADER);
c68c9682 366 } else if (strncmp(token, "%>{", 3) == 0) {
3650c90e 367 debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: external_acl_type format %>{...} is being replaced by %>ha{...} for : " << token);
5aca9cf2 368 parse_header_token(format, (token+3), Format::LFT_ADAPTED_REQUEST_HEADER);
3650c90e 369 } else if (strncmp(token, "%>ha{", 5) == 0) {
5aca9cf2 370 parse_header_token(format, (token+3), Format::LFT_ADAPTED_REQUEST_HEADER);
c68c9682 371 } else if (strncmp(token, "%<{", 3) == 0) {
3650c90e 372 debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: external_acl_type format %<{...} is being replaced by %<h{...} for : " << token);
5aca9cf2 373 parse_header_token(format, (token+3), Format::LFT_REPLY_HEADER);
3650c90e 374 } else if (strncmp(token, "%<h{", 4) == 0) {
5aca9cf2 375 parse_header_token(format, (token+3), Format::LFT_REPLY_HEADER);
2f1431ea 376#if USE_AUTH
3650c90e 377 } else if (strcmp(token, "%LOGIN") == 0 || strcmp(token, "%ul") == 0) {
5aca9cf2 378 format->type = Format::LFT_USER_LOGIN;
e870b1f8 379 a->require_auth = true;
2f1431ea 380#endif
62e76326 381 }
7a16973f 382#if USE_IDENT
3650c90e 383 else if (strcmp(token, "%IDENT") == 0 || strcmp(token, "%ui") == 0)
5aca9cf2 384 format->type = Format::LFT_USER_IDENT;
7a16973f 385#endif
3650c90e 386 else if (strcmp(token, "%SRC") == 0 || strcmp(token, "%>a") == 0)
5aca9cf2 387 format->type = Format::LFT_CLIENT_IP_ADDRESS;
3650c90e 388 else if (strcmp(token, "%SRCPORT") == 0 || strcmp(token, "%>p") == 0)
5aca9cf2 389 format->type = Format::LFT_CLIENT_PORT;
a98c2da5
AJ
390#if USE_SQUID_EUI
391 else if (strcmp(token, "%SRCEUI48") == 0)
5aca9cf2 392 format->type = Format::LFT_EXT_ACL_CLIENT_EUI48;
a98c2da5 393 else if (strcmp(token, "%SRCEUI64") == 0)
5aca9cf2 394 format->type = Format::LFT_EXT_ACL_CLIENT_EUI64;
a98c2da5 395#endif
3650c90e 396 else if (strcmp(token, "%MYADDR") == 0 || strcmp(token, "%la") == 0)
5aca9cf2 397 format->type = Format::LFT_LOCAL_LISTENING_IP;
3650c90e 398 else if (strcmp(token, "%MYPORT") == 0 || strcmp(token, "%lp") == 0)
5aca9cf2 399 format->type = Format::LFT_LOCAL_LISTENING_PORT;
3650c90e 400 else if (strcmp(token, "%URI") == 0 || strcmp(token, "%>ru") == 0)
5aca9cf2
AJ
401 format->type = Format::LFT_CLIENT_REQ_URI;
402 else if (strcmp(token, "%DST") == 0 || strcmp(token, "%>rd") == 0)
403 format->type = Format::LFT_CLIENT_REQ_URLDOMAIN;
404 else if (strcmp(token, "%PROTO") == 0 || strcmp(token, "%>rs") == 0)
405 format->type = Format::LFT_CLIENT_REQ_URLSCHEME;
406 else if (strcmp(token, "%PORT") == 0) // XXX: add a logformat token
407 format->type = Format::LFT_CLIENT_REQ_URLPORT;
3650c90e 408 else if (strcmp(token, "%PATH") == 0 || strcmp(token, "%>rp") == 0)
5aca9cf2 409 format->type = Format::LFT_CLIENT_REQ_URLPATH;
3650c90e 410 else if (strcmp(token, "%METHOD") == 0 || strcmp(token, "%>rm") == 0)
5aca9cf2 411 format->type = Format::LFT_CLIENT_REQ_METHOD;
cb4f4424 412#if USE_OPENSSL
4ac9968f 413 else if (strcmp(token, "%USER_CERT") == 0)
5aca9cf2 414 format->type = Format::LFT_EXT_ACL_USER_CERT_RAW;
3d61c476 415 else if (strcmp(token, "%USER_CERTCHAIN") == 0)
5aca9cf2 416 format->type = Format::LFT_EXT_ACL_USER_CERTCHAIN_RAW;
c68c9682 417 else if (strncmp(token, "%USER_CERT_", 11) == 0) {
5aca9cf2 418 format->type = Format::LFT_EXT_ACL_USER_CERT;
62e76326 419 format->header = xstrdup(token + 11);
a8e35235 420 } else if (strncmp(token, "%USER_CA_CERT_", 11) == 0) {
5aca9cf2 421 format->type = Format::LFT_EXT_ACL_USER_CA_CERT;
a8e35235 422 format->header = xstrdup(token + 11);
c68c9682 423 } else if (strncmp(token, "%CA_CERT_", 11) == 0) {
3650c90e 424 debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: external_acl_type %CA_CERT_* code is obsolete. Use %USER_CA_CERT_* instead");
5aca9cf2 425 format->type = Format::LFT_EXT_ACL_USER_CA_CERT;
62e76326 426 format->header = xstrdup(token + 11);
cedca6e7
CT
427 } else if (strcmp(token, "%ssl::>sni") == 0)
428 format->type = Format::LFT_SSL_CLIENT_SNI;
a7ad6e4e 429#endif
2f1431ea 430#if USE_AUTH
5aca9cf2
AJ
431 else if (strcmp(token, "%EXT_USER") == 0 || strcmp(token, "%ue") == 0)
432 format->type = Format::LFT_USER_EXTERNAL;
2f1431ea 433#endif
5aca9cf2
AJ
434 else if (strcmp(token, "%EXT_LOG") == 0 || strcmp(token, "%ea") == 0)
435 format->type = Format::LFT_EXT_LOG;
436 else if (strcmp(token, "%TAG") == 0 || strcmp(token, "%et") == 0)
437 format->type = Format::LFT_TAG;
ec2d5242 438 else if (strcmp(token, "%ACL") == 0)
5aca9cf2 439 format->type = Format::LFT_EXT_ACL_NAME;
ec2d5242 440 else if (strcmp(token, "%DATA") == 0)
5aca9cf2 441 format->type = Format::LFT_EXT_ACL_DATA;
0db8942f 442 else if (strcmp(token, "%%") == 0)
5aca9cf2 443 format->type = Format::LFT_PERCENT;
62e76326 444 else {
fa84c01d 445 debugs(0, DBG_CRITICAL, "ERROR: Unknown Format token " << token);
62e76326 446 self_destruct();
447 }
448
449 *p = format;
450 p = &format->next;
2eceb328 451 token = ConfigParser::NextToken();
d9572179 452 }
453
454 /* There must be at least one format token */
455 if (!a->format)
62e76326 456 self_destruct();
d9572179 457
458 /* helper */
459 if (!token)
62e76326 460 self_destruct();
461
d9572179 462 wordlistAdd(&a->cmdline, token);
463
464 /* arguments */
a336d130 465 parse_wordlist(&a->cmdline);
d9572179 466
467 while (*list)
62e76326 468 list = &(*list)->next;
469
d9572179 470 *list = a;
471}
472
473void
474dump_externalAclHelper(StoreEntry * sentry, const char *name, const external_acl * list)
475{
476 const external_acl *node;
477 const external_acl_format *format;
478 const wordlist *word;
62e76326 479
d9572179 480 for (node = list; node; node = node->next) {
62e76326 481 storeAppendPrintf(sentry, "%s %s", name, node->name);
482
4dd643d5 483 if (!node->local_addr.isIPv6())
cc192b50 484 storeAppendPrintf(sentry, " ipv4");
485 else
486 storeAppendPrintf(sentry, " ipv6");
487
62e76326 488 if (node->ttl != DEFAULT_EXTERNAL_ACL_TTL)
489 storeAppendPrintf(sentry, " ttl=%d", node->ttl);
490
491 if (node->negative_ttl != node->ttl)
492 storeAppendPrintf(sentry, " negative_ttl=%d", node->negative_ttl);
493
47b0c1fa 494 if (node->grace)
495 storeAppendPrintf(sentry, " grace=%d", node->grace);
496
48d54e4d
AJ
497 if (node->children.n_max != DEFAULT_EXTERNAL_ACL_CHILDREN)
498 storeAppendPrintf(sentry, " children-max=%d", node->children.n_max);
62e76326 499
48d54e4d
AJ
500 if (node->children.n_startup != 1)
501 storeAppendPrintf(sentry, " children-startup=%d", node->children.n_startup);
62e76326 502
48d54e4d
AJ
503 if (node->children.n_idle != (node->children.n_max + node->children.n_startup) )
504 storeAppendPrintf(sentry, " children-idle=%d", node->children.n_idle);
505
506 if (node->children.concurrency)
507 storeAppendPrintf(sentry, " concurrency=%d", node->children.concurrency);
07eca7e0 508
47b0c1fa 509 if (node->cache)
510 storeAppendPrintf(sentry, " cache=%d", node->cache_size);
511
05e52854
AJ
512 if (node->quote == external_acl::QUOTE_METHOD_SHELL)
513 storeAppendPrintf(sentry, " protocol=2.5");
514
62e76326 515 for (format = node->format; format; format = format->next) {
516 switch (format->type) {
517
5aca9cf2
AJ
518 case Format::LFT_ADAPTED_REQUEST_HEADER:
519 storeAppendPrintf(sentry, " %%>ha{%s}", format->header);
62e76326 520 break;
521
5aca9cf2
AJ
522 case Format::LFT_ADAPTED_REQUEST_HEADER_ELEM:
523 storeAppendPrintf(sentry, " %%>ha{%s:%s}", format->header, format->member);
2170db3e
AJ
524 break;
525
5aca9cf2
AJ
526 case Format::LFT_REPLY_HEADER:
527 storeAppendPrintf(sentry, " %%<h{%s}", format->header);
2170db3e
AJ
528 break;
529
5aca9cf2
AJ
530 case Format::LFT_REPLY_HEADER_ELEM:
531 storeAppendPrintf(sentry, " %%<h{%s:%s}", format->header, format->member);
62e76326 532 break;
5aca9cf2 533
7456a5c9 534#define DUMP_EXT_ACL_TYPE_FMT(a, fmt, ...) \
5aca9cf2 535 case Format::LFT_##a: \
7456a5c9
RC
536 storeAppendPrintf(sentry, fmt, ##__VA_ARGS__); \
537 break
2f1431ea 538#if USE_AUTH
5aca9cf2 539 DUMP_EXT_ACL_TYPE_FMT(USER_LOGIN," %%ul");
2f1431ea 540#endif
8f45b34c 541#if USE_IDENT
62e76326 542
5aca9cf2 543 DUMP_EXT_ACL_TYPE_FMT(USER_IDENT," %%ui");
8f45b34c 544#endif
5aca9cf2
AJ
545 DUMP_EXT_ACL_TYPE_FMT(CLIENT_IP_ADDRESS," %%>a");
546 DUMP_EXT_ACL_TYPE_FMT(CLIENT_PORT," %%>p");
a98c2da5 547#if USE_SQUID_EUI
5aca9cf2
AJ
548 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_CLIENT_EUI48," %%SRCEUI48");
549 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_CLIENT_EUI64," %%SRCEUI64");
a98c2da5 550#endif
5aca9cf2
AJ
551 DUMP_EXT_ACL_TYPE_FMT(LOCAL_LISTENING_IP," %%>la");
552 DUMP_EXT_ACL_TYPE_FMT(LOCAL_LISTENING_PORT," %%>lp");
553 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URI," %%>ru");
554 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLDOMAIN," %%>rd");
555 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLSCHEME," %%>rs");
556 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLPORT," %%>rP");
557 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLPATH," %%>rp");
558 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_METHOD," %%>rm");
cb4f4424 559#if USE_OPENSSL
5aca9cf2
AJ
560 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERT_RAW, " %%USER_CERT_RAW");
561 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERTCHAIN_RAW, " %%USER_CERTCHAIN_RAW");
562 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERT, " %%USER_CERT_%s", format->header);
563 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CA_CERT, " %%USER_CA_CERT_%s", format->header);
cedca6e7 564 DUMP_EXT_ACL_TYPE_FMT(SSL_CLIENT_SNI, "ssl::>sni");
1f183752 565#endif
2f1431ea 566#if USE_AUTH
5aca9cf2 567 DUMP_EXT_ACL_TYPE_FMT(USER_EXTERNAL," %%ue");
2f1431ea 568#endif
5aca9cf2
AJ
569 DUMP_EXT_ACL_TYPE_FMT(EXT_LOG," %%ea");
570 DUMP_EXT_ACL_TYPE_FMT(TAG," %%et");
571 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_NAME," %%ACL");
572 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_DATA," %%DATA");
7456a5c9 573 DUMP_EXT_ACL_TYPE_FMT(PERCENT, " %%%%");
4ecc6631 574 default:
62e76326 575 fatal("unknown external_acl format error");
576 break;
577 }
578 }
579
580 for (word = node->cmdline; word; word = word->next)
581 storeAppendPrintf(sentry, " %s", word->key);
582
583 storeAppendPrintf(sentry, "\n");
d9572179 584 }
585}
586
587void
588free_externalAclHelper(external_acl ** list)
589{
590 while (*list) {
62e76326 591 external_acl *node = *list;
592 *list = node->next;
593 node->next = NULL;
594 cbdataFree(node);
d9572179 595 }
596}
597
598static external_acl *
599find_externalAclHelper(const char *name)
600{
601 external_acl *node;
602
603 for (node = Config.externalAclHelperList; node; node = node->next) {
62e76326 604 if (strcmp(node->name, name) == 0)
605 return node;
d9572179 606 }
62e76326 607
d9572179 608 return NULL;
609}
610
1e5562e3 611void
6ca34f6f 612external_acl::add(ExternalACLEntry *anEntry)
1e5562e3 613{
614 trimCache();
615 assert (anEntry->def == NULL);
616 anEntry->def = this;
617 hash_join(cache, anEntry);
618 dlinkAdd(anEntry, &anEntry->lru, &lru_list);
95dc7ff4 619 ++cache_entries;
1e5562e3 620}
621
622void
623external_acl::trimCache()
624{
625 if (cache_size && cache_entries >= cache_size)
626 external_acl_cache_delete(this, static_cast<external_acl_entry *>(lru_list.tail->data));
627}
628
d9572179 629/******************************************************************
630 * external acl type
631 */
632
26ac0430 633struct _external_acl_data {
d9572179 634 external_acl *def;
ec2d5242 635 const char *name;
d9572179 636 wordlist *arguments;
637};
638
639CBDATA_TYPE(external_acl_data);
640static void
641free_external_acl_data(void *data)
642{
e6ccf245 643 external_acl_data *p = static_cast<external_acl_data *>(data);
ec2d5242 644 safe_free(p->name);
d9572179 645 wordlistDestroy(&p->arguments);
646 cbdataReferenceDone(p->def);
647}
648
649void
b0dd28ba 650ACLExternal::parse()
d9572179 651{
d9572179 652 char *token;
62e76326 653
b0dd28ba 654 if (data)
62e76326 655 self_destruct();
656
d9572179 657 CBDATA_INIT_TYPE_FREECB(external_acl_data, free_external_acl_data);
62e76326 658
d9572179 659 data = cbdataAlloc(external_acl_data);
62e76326 660
33810b1d 661 token = strtokFile();
62e76326 662
d9572179 663 if (!token)
62e76326 664 self_destruct();
665
d9572179 666 data->def = cbdataReference(find_externalAclHelper(token));
62e76326 667
d9572179 668 if (!data->def)
62e76326 669 self_destruct();
670
ec2d5242
HN
671 // def->name is the name of the external_acl_type.
672 // this is the name of the 'acl' directive being tested
673 data->name = xstrdup(AclMatchedName);
674
4d091667 675 while ((token = strtokFile())) {
62e76326 676 wordlistAdd(&data->arguments, token);
d9572179 677 }
d9572179 678}
679
4b0f5de8 680bool
681ACLExternal::valid () const
682{
2f1431ea 683#if USE_AUTH
4b0f5de8 684 if (data->def->require_auth) {
685 if (authenticateSchemeCount() == 0) {
fa84c01d 686 debugs(28, DBG_CRITICAL, "Can't use proxy auth because no authentication schemes were compiled.");
4b0f5de8 687 return false;
688 }
689
690 if (authenticateActiveSchemeCount() == 0) {
fa84c01d 691 debugs(28, DBG_CRITICAL, "Can't use proxy auth because no authentication schemes are fully configured.");
4b0f5de8 692 return false;
693 }
694 }
2f1431ea 695#endif
4b0f5de8 696
697 return true;
698}
699
700bool
701ACLExternal::empty () const
702{
703 return false;
704}
705
b0dd28ba 706ACLExternal::~ACLExternal()
d9572179 707{
b0dd28ba 708 cbdataFree(data);
709 safe_free (class_);
d9572179 710}
711
1abe0161
AJ
712static void
713copyResultsFromEntry(HttpRequest *req, external_acl_entry *entry)
714{
715 if (req) {
716#if USE_AUTH
717 if (entry->user.size())
718 req->extacl_user = entry->user;
719
720 if (entry->password.size())
721 req->extacl_passwd = entry->password;
722#endif
723 if (!req->tag.size())
724 req->tag = entry->tag;
725
726 if (entry->log.size())
727 req->extacl_log = entry->log;
728
729 if (entry->message.size())
730 req->extacl_message = entry->message;
731 }
732}
733
e5f825ea 734static allow_t
c0941a6a 735aclMatchExternal(external_acl_data *acl, ACLFilledChecklist *ch)
d9572179 736{
c69ce640 737 debugs(82, 9, HERE << "acl=\"" << acl->def->name << "\"");
e5f825ea 738 external_acl_entry *entry = ch->extacl_entry;
62e76326 739
6f58d7d7
AR
740 external_acl_message = "MISSING REQUIRED INFORMATION";
741
d9572179 742 if (entry) {
c69ce640 743 if (cbdataReferenceValid(entry) && entry->def == acl->def) {
ad06254e 744 /* Ours, use it.. if the key matches */
6f58d7d7
AR
745 const char *key = makeExternalAclKey(ch, acl);
746 if (!key)
747 return ACCESS_DUNNO; // insufficent data to continue
ad06254e
AJ
748 if (strcmp(key, (char*)entry->key) != 0) {
749 debugs(82, 9, HERE << "entry key='" << (char *)entry->key << "', our key='" << key << "' dont match. Discarded.");
750 // too bad. need a new lookup.
751 cbdataReferenceDone(ch->extacl_entry);
752 entry = NULL;
753 }
62e76326 754 } else {
755 /* Not valid, or not ours.. get rid of it */
c69ce640
AJ
756 debugs(82, 9, HERE << "entry " << entry << " not valid or not ours. Discarded.");
757 if (entry) {
758 debugs(82, 9, HERE << "entry def=" << entry->def << ", our def=" << acl->def);
6f58d7d7 759 const char *key = makeExternalAclKey(ch, acl); // may be nil
c69ce640
AJ
760 debugs(82, 9, HERE << "entry key='" << (char *)entry->key << "', our key='" << key << "'");
761 }
62e76326 762 cbdataReferenceDone(ch->extacl_entry);
763 entry = NULL;
764 }
d9572179 765 }
62e76326 766
d9572179 767 if (!entry) {
c69ce640 768 debugs(82, 9, HERE << "No helper entry available");
2f1431ea 769#if USE_AUTH
62e76326 770 if (acl->def->require_auth) {
62e76326 771 /* Make sure the user is authenticated */
e5f825ea 772 debugs(82, 3, HERE << acl->def->name << " check user authenticated.");
e0f7153c
AR
773 const allow_t ti = AuthenticateAcl(ch);
774 if (ti != ACCESS_ALLOWED) {
e5f825ea 775 debugs(82, 2, HERE << acl->def->name << " user not authenticated (" << ti << ")");
e0f7153c 776 return ti;
62e76326 777 }
e5f825ea 778 debugs(82, 3, HERE << acl->def->name << " user is authenticated.");
62e76326 779 }
2f1431ea 780#endif
6f58d7d7 781 const char *key = makeExternalAclKey(ch, acl);
41218131 782
783 if (!key) {
784 /* Not sufficient data to process */
e5f825ea 785 return ACCESS_DUNNO;
41218131 786 }
787
62e76326 788 entry = static_cast<external_acl_entry *>(hash_lookup(acl->def->cache, key));
789
e0f7153c
AR
790 external_acl_entry *staleEntry = entry;
791 if (entry && external_acl_entry_expired(acl->def, entry))
792 entry = NULL;
793
794 if (entry && external_acl_grace_expired(acl->def, entry)) {
795 // refresh in the background
796 ExternalACLLookup::Start(ch, acl, true);
797 debugs(82, 4, HERE << "no need to wait for the refresh of '" <<
798 key << "' in '" << acl->def->name << "' (ch=" << ch << ").");
799 }
800
801 if (!entry) {
e5f825ea
AJ
802 debugs(82, 2, HERE << acl->def->name << "(\"" << key << "\") = lookup needed");
803 debugs(82, 2, HERE << "\"" << key << "\": entry=@" <<
bf8fe701 804 entry << ", age=" << (entry ? (long int) squid_curtime - entry->date : 0));
95a83c22 805
c1e22df0 806 if (acl->def->theHelper->stats.queue_size < (int)acl->def->theHelper->childs.n_active) {
e5f825ea 807 debugs(82, 2, HERE << "\"" << key << "\": queueing a call.");
6f58d7d7
AR
808 if (!ch->goAsync(ExternalACLLookup::Instance()))
809 debugs(82, 2, "\"" << key << "\": no async support!");
e5f825ea 810 debugs(82, 2, HERE << "\"" << key << "\": return -1.");
e0f7153c 811 return ACCESS_DUNNO; // expired cached or simply absent entry
47b0c1fa 812 } else {
e0f7153c 813 if (!staleEntry) {
e5f825ea 814 debugs(82, DBG_IMPORTANT, "WARNING: external ACL '" << acl->def->name <<
bf8fe701 815 "' queue overload. Request rejected '" << key << "'.");
4a972fa2 816 external_acl_message = "SYSTEM TOO BUSY, TRY AGAIN LATER";
e5f825ea 817 return ACCESS_DUNNO;
47b0c1fa 818 } else {
e5f825ea 819 debugs(82, DBG_IMPORTANT, "WARNING: external ACL '" << acl->def->name <<
bf8fe701 820 "' queue overload. Using stale result. '" << key << "'.");
e0f7153c 821 entry = staleEntry;
47b0c1fa 822 /* Fall thru to processing below */
823 }
824 }
825 }
d9572179 826 }
62e76326 827
e0f7153c
AR
828 debugs(82, 4, HERE << "entry = { date=" <<
829 (long unsigned int) entry->date <<
830 ", result=" << entry->result <<
831 " tag=" << entry->tag <<
832 " log=" << entry->log << " }");
833#if USE_AUTH
834 debugs(82, 4, HERE << "entry user=" << entry->user);
835#endif
836
d9572179 837 external_acl_cache_touch(acl->def, entry);
a7a42b14 838 external_acl_message = entry->message.termedBuf();
4a972fa2 839
e5f825ea 840 debugs(82, 2, HERE << acl->def->name << " = " << entry->result);
1abe0161 841 copyResultsFromEntry(ch->request, entry);
e5f825ea 842 return entry->result;
d9572179 843}
844
b0dd28ba 845int
846ACLExternal::match(ACLChecklist *checklist)
847{
e5f825ea 848 allow_t answer = aclMatchExternal(data, Filled(checklist));
e5f825ea
AJ
849
850 // convert to tri-state ACL match 1,0,-1
9b831d57 851 switch (answer) {
e5f825ea 852 case ACCESS_ALLOWED:
e5f825ea
AJ
853 return 1; // match
854
855 case ACCESS_DENIED:
e5f825ea
AJ
856 return 0; // non-match
857
858 case ACCESS_DUNNO:
859 case ACCESS_AUTH_REQUIRED:
860 default:
e0f7153c 861 // If the answer is not allowed or denied (matches/not matches) and
6f58d7d7
AR
862 // async authentication is not in progress, then we are done.
863 if (checklist->keepMatching())
e0f7153c 864 checklist->markFinished(answer, "aclMatchExternal exception");
e5f825ea
AJ
865 return -1; // other
866 }
b0dd28ba 867}
868
dfad5100 869SBufList
b0dd28ba 870ACLExternal::dump() const
d9572179 871{
b0dd28ba 872 external_acl_data const *acl = data;
dfad5100
FC
873 SBufList rv;
874 rv.push_back(SBuf(acl->def->name));
62e76326 875
dfad5100
FC
876 for (wordlist *arg = acl->arguments; arg; arg = arg->next) {
877 SBuf s;
878 s.Printf(" %s", arg->key);
879 rv.push_back(s);
d9572179 880 }
62e76326 881
dfad5100 882 return rv;
d9572179 883}
884
885/******************************************************************
886 * external_acl cache
887 */
888
d9572179 889static void
890external_acl_cache_touch(external_acl * def, external_acl_entry * entry)
891{
c69ce640
AJ
892 // this must not be done when nothing is being cached.
893 if (def->cache_size <= 0 || (def->ttl <= 0 && entry->result == 1) || (def->negative_ttl <= 0 && entry->result != 1))
894 return;
895
d9572179 896 dlinkDelete(&entry->lru, &def->lru_list);
897 dlinkAdd(entry, &entry->lru, &def->lru_list);
898}
899
900static char *
c0941a6a 901makeExternalAclKey(ACLFilledChecklist * ch, external_acl_data * acl_data)
d9572179 902{
032785bf 903 static MemBuf mb;
d9572179 904 char buf[256];
905 int first = 1;
906 wordlist *arg;
907 external_acl_format *format;
190154cf 908 HttpRequest *request = ch->request;
7b0ca1e8 909 HttpReply *reply = ch->reply;
2fe7eff9 910 mb.reset();
ec2d5242 911 bool data_used = false;
62e76326 912
d9572179 913 for (format = acl_data->def->format; format; format = format->next) {
62e76326 914 const char *str = NULL;
30abd221 915 String sb;
62e76326 916
917 switch (format->type) {
2f1431ea 918#if USE_AUTH
5aca9cf2 919 case Format::LFT_USER_LOGIN:
d28c476f
AJ
920 // if this ACL line was the cause of credentials fetch
921 // they may not already be in the checklist
922 if (ch->auth_user_request == NULL && ch->request)
923 ch->auth_user_request = ch->request->auth_user_request;
924
925 if (ch->auth_user_request != NULL)
926 str = ch->auth_user_request->username();
62e76326 927 break;
2f1431ea 928#endif
7a16973f 929#if USE_IDENT
5aca9cf2 930 case Format::LFT_USER_IDENT:
62e76326 931 str = ch->rfc931;
932
41218131 933 if (!str || !*str) {
6f58d7d7
AR
934 // if we fail to go async, we still return NULL and the caller
935 // will detect the failure in ACLExternal::match().
936 (void)ch->goAsync(IdentLookup::Instance());
62e76326 937 return NULL;
938 }
939
940 break;
7a16973f 941#endif
62e76326 942
5aca9cf2 943 case Format::LFT_CLIENT_IP_ADDRESS:
4dd643d5 944 str = ch->src_addr.toStr(buf,sizeof(buf));
62e76326 945 break;
946
5aca9cf2 947 case Format::LFT_CLIENT_PORT:
4dd643d5 948 snprintf(buf, sizeof(buf), "%d", request->client_addr.port());
47b0c1fa 949 str = buf;
950 break;
951
a98c2da5 952#if USE_SQUID_EUI
5aca9cf2 953 case Format::LFT_EXT_ACL_CLIENT_EUI48:
40d34a62
AJ
954 if (request->clientConnectionManager.valid() && request->clientConnectionManager->clientConnection != NULL &&
955 request->clientConnectionManager->clientConnection->remoteEui48.encode(buf, sizeof(buf)))
a98c2da5
AJ
956 str = buf;
957 break;
958
5aca9cf2 959 case Format::LFT_EXT_ACL_CLIENT_EUI64:
40d34a62
AJ
960 if (request->clientConnectionManager.valid() && request->clientConnectionManager->clientConnection != NULL &&
961 request->clientConnectionManager->clientConnection->remoteEui64.encode(buf, sizeof(buf)))
a98c2da5
AJ
962 str = buf;
963 break;
964#endif
965
5aca9cf2 966 case Format::LFT_LOCAL_LISTENING_IP:
4dd643d5 967 str = request->my_addr.toStr(buf, sizeof(buf));
47b0c1fa 968 break;
969
5aca9cf2 970 case Format::LFT_LOCAL_LISTENING_PORT:
4dd643d5 971 snprintf(buf, sizeof(buf), "%d", request->my_addr.port());
47b0c1fa 972 str = buf;
973 break;
974
5aca9cf2 975 case Format::LFT_CLIENT_REQ_URI:
3cf6e9c5 976 str = urlCanonical(request);
977 break;
978
5aca9cf2 979 case Format::LFT_CLIENT_REQ_URLDOMAIN:
cc192b50 980 str = request->GetHost();
62e76326 981 break;
982
5aca9cf2 983 case Format::LFT_CLIENT_REQ_URLSCHEME:
4e3f4dc7 984 str = request->url.getScheme().c_str();
62e76326 985 break;
986
5aca9cf2 987 case Format::LFT_CLIENT_REQ_URLPORT:
62e76326 988 snprintf(buf, sizeof(buf), "%d", request->port);
989 str = buf;
990 break;
991
5aca9cf2 992 case Format::LFT_CLIENT_REQ_URLPATH:
a313a9ba 993 str = request->urlpath.termedBuf();
62e76326 994 break;
995
e2849af8
A
996 case Format::LFT_CLIENT_REQ_METHOD: {
997 const SBuf &s = request->method.image();
998 sb.append(s.rawContent(), s.length());
999 }
1000 str = sb.termedBuf();
1001 break;
62e76326 1002
5aca9cf2
AJ
1003 case Format::LFT_ADAPTED_REQUEST_HEADER:
1004 if (format->header_id == -1)
1005 sb = request->header.getByName(format->header);
1006 else
1007 sb = request->header.getStrOrList(format->header_id);
a313a9ba 1008 str = sb.termedBuf();
62e76326 1009 break;
1010
5aca9cf2
AJ
1011 case Format::LFT_ADAPTED_REQUEST_HEADER_ELEM:
1012 if (format->header_id == -1)
1013 sb = request->header.getByNameListMember(format->header, format->member, format->separator);
1014 else
1015 sb = request->header.getListMember(format->header_id, format->member, format->separator);
a313a9ba 1016 str = sb.termedBuf();
62e76326 1017 break;
7b0ca1e8 1018
5aca9cf2 1019 case Format::LFT_REPLY_HEADER:
26ac0430 1020 if (reply) {
5aca9cf2
AJ
1021 if (format->header_id == -1)
1022 sb = reply->header.getByName(format->header);
1023 else
1024 sb = reply->header.getStrOrList(format->header_id);
a313a9ba 1025 str = sb.termedBuf();
7b0ca1e8
AJ
1026 }
1027 break;
1028
5aca9cf2 1029 case Format::LFT_REPLY_HEADER_ELEM:
26ac0430 1030 if (reply) {
5aca9cf2
AJ
1031 if (format->header_id == -1)
1032 sb = reply->header.getByNameListMember(format->header, format->member, format->separator);
1033 else
1034 sb = reply->header.getListMember(format->header_id, format->member, format->separator);
a313a9ba 1035 str = sb.termedBuf();
7b0ca1e8
AJ
1036 }
1037 break;
1038
cb4f4424 1039#if USE_OPENSSL
62e76326 1040
5aca9cf2 1041 case Format::LFT_EXT_ACL_USER_CERT_RAW:
4ac9968f 1042
73c36fd9
AJ
1043 if (ch->conn() != NULL && Comm::IsConnOpen(ch->conn()->clientConnection)) {
1044 SSL *ssl = fd_table[ch->conn()->clientConnection->fd].ssl;
4ac9968f 1045
1046 if (ssl)
1047 str = sslGetUserCertificatePEM(ssl);
1048 }
1049
1050 break;
1051
5aca9cf2 1052 case Format::LFT_EXT_ACL_USER_CERTCHAIN_RAW:
3d61c476 1053
73c36fd9
AJ
1054 if (ch->conn() != NULL && Comm::IsConnOpen(ch->conn()->clientConnection)) {
1055 SSL *ssl = fd_table[ch->conn()->clientConnection->fd].ssl;
3d61c476 1056
1057 if (ssl)
1058 str = sslGetUserCertificateChainPEM(ssl);
1059 }
1060
1061 break;
1062
5aca9cf2 1063 case Format::LFT_EXT_ACL_USER_CERT:
62e76326 1064
73c36fd9
AJ
1065 if (ch->conn() != NULL && Comm::IsConnOpen(ch->conn()->clientConnection)) {
1066 SSL *ssl = fd_table[ch->conn()->clientConnection->fd].ssl;
62e76326 1067
1068 if (ssl)
1069 str = sslGetUserAttribute(ssl, format->header);
1070 }
1071
1072 break;
1073
5aca9cf2 1074 case Format::LFT_EXT_ACL_USER_CA_CERT:
62e76326 1075
73c36fd9
AJ
1076 if (ch->conn() != NULL && Comm::IsConnOpen(ch->conn()->clientConnection)) {
1077 SSL *ssl = fd_table[ch->conn()->clientConnection->fd].ssl;
62e76326 1078
1079 if (ssl)
1080 str = sslGetCAAttribute(ssl, format->header);
1081 }
1082
1083 break;
cedca6e7
CT
1084
1085 case Format::LFT_SSL_CLIENT_SNI:
1086 if (ch->conn() != NULL) {
1087 if (Ssl::ServerBump * srvBump = ch->conn()->serverBump()) {
1088 if (!srvBump->clientSni.isEmpty())
1089 str = srvBump->clientSni.c_str();
1090 }
1091 }
1092 break;
a7ad6e4e 1093#endif
2f1431ea 1094#if USE_AUTH
5aca9cf2 1095 case Format::LFT_USER_EXTERNAL:
a313a9ba 1096 str = request->extacl_user.termedBuf();
abb929f0 1097 break;
2f1431ea 1098#endif
5aca9cf2 1099 case Format::LFT_EXT_LOG:
99e4ad67
JB
1100 str = request->extacl_log.termedBuf();
1101 break;
5aca9cf2 1102 case Format::LFT_TAG:
99e4ad67
JB
1103 str = request->tag.termedBuf();
1104 break;
5aca9cf2 1105 case Format::LFT_EXT_ACL_NAME:
ec2d5242
HN
1106 str = acl_data->name;
1107 break;
5aca9cf2 1108 case Format::LFT_EXT_ACL_DATA:
ec2d5242
HN
1109 data_used = true;
1110 for (arg = acl_data->arguments; arg; arg = arg->next) {
1111 if (!first)
1112 sb.append(" ", 1);
1113
1114 if (acl_data->def->quote == external_acl::QUOTE_METHOD_URL) {
1115 const char *quoted = rfc1738_escape(arg->key);
1116 sb.append(quoted, strlen(quoted));
1117 } else {
1118 static MemBuf mb2;
1119 mb2.init();
1120 strwordquote(&mb2, arg->key);
1121 sb.append(mb2.buf, mb2.size);
1122 mb2.clean();
1123 }
1124
1125 first = 0;
1126 }
1127 break;
5aca9cf2 1128 case Format::LFT_PERCENT:
0db8942f
AJ
1129 str = "%";
1130 break;
62e76326 1131
5aca9cf2
AJ
1132 default:
1133 // TODO: replace this function with Format::assemble()
1134 // For now die on unsupported logformat codes.
1135 fatalf("ERROR: unknown external_acl_type format %u", (uint8_t)format->type);
62e76326 1136 break;
1137 }
1138
1139 if (str)
1140 if (!*str)
1141 str = NULL;
1142
1143 if (!str)
1144 str = "-";
1145
1146 if (!first)
2fe7eff9 1147 mb.append(" ", 1);
62e76326 1148
dc1af3cf 1149 if (acl_data->def->quote == external_acl::QUOTE_METHOD_URL) {
1150 const char *quoted = rfc1738_escape(str);
2fe7eff9 1151 mb.append(quoted, strlen(quoted));
dc1af3cf 1152 } else {
1153 strwordquote(&mb, str);
1154 }
62e76326 1155
30abd221 1156 sb.clean();
62e76326 1157
1158 first = 0;
d9572179 1159 }
62e76326 1160
ec2d5242
HN
1161 if (!data_used) {
1162 for (arg = acl_data->arguments; arg; arg = arg->next) {
1163 if (!first)
1164 mb.append(" ", 1);
62e76326 1165
ec2d5242
HN
1166 if (acl_data->def->quote == external_acl::QUOTE_METHOD_URL) {
1167 const char *quoted = rfc1738_escape(arg->key);
1168 mb.append(quoted, strlen(quoted));
1169 } else {
1170 strwordquote(&mb, arg->key);
1171 }
62e76326 1172
ec2d5242
HN
1173 first = 0;
1174 }
d9572179 1175 }
62e76326 1176
d9572179 1177 return mb.buf;
d9572179 1178}
1179
1180static int
1181external_acl_entry_expired(external_acl * def, external_acl_entry * entry)
1182{
c69ce640
AJ
1183 if (def->cache_size <= 0)
1184 return 1;
1185
d9572179 1186 if (entry->date + (entry->result == 1 ? def->ttl : def->negative_ttl) < squid_curtime)
62e76326 1187 return 1;
d9572179 1188 else
62e76326 1189 return 0;
d9572179 1190}
62e76326 1191
47b0c1fa 1192static int
1193external_acl_grace_expired(external_acl * def, external_acl_entry * entry)
1194{
c69ce640
AJ
1195 if (def->cache_size <= 0)
1196 return 1;
1197
47b0c1fa 1198 int ttl;
1199 ttl = entry->result == 1 ? def->ttl : def->negative_ttl;
1200 ttl = (ttl * (100 - def->grace)) / 100;
1201
2e1359a0 1202 if (entry->date + ttl <= squid_curtime)
47b0c1fa 1203 return 1;
1204 else
1205 return 0;
1206}
1207
d9572179 1208static external_acl_entry *
1e5562e3 1209external_acl_cache_add(external_acl * def, const char *key, ExternalACLEntryData const & data)
d9572179 1210{
c69ce640
AJ
1211 ExternalACLEntry *entry;
1212
1213 // do not bother caching this result if TTL is going to expire it immediately
1214 if (def->cache_size <= 0 || (def->ttl <= 0 && data.result == 1) || (def->negative_ttl <= 0 && data.result != 1)) {
1215 debugs(82,6, HERE);
1216 entry = new ExternalACLEntry;
1217 entry->key = xstrdup(key);
1218 entry->update(data);
1219 entry->def = def;
1220 return entry;
1221 }
1222
1223 entry = static_cast<ExternalACLEntry *>(hash_lookup(def->cache, key));
bf8fe701 1224 debugs(82, 2, "external_acl_cache_add: Adding '" << key << "' = " << data.result);
62e76326 1225
d9572179 1226 if (entry) {
bf8fe701 1227 debugs(82, 3, "ExternalACLEntry::update: updating existing entry");
c69ce640 1228 entry->update(data);
62e76326 1229 external_acl_cache_touch(def, entry);
1230
1231 return entry;
d9572179 1232 }
62e76326 1233
1e5562e3 1234 entry = new ExternalACLEntry;
4a8b20e8 1235 entry->key = xstrdup(key);
c69ce640 1236 entry->update(data);
1e5562e3 1237
6ca34f6f 1238 def->add(entry);
1e5562e3 1239
d9572179 1240 return entry;
1241}
1242
1243static void
1244external_acl_cache_delete(external_acl * def, external_acl_entry * entry)
1245{
c69ce640 1246 assert(def->cache_size > 0 && entry->def == def);
4a8b20e8 1247 hash_remove_link(def->cache, entry);
d9572179 1248 dlinkDelete(&entry->lru, &def->lru_list);
1249 def->cache_entries -= 1;
00d77d6b 1250 delete entry;
d9572179 1251}
1252
1253/******************************************************************
1254 * external_acl helpers
1255 */
1256
1257typedef struct _externalAclState externalAclState;
62e76326 1258
26ac0430 1259struct _externalAclState {
d9572179 1260 EAH *callback;
1261 void *callback_data;
1262 char *key;
1263 external_acl *def;
1264 dlink_node list;
1265 externalAclState *queue;
1266};
1267
1268CBDATA_TYPE(externalAclState);
1269static void
1270free_externalAclState(void *data)
1271{
e6ccf245 1272 externalAclState *state = static_cast<externalAclState *>(data);
d9572179 1273 safe_free(state->key);
1274 cbdataReferenceDone(state->callback_data);
1275 cbdataReferenceDone(state->def);
1276}
1277
d9572179 1278/*
1279 * The helper program receives queries on stdin, one
07eca7e0 1280 * per line, and must return the result on on stdout
d9572179 1281 *
1282 * General result syntax:
1283 *
1284 * OK/ERR keyword=value ...
1285 *
1286 * Keywords:
1287 *
4a972fa2 1288 * user= The users name (login)
1289 * message= Message describing the reason
1290 * tag= A string tag to be applied to the request that triggered the acl match.
1291 * applies to both OK and ERR responses.
1292 * Won't override existing request tags.
1293 * log= A string to be used in access logging
d9572179 1294 *
1295 * Other keywords may be added to the protocol later
1296 *
05e52854
AJ
1297 * value needs to be URL-encoded or enclosed in double quotes (")
1298 * with \-escaping on any whitespace, quotes, or slashes (\).
d9572179 1299 */
d9572179 1300static void
0272dd08 1301externalAclHandleReply(void *data, const HelperReply &reply)
d9572179 1302{
e6ccf245 1303 externalAclState *state = static_cast<externalAclState *>(data);
d9572179 1304 externalAclState *next;
1e5562e3 1305 ExternalACLEntryData entryData;
e5f825ea 1306 entryData.result = ACCESS_DENIED;
466090ac 1307 external_acl_entry *entry = NULL;
d9572179 1308
0272dd08 1309 debugs(82, 2, HERE << "reply=" << reply);
d9572179 1310
0272dd08
AJ
1311 if (reply.result == HelperReply::Okay)
1312 entryData.result = ACCESS_ALLOWED;
1313 // XXX: handle other non-DENIED results better
62e76326 1314
fd7f26ea 1315 // XXX: make entryData store a proper HelperReply object instead of copying.
ab332e27 1316
a0592634
AJ
1317 entryData.notes.append(&reply.notes);
1318
cf9f0261
CT
1319 const char *label = reply.notes.findFirst("tag");
1320 if (label != NULL && *label != '\0')
1321 entryData.tag = label;
62e76326 1322
cf9f0261
CT
1323 label = reply.notes.findFirst("message");
1324 if (label != NULL && *label != '\0')
1325 entryData.message = label;
62e76326 1326
cf9f0261
CT
1327 label = reply.notes.findFirst("log");
1328 if (label != NULL && *label != '\0')
1329 entryData.log = label;
62e76326 1330
2f1431ea 1331#if USE_AUTH
cf9f0261
CT
1332 label = reply.notes.findFirst("user");
1333 if (label != NULL && *label != '\0')
1334 entryData.user = label;
7bbefa01 1335
cf9f0261
CT
1336 label = reply.notes.findFirst("password");
1337 if (label != NULL && *label != '\0')
1338 entryData.password = label;
2f1431ea 1339#endif
62e76326 1340
d9572179 1341 dlinkDelete(&state->list, &state->def->queue);
62e76326 1342
4960475f 1343 if (cbdataReferenceValid(state->def)) {
0272dd08
AJ
1344 // only cache OK and ERR results.
1345 if (reply.result == HelperReply::Okay || reply.result == HelperReply::Error)
1e5562e3 1346 entry = external_acl_cache_add(state->def, state->key, entryData);
62e76326 1347 else {
2ae2408b 1348 external_acl_entry *oldentry = (external_acl_entry *)hash_lookup(state->def->cache, state->key);
62e76326 1349
2ae2408b 1350 if (oldentry)
1351 external_acl_cache_delete(state->def, oldentry);
62e76326 1352 }
466090ac 1353 }
d9572179 1354
1355 do {
62e76326 1356 void *cbdata;
1357 cbdataReferenceDone(state->def);
1358
47b0c1fa 1359 if (state->callback && cbdataReferenceValidDone(state->callback_data, &cbdata))
62e76326 1360 state->callback(cbdata, entry);
1361
1362 next = state->queue;
d9572179 1363
62e76326 1364 cbdataFree(state);
d9572179 1365
62e76326 1366 state = next;
d9572179 1367 } while (state);
1368}
1369
1370void
e0f7153c 1371ACLExternal::ExternalAclLookup(ACLChecklist *checklist, ACLExternal * me)
d9572179 1372{
e0f7153c
AR
1373 ExternalACLLookup::Start(checklist, me->data, false);
1374}
c8e7608c 1375
e0f7153c
AR
1376void
1377ExternalACLLookup::Start(ACLChecklist *checklist, external_acl_data *acl, bool inBackground)
1378{
1379 external_acl *def = acl->def;
c0f81932 1380
c0941a6a 1381 ACLFilledChecklist *ch = Filled(checklist);
c8e7608c 1382 const char *key = makeExternalAclKey(ch, acl);
6f58d7d7 1383 assert(key); // XXX: will fail if EXT_ACL_IDENT case needs an async lookup
62e76326 1384
e0f7153c
AR
1385 debugs(82, 2, HERE << (inBackground ? "bg" : "fg") << " lookup in '" <<
1386 def->name << "' for '" << key << "'");
62e76326 1387
47b0c1fa 1388 /* Check for a pending lookup to hook into */
c69ce640 1389 // only possible if we are caching results.
e0f7153c 1390 externalAclState *oldstate = NULL;
c69ce640 1391 if (def->cache_size > 0) {
e0f7153c 1392 for (dlink_node *node = def->queue.head; node; node = node->next) {
c69ce640 1393 externalAclState *oldstatetmp = static_cast<externalAclState *>(node->data);
62e76326 1394
c69ce640
AJ
1395 if (strcmp(key, oldstatetmp->key) == 0) {
1396 oldstate = oldstatetmp;
1397 break;
1398 }
47b0c1fa 1399 }
1400 }
62e76326 1401
e0f7153c
AR
1402 // A background refresh has no need to piggiback on a pending request:
1403 // When the pending request completes, the cache will be refreshed anyway.
1404 if (oldstate && inBackground) {
1405 debugs(82, 7, HERE << "'" << def->name << "' queue is already being refreshed (ch=" << ch << ")");
47b0c1fa 1406 return;
1407 }
62e76326 1408
e0f7153c 1409 externalAclState *state = cbdataAlloc(externalAclState);
47b0c1fa 1410 state->def = cbdataReference(def);
62e76326 1411
47b0c1fa 1412 state->key = xstrdup(key);
62e76326 1413
e0f7153c
AR
1414 if (!inBackground) {
1415 state->callback = &ExternalACLLookup::LookupDone;
1416 state->callback_data = cbdataReference(checklist);
d9572179 1417 }
62e76326 1418
47b0c1fa 1419 if (oldstate) {
1420 /* Hook into pending lookup */
1421 state->queue = oldstate->queue;
1422 oldstate->queue = state;
1423 } else {
e0f7153c
AR
1424 /* No pending lookup found. Sumbit to helper */
1425
47b0c1fa 1426 /* Check for queue overload */
1427
48d54e4d 1428 if (def->theHelper->stats.queue_size >= (int)def->theHelper->childs.n_running) {
e0f7153c
AR
1429 debugs(82, 7, HERE << "'" << def->name << "' queue is too long");
1430 assert(inBackground); // or the caller should have checked
47b0c1fa 1431 cbdataFree(state);
47b0c1fa 1432 return;
1433 }
1434
1435 /* Send it off to the helper */
e0f7153c 1436 MemBuf buf;
2fe7eff9 1437 buf.init();
62e76326 1438
2fe7eff9 1439 buf.Printf("%s\n", key);
62e76326 1440
bf8fe701 1441 debugs(82, 4, "externalAclLookup: looking up for '" << key << "' in '" << def->name << "'.");
ab321f4b 1442
47b0c1fa 1443 helperSubmit(def->theHelper, buf.buf, externalAclHandleReply, state);
62e76326 1444
47b0c1fa 1445 dlinkAdd(state, &state->list, &def->queue);
62e76326 1446
2fe7eff9 1447 buf.clean();
47b0c1fa 1448 }
62e76326 1449
bf8fe701 1450 debugs(82, 4, "externalAclLookup: will wait for the result of '" << key <<
1451 "' in '" << def->name << "' (ch=" << ch << ").");
d9572179 1452}
1453
1454static void
1455externalAclStats(StoreEntry * sentry)
1456{
1457 external_acl *p;
1458
1459 for (p = Config.externalAclHelperList; p; p = p->next) {
62e76326 1460 storeAppendPrintf(sentry, "External ACL Statistics: %s\n", p->name);
1461 storeAppendPrintf(sentry, "Cache size: %d\n", p->cache->count);
1462 helperStats(sentry, p->theHelper);
1463 storeAppendPrintf(sentry, "\n");
d9572179 1464 }
1465}
1466
6b7d87bb
FC
1467static void
1468externalAclRegisterWithCacheManager(void)
1469{
8822ebee 1470 Mgr::RegisterAction("external_acl",
d9fc6862
A
1471 "External ACL stats",
1472 externalAclStats, 0, 1);
6b7d87bb
FC
1473}
1474
d9572179 1475void
1476externalAclInit(void)
1477{
1478 static int firstTimeInit = 1;
1479 external_acl *p;
1480
1481 for (p = Config.externalAclHelperList; p; p = p->next) {
62e76326 1482 if (!p->cache)
30abd221 1483 p->cache = hash_create((HASHCMP *) strcmp, hashPrime(1024), hash4);
62e76326 1484
1485 if (!p->theHelper)
48d54e4d 1486 p->theHelper = new helper(p->name);
62e76326 1487
1488 p->theHelper->cmdline = p->cmdline;
1489
1af735c7 1490 p->theHelper->childs.updateLimits(p->children);
07eca7e0 1491
62e76326 1492 p->theHelper->ipc_type = IPC_TCP_SOCKET;
1493
cc192b50 1494 p->theHelper->addr = p->local_addr;
1495
62e76326 1496 helperOpenServers(p->theHelper);
d9572179 1497 }
62e76326 1498
d9572179 1499 if (firstTimeInit) {
62e76326 1500 firstTimeInit = 0;
62e76326 1501 CBDATA_INIT_TYPE_FREECB(externalAclState, free_externalAclState);
d9572179 1502 }
ea391f18
FC
1503
1504 externalAclRegisterWithCacheManager();
d9572179 1505}
1506
1507void
1508externalAclShutdown(void)
1509{
1510 external_acl *p;
62e76326 1511
d9572179 1512 for (p = Config.externalAclHelperList; p; p = p->next) {
62e76326 1513 helperShutdown(p->theHelper);
d9572179 1514 }
1515}
225b7b10 1516
1517ExternalACLLookup ExternalACLLookup::instance_;
1518ExternalACLLookup *
1519ExternalACLLookup::Instance()
1520{
1521 return &instance_;
1522}
1523
1524void
1525ExternalACLLookup::checkForAsync(ACLChecklist *checklist)const
1526{
b0dd28ba 1527 /* TODO: optimise this - we probably have a pointer to this
1528 * around somewhere */
97427e90 1529 ACL *acl = ACL::FindByName(AclMatchedName);
ab321f4b 1530 assert(acl);
b0dd28ba 1531 ACLExternal *me = dynamic_cast<ACLExternal *> (acl);
1532 assert (me);
e0f7153c 1533 ACLExternal::ExternalAclLookup(checklist, me);
225b7b10 1534}
1535
e0f7153c 1536/// Called when an async lookup returns
225b7b10 1537void
1538ExternalACLLookup::LookupDone(void *data, void *result)
1539{
c0941a6a 1540 ACLFilledChecklist *checklist = Filled(static_cast<ACLChecklist*>(data));
225b7b10 1541 checklist->extacl_entry = cbdataReference((external_acl_entry *)result);
a0592634
AJ
1542
1543 // attach the helper kv-pair to the transaction
243743d8
AR
1544 if (checklist->extacl_entry) {
1545 if (HttpRequest * req = checklist->request) {
1546 // XXX: we have no access to the transaction / AccessLogEntry so cant SyncNotes().
1547 // workaround by using anything already set in HttpRequest
1548 // OR use new and rely on a later Sync copying these to AccessLogEntry
1549 if (!req->notes)
1550 req->notes = new NotePairs;
1551
1552 req->notes->appendNewOnly(&checklist->extacl_entry->notes);
1553 }
a0592634
AJ
1554 }
1555
6f58d7d7 1556 checklist->resumeNonBlockingCheck(ExternalACLLookup::Instance());
225b7b10 1557}
b0dd28ba 1558
1559/* This registers "external" in the registry. To do dynamic definitions
1560 * of external ACL's, rather than a static prototype, have a Prototype instance
1561 * prototype in the class that defines each external acl 'class'.
1562 * Then, then the external acl instance is created, it self registers under
1563 * it's name.
1564 * Be sure that clone is fully functional for that acl class though!
1565 */
1566ACL::Prototype ACLExternal::RegistryProtoype(&ACLExternal::RegistryEntry_, "external");
1567
1568ACLExternal ACLExternal::RegistryEntry_("external");
1569
1570ACL *
1571ACLExternal::clone() const
1572{
1573 return new ACLExternal(*this);
1574}
1575
86c63190 1576ACLExternal::ACLExternal(char const *theClass) : data(NULL), class_(xstrdup(theClass))
b0dd28ba 1577{}
1578
86c63190 1579ACLExternal::ACLExternal(ACLExternal const & old) : data(NULL), class_(old.class_ ? xstrdup(old.class_) : NULL)
b0dd28ba 1580{
1581 /* we don't have copy constructors for the data yet */
86c63190 1582 assert(!old.data);
b0dd28ba 1583}
1584
b0dd28ba 1585char const *
1586ACLExternal::typeString() const
1587{
1588 return class_;
1589}
1590
e870b1f8 1591bool
1592ACLExternal::isProxyAuth() const
1593{
2f1431ea 1594#if USE_AUTH
e870b1f8 1595 return data->def->require_auth;
2f1431ea
AJ
1596#else
1597 return false;
1598#endif
e870b1f8 1599}