]> git.ipfire.org Git - thirdparty/squid.git/blame - src/external_acl.cc
Cleanup: use new/delete allocation from netdbEntry class
[thirdparty/squid.git] / src / external_acl.cc
CommitLineData
d9572179 1/*
ef57eb7b 2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
d9572179 3 *
bbc27441
AJ
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
d9572179 7 */
8
bbc27441
AJ
9/* DEBUG: section 82 External ACL */
10
582c2af2 11#include "squid.h"
c0941a6a 12#include "acl/Acl.h"
582c2af2 13#include "acl/FilledChecklist.h"
8a01b99e 14#include "cache_cf.h"
a46d2c0e 15#include "client_side.h"
4e56d7f6 16#include "client_side_request.h"
5c336a3b 17#include "comm/Connection.h"
2eceb328 18#include "ConfigParser.h"
582c2af2 19#include "ExternalACL.h"
f9b6ff6e 20#include "ExternalACLEntry.h"
582c2af2 21#include "fde.h"
4e56d7f6 22#include "format/Token.h"
aa839030 23#include "helper.h"
24438ec5 24#include "helper/Reply.h"
d3dddfb5 25#include "http/Stream.h"
a5bac1d2 26#include "HttpHeaderTools.h"
582c2af2
FC
27#include "HttpReply.h"
28#include "HttpRequest.h"
29#include "ip/tools.h"
0eb49b6d 30#include "MemBuf.h"
582c2af2 31#include "mgr/Registration.h"
1fa9b1a7 32#include "rfc1738.h"
4d5904f7 33#include "SquidConfig.h"
f9b6ff6e 34#include "SquidString.h"
582c2af2
FC
35#include "SquidTime.h"
36#include "Store.h"
4e540555 37#include "tools.h"
b1bd952a 38#include "URL.h"
d295d770 39#include "wordlist.h"
cb4f4424 40#if USE_OPENSSL
cedca6e7 41#include "ssl/ServerBump.h"
4db984be
CT
42#include "ssl/support.h"
43#endif
582c2af2
FC
44#if USE_AUTH
45#include "auth/Acl.h"
46#include "auth/Gadgets.h"
47#include "auth/UserRequest.h"
48#endif
49#if USE_IDENT
50#include "ident/AclIdent.h"
51#endif
d9572179 52
53#ifndef DEFAULT_EXTERNAL_ACL_TTL
54#define DEFAULT_EXTERNAL_ACL_TTL 1 * 60 * 60
55#endif
d4f2f353 56#ifndef DEFAULT_EXTERNAL_ACL_CHILDREN
57#define DEFAULT_EXTERNAL_ACL_CHILDREN 5
d9572179 58#endif
59
c0941a6a 60static char *makeExternalAclKey(ACLFilledChecklist * ch, external_acl_data * acl_data);
abdd93d0
AJ
61static void external_acl_cache_delete(external_acl * def, const ExternalACLEntryPointer &entry);
62static int external_acl_entry_expired(external_acl * def, const ExternalACLEntryPointer &entry);
63static int external_acl_grace_expired(external_acl * def, const ExternalACLEntryPointer &entry);
64static void external_acl_cache_touch(external_acl * def, const ExternalACLEntryPointer &entry);
65static ExternalACLEntryPointer external_acl_cache_add(external_acl * def, const char *key, ExternalACLEntryData const &data);
d9572179 66
67/******************************************************************
68 * external_acl directive
69 */
62e76326 70
1e5562e3 71class external_acl
62e76326 72{
f205a92e
SM
73 /* FIXME: These are not really cbdata, but it is an easy way
74 * to get them pooled, refcounted, accounted and freed properly...
75 */
f963b531 76 CBDATA_CLASS(external_acl);
1e5562e3 77
78public:
f963b531
AJ
79 external_acl();
80 ~external_acl();
81
d9572179 82 external_acl *next;
1e5562e3 83
abdd93d0 84 void add(const ExternalACLEntryPointer &);
1e5562e3 85
86 void trimCache();
87
d9572179 88 int ttl;
1e5562e3 89
d9572179 90 int negative_ttl;
1e5562e3 91
47b0c1fa 92 int grace;
93
d9572179 94 char *name;
1e5562e3 95
4e56d7f6 96 Format::Format format;
1e5562e3 97
d9572179 98 wordlist *cmdline;
1e5562e3 99
76d9b994 100 Helper::ChildConfig children;
07eca7e0 101
e6ccf245 102 helper *theHelper;
1e5562e3 103
d9572179 104 hash_table *cache;
1e5562e3 105
d9572179 106 dlink_list lru_list;
1e5562e3 107
d9572179 108 int cache_size;
1e5562e3 109
d9572179 110 int cache_entries;
1e5562e3 111
d9572179 112 dlink_list queue;
1e5562e3 113
2f1431ea 114#if USE_AUTH
3265364b
AJ
115 /**
116 * Configuration flag. May only be altered by the configuration parser.
117 *
118 * Indicates that all uses of this external_acl_type helper require authentication
119 * details to be processed. If none are available its a fail match.
120 */
e870b1f8 121 bool require_auth;
2f1431ea 122#endif
dc1af3cf 123
95d78f10 124 Format::Quoting quote; // default quoting to use, set by protocol= parameter
cc192b50 125
b7ac5457 126 Ip::Address local_addr;
d9572179 127};
128
f963b531
AJ
129CBDATA_CLASS_INIT(external_acl);
130
131external_acl::external_acl() :
cc8c4af2 132 next(NULL),
f963b531
AJ
133 ttl(DEFAULT_EXTERNAL_ACL_TTL),
134 negative_ttl(-1),
135 grace(1),
136 name(NULL),
4e56d7f6 137 format("external_acl_type"),
f963b531
AJ
138 cmdline(NULL),
139 children(DEFAULT_EXTERNAL_ACL_CHILDREN),
140 theHelper(NULL),
141 cache(NULL),
142 cache_size(256*1024),
143 cache_entries(0),
144#if USE_AUTH
145 require_auth(0),
146#endif
95d78f10 147 quote(Format::LOG_QUOTE_URL)
d9572179 148{
f963b531
AJ
149 local_addr.setLocalhost();
150}
62e76326 151
f963b531
AJ
152external_acl::~external_acl()
153{
154 xfree(name);
f963b531
AJ
155 wordlistDestroy(&cmdline);
156
157 if (theHelper) {
158 helperShutdown(theHelper);
159 delete theHelper;
160 theHelper = NULL;
d9572179 161 }
62e76326 162
f963b531
AJ
163 while (lru_list.tail) {
164 ExternalACLEntryPointer e(static_cast<ExternalACLEntry *>(lru_list.tail->data));
165 external_acl_cache_delete(this, e);
166 }
167 if (cache)
168 hashFreeMemory(cache);
169
170 while (next) {
171 external_acl *node = next;
172 next = node->next;
173 node->next = NULL; // prevent recursion
174 delete node;
abdd93d0 175 }
d9572179 176}
177
178void
179parse_externalAclHelper(external_acl ** list)
180{
f963b531 181 char *token = ConfigParser::NextToken();
62e76326 182
7b6ce1c0 183 if (!token) {
62e76326 184 self_destruct();
7b6ce1c0
AJ
185 return;
186 }
62e76326 187
7b6ce1c0 188 external_acl *a = new external_acl;
d9572179 189 a->name = xstrdup(token);
190
2eceb328
CT
191 // Allow supported %macros inside quoted tokens
192 ConfigParser::EnableMacros();
193 token = ConfigParser::NextToken();
62e76326 194
d9572179 195 /* Parse options */
196 while (token) {
62e76326 197 if (strncmp(token, "ttl=", 4) == 0) {
198 a->ttl = atoi(token + 4);
199 } else if (strncmp(token, "negative_ttl=", 13) == 0) {
200 a->negative_ttl = atoi(token + 13);
07eca7e0 201 } else if (strncmp(token, "children=", 9) == 0) {
48d54e4d 202 a->children.n_max = atoi(token + 9);
fa84c01d 203 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
204 } else if (strncmp(token, "children-max=", 13) == 0) {
205 a->children.n_max = atoi(token + 13);
206 } else if (strncmp(token, "children-startup=", 17) == 0) {
207 a->children.n_startup = atoi(token + 17);
208 } else if (strncmp(token, "children-idle=", 14) == 0) {
209 a->children.n_idle = atoi(token + 14);
07eca7e0 210 } else if (strncmp(token, "concurrency=", 12) == 0) {
48d54e4d 211 a->children.concurrency = atoi(token + 12);
6825b101
CT
212 } else if (strncmp(token, "queue-size=", 11) == 0) {
213 a->children.queue_size = atoi(token + 11);
214 a->children.defaultQueueSize = false;
62e76326 215 } else if (strncmp(token, "cache=", 6) == 0) {
216 a->cache_size = atoi(token + 6);
47b0c1fa 217 } else if (strncmp(token, "grace=", 6) == 0) {
218 a->grace = atoi(token + 6);
dc1af3cf 219 } else if (strcmp(token, "protocol=2.5") == 0) {
95d78f10 220 a->quote = Format::LOG_QUOTE_SHELL;
dc1af3cf 221 } else if (strcmp(token, "protocol=3.0") == 0) {
05e52854 222 debugs(3, DBG_PARSE_NOTE(2), "WARNING: external_acl_type option protocol=3.0 is deprecated. Remove this from your config.");
95d78f10 223 a->quote = Format::LOG_QUOTE_URL;
dc1af3cf 224 } else if (strcmp(token, "quote=url") == 0) {
05e52854 225 debugs(3, DBG_PARSE_NOTE(2), "WARNING: external_acl_type option quote=url is deprecated. Remove this from your config.");
95d78f10 226 a->quote = Format::LOG_QUOTE_URL;
dc1af3cf 227 } else if (strcmp(token, "quote=shell") == 0) {
05e52854 228 debugs(3, DBG_PARSE_NOTE(2), "WARNING: external_acl_type option quote=shell is deprecated. Use protocol=2.5 if still needed.");
95d78f10 229 a->quote = Format::LOG_QUOTE_SHELL;
cc192b50 230
26ac0430
AJ
231 /* INET6: allow admin to configure some helpers explicitly to
232 bind to IPv4/v6 localhost port. */
cc192b50 233 } else if (strcmp(token, "ipv4") == 0) {
4dd643d5 234 if ( !a->local_addr.setIPv4() ) {
fa84c01d 235 debugs(3, DBG_CRITICAL, "WARNING: Error converting " << a->local_addr << " to IPv4 in " << a->name );
cc192b50 236 }
237 } else if (strcmp(token, "ipv6") == 0) {
055421ee 238 if (!Ip::EnableIpv6)
fa84c01d 239 debugs(3, DBG_CRITICAL, "WARNING: --enable-ipv6 required for external ACL helpers to use IPv6: " << a->name );
055421ee 240 // else nothing to do.
62e76326 241 } else {
242 break;
243 }
244
2eceb328 245 token = ConfigParser::NextToken();
d9572179 246 }
2eceb328 247 ConfigParser::DisableMacros();
62e76326 248
6f78f0ed 249 /* check that child startup value is sane. */
a3cecd6c
AJ
250 if (a->children.n_startup > a->children.n_max)
251 a->children.n_startup = a->children.n_max;
48d54e4d 252
6f78f0ed 253 /* check that child idle value is sane. */
a3cecd6c
AJ
254 if (a->children.n_idle > a->children.n_max)
255 a->children.n_idle = a->children.n_max;
256 if (a->children.n_idle < 1)
257 a->children.n_idle = 1;
48d54e4d 258
d9572179 259 if (a->negative_ttl == -1)
62e76326 260 a->negative_ttl = a->ttl;
d9572179 261
e44a3ec4 262 if (a->children.defaultQueueSize)
6825b101
CT
263 a->children.queue_size = 2 * a->children.n_max;
264
4e56d7f6
AJ
265 /* Legacy external_acl_type format parser.
266 * Handles a series of %... tokens where any non-% means
267 * the start of another parameter field (ie the path to binary).
268 */
269 enum Format::Quoting quote = Format::LOG_QUOTE_NONE;
270 Format::Token **fmt = &a->format.format;
712fa21e 271 bool data_used = false;
d9572179 272 while (token) {
4e56d7f6 273 /* stop on first non-% token found */
62e76326 274 if (*token != '%')
275 break;
276
4e56d7f6 277 *fmt = new Format::Token;
09e34608
AJ
278 // these tokens are whitespace delimited
279 (*fmt)->space = true;
4e56d7f6 280
3a80bdd7 281 // set the default encoding to match the protocol= config
95d78f10
AJ
282 // this will be overridden by explicit %macro attributes
283 (*fmt)->quote = a->quote;
284
4e56d7f6
AJ
285 // compatibility for old tokens incompatible with Format::Token syntax
286#if USE_OPENSSL // dont bother if we dont have to.
287 if (strncmp(token, "%USER_CERT_", 11) == 0) {
288 (*fmt)->type = Format::LFT_EXT_ACL_USER_CERT;
289 (*fmt)->data.string = xstrdup(token + 11);
290 (*fmt)->data.header.header = (*fmt)->data.string;
f06585e0 291 } else if (strncmp(token, "%USER_CA_CERT_", 14) == 0) {
4e56d7f6
AJ
292 (*fmt)->type = Format::LFT_EXT_ACL_USER_CA_CERT;
293 (*fmt)->data.string = xstrdup(token + 14);
294 (*fmt)->data.header.header = (*fmt)->data.string;
f06585e0 295 } else if (strncmp(token, "%CA_CERT_", 9) == 0) {
3650c90e 296 debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: external_acl_type %CA_CERT_* code is obsolete. Use %USER_CA_CERT_* instead");
4e56d7f6
AJ
297 (*fmt)->type = Format::LFT_EXT_ACL_USER_CA_CERT;
298 (*fmt)->data.string = xstrdup(token + 9);
299 (*fmt)->data.header.header = (*fmt)->data.string;
300 } else
a7ad6e4e 301#endif
4e56d7f6
AJ
302 {
303 // we can use the Format::Token::parse() method since it
304 // only pulls off one token. Since we already checked
305 // for '%' prefix above this is guaranteed to be a token.
306 const size_t len = (*fmt)->parse(token, &quote);
307 assert(len == strlen(token));
308 }
309
310 // process special token-specific actions (only if necessary)
2f1431ea 311#if USE_AUTH
4e56d7f6
AJ
312 if ((*fmt)->type == Format::LFT_USER_LOGIN)
313 a->require_auth = true;
2f1431ea 314#endif
62e76326 315
95d78f10
AJ
316 if ((*fmt)->type == Format::LFT_EXT_ACL_DATA)
317 data_used = true;
712fa21e 318
4e56d7f6 319 fmt = &((*fmt)->next);
2eceb328 320 token = ConfigParser::NextToken();
d9572179 321 }
322
323 /* There must be at least one format token */
7b6ce1c0
AJ
324 if (!a->format.format) {
325 delete a;
62e76326 326 self_destruct();
7b6ce1c0
AJ
327 return;
328 }
d9572179 329
712fa21e
AJ
330 // format has implicit %DATA on the end if not used explicitly
331 if (!data_used) {
332 *fmt = new Format::Token;
333 (*fmt)->type = Format::LFT_EXT_ACL_DATA;
262eaf9a 334 (*fmt)->quote = Format::LOG_QUOTE_NONE;
712fa21e
AJ
335 }
336
d9572179 337 /* helper */
7b6ce1c0
AJ
338 if (!token) {
339 delete a;
62e76326 340 self_destruct();
7b6ce1c0
AJ
341 return;
342 }
62e76326 343
d9572179 344 wordlistAdd(&a->cmdline, token);
345
346 /* arguments */
a336d130 347 parse_wordlist(&a->cmdline);
d9572179 348
349 while (*list)
62e76326 350 list = &(*list)->next;
351
d9572179 352 *list = a;
353}
354
355void
356dump_externalAclHelper(StoreEntry * sentry, const char *name, const external_acl * list)
357{
358 const external_acl *node;
d9572179 359 const wordlist *word;
62e76326 360
d9572179 361 for (node = list; node; node = node->next) {
62e76326 362 storeAppendPrintf(sentry, "%s %s", name, node->name);
363
4dd643d5 364 if (!node->local_addr.isIPv6())
cc192b50 365 storeAppendPrintf(sentry, " ipv4");
366 else
367 storeAppendPrintf(sentry, " ipv6");
368
62e76326 369 if (node->ttl != DEFAULT_EXTERNAL_ACL_TTL)
370 storeAppendPrintf(sentry, " ttl=%d", node->ttl);
371
372 if (node->negative_ttl != node->ttl)
373 storeAppendPrintf(sentry, " negative_ttl=%d", node->negative_ttl);
374
47b0c1fa 375 if (node->grace)
376 storeAppendPrintf(sentry, " grace=%d", node->grace);
377
48d54e4d
AJ
378 if (node->children.n_max != DEFAULT_EXTERNAL_ACL_CHILDREN)
379 storeAppendPrintf(sentry, " children-max=%d", node->children.n_max);
62e76326 380
48d54e4d
AJ
381 if (node->children.n_startup != 1)
382 storeAppendPrintf(sentry, " children-startup=%d", node->children.n_startup);
62e76326 383
48d54e4d
AJ
384 if (node->children.n_idle != (node->children.n_max + node->children.n_startup) )
385 storeAppendPrintf(sentry, " children-idle=%d", node->children.n_idle);
386
387 if (node->children.concurrency)
388 storeAppendPrintf(sentry, " concurrency=%d", node->children.concurrency);
07eca7e0 389
47b0c1fa 390 if (node->cache)
391 storeAppendPrintf(sentry, " cache=%d", node->cache_size);
392
95d78f10 393 if (node->quote == Format::LOG_QUOTE_SHELL)
05e52854
AJ
394 storeAppendPrintf(sentry, " protocol=2.5");
395
4e56d7f6 396 node->format.dump(sentry, NULL, false);
62e76326 397
398 for (word = node->cmdline; word; word = word->next)
399 storeAppendPrintf(sentry, " %s", word->key);
400
401 storeAppendPrintf(sentry, "\n");
d9572179 402 }
403}
404
405void
406free_externalAclHelper(external_acl ** list)
407{
f963b531
AJ
408 delete *list;
409 *list = NULL;
d9572179 410}
411
412static external_acl *
413find_externalAclHelper(const char *name)
414{
415 external_acl *node;
416
417 for (node = Config.externalAclHelperList; node; node = node->next) {
62e76326 418 if (strcmp(node->name, name) == 0)
419 return node;
d9572179 420 }
62e76326 421
d9572179 422 return NULL;
423}
424
1e5562e3 425void
abdd93d0 426external_acl::add(const ExternalACLEntryPointer &anEntry)
1e5562e3 427{
428 trimCache();
abdd93d0 429 assert(anEntry != NULL);
1e5562e3 430 assert (anEntry->def == NULL);
431 anEntry->def = this;
abdd93d0
AJ
432 ExternalACLEntry *e = const_cast<ExternalACLEntry *>(anEntry.getRaw()); // XXX: make hash a std::map of Pointer.
433 hash_join(cache, e);
434 dlinkAdd(e, &e->lru, &lru_list);
435 e->lock(); //cbdataReference(e); // lock it on behalf of the hash
95dc7ff4 436 ++cache_entries;
1e5562e3 437}
438
439void
440external_acl::trimCache()
441{
abdd93d0
AJ
442 if (cache_size && cache_entries >= cache_size) {
443 ExternalACLEntryPointer e(static_cast<ExternalACLEntry *>(lru_list.tail->data));
444 external_acl_cache_delete(this, e);
445 }
1e5562e3 446}
447
d9572179 448/******************************************************************
449 * external acl type
450 */
451
f963b531
AJ
452class external_acl_data
453{
454 CBDATA_CLASS(external_acl_data);
455
456public:
457 explicit external_acl_data(external_acl *aDef) : def(cbdataReference(aDef)), name(NULL), arguments(NULL) {}
458 ~external_acl_data();
459
d9572179 460 external_acl *def;
ec2d5242 461 const char *name;
d9572179 462 wordlist *arguments;
463};
464
f963b531
AJ
465CBDATA_CLASS_INIT(external_acl_data);
466
467external_acl_data::~external_acl_data()
d9572179 468{
f963b531
AJ
469 xfree(name);
470 wordlistDestroy(&arguments);
471 cbdataReferenceDone(def);
d9572179 472}
473
474void
b0dd28ba 475ACLExternal::parse()
d9572179 476{
7b6ce1c0 477 if (data) {
62e76326 478 self_destruct();
7b6ce1c0
AJ
479 return;
480 }
62e76326 481
16c5ad96 482 char *token = ConfigParser::strtokFile();
62e76326 483
7b6ce1c0 484 if (!token) {
62e76326 485 self_destruct();
7b6ce1c0
AJ
486 return;
487 }
62e76326 488
f963b531 489 data = new external_acl_data(find_externalAclHelper(token));
62e76326 490
7b6ce1c0
AJ
491 if (!data->def) {
492 delete data;
62e76326 493 self_destruct();
7b6ce1c0
AJ
494 return;
495 }
62e76326 496
ec2d5242
HN
497 // def->name is the name of the external_acl_type.
498 // this is the name of the 'acl' directive being tested
499 data->name = xstrdup(AclMatchedName);
500
16c5ad96 501 while ((token = ConfigParser::strtokFile())) {
62e76326 502 wordlistAdd(&data->arguments, token);
d9572179 503 }
d9572179 504}
505
4b0f5de8 506bool
507ACLExternal::valid () const
508{
2f1431ea 509#if USE_AUTH
4b0f5de8 510 if (data->def->require_auth) {
511 if (authenticateSchemeCount() == 0) {
fa84c01d 512 debugs(28, DBG_CRITICAL, "Can't use proxy auth because no authentication schemes were compiled.");
4b0f5de8 513 return false;
514 }
515
516 if (authenticateActiveSchemeCount() == 0) {
fa84c01d 517 debugs(28, DBG_CRITICAL, "Can't use proxy auth because no authentication schemes are fully configured.");
4b0f5de8 518 return false;
519 }
520 }
2f1431ea 521#endif
4b0f5de8 522
523 return true;
524}
525
526bool
527ACLExternal::empty () const
528{
529 return false;
530}
531
b0dd28ba 532ACLExternal::~ACLExternal()
d9572179 533{
f963b531
AJ
534 delete data;
535 xfree(class_);
d9572179 536}
537
1abe0161 538static void
abdd93d0 539copyResultsFromEntry(HttpRequest *req, const ExternalACLEntryPointer &entry)
1abe0161
AJ
540{
541 if (req) {
542#if USE_AUTH
543 if (entry->user.size())
544 req->extacl_user = entry->user;
545
546 if (entry->password.size())
547 req->extacl_passwd = entry->password;
548#endif
549 if (!req->tag.size())
550 req->tag = entry->tag;
551
552 if (entry->log.size())
553 req->extacl_log = entry->log;
554
555 if (entry->message.size())
556 req->extacl_message = entry->message;
a1df1417
NH
557
558 // attach the helper kv-pair to the transaction
559 UpdateRequestNotes(req->clientConnectionManager.get(), *req, entry->notes);
1abe0161
AJ
560 }
561}
562
e5f825ea 563static allow_t
c0941a6a 564aclMatchExternal(external_acl_data *acl, ACLFilledChecklist *ch)
d9572179 565{
c69ce640 566 debugs(82, 9, HERE << "acl=\"" << acl->def->name << "\"");
abdd93d0 567 ExternalACLEntryPointer entry = ch->extacl_entry;
62e76326 568
6f58d7d7
AR
569 external_acl_message = "MISSING REQUIRED INFORMATION";
570
abdd93d0
AJ
571 if (entry != NULL) {
572 if (entry->def == acl->def) {
ad06254e 573 /* Ours, use it.. if the key matches */
6f58d7d7
AR
574 const char *key = makeExternalAclKey(ch, acl);
575 if (!key)
576 return ACCESS_DUNNO; // insufficent data to continue
ad06254e 577 if (strcmp(key, (char*)entry->key) != 0) {
abdd93d0 578 debugs(82, 9, "entry key='" << (char *)entry->key << "', our key='" << key << "' dont match. Discarded.");
ad06254e 579 // too bad. need a new lookup.
abdd93d0 580 entry = ch->extacl_entry = NULL;
ad06254e 581 }
62e76326 582 } else {
abdd93d0
AJ
583 /* Not ours.. get rid of it */
584 debugs(82, 9, "entry " << entry << " not valid or not ours. Discarded.");
585 if (entry != NULL) {
586 debugs(82, 9, "entry def=" << entry->def << ", our def=" << acl->def);
6f58d7d7 587 const char *key = makeExternalAclKey(ch, acl); // may be nil
abdd93d0 588 debugs(82, 9, "entry key='" << (char *)entry->key << "', our key='" << key << "'");
c69ce640 589 }
abdd93d0 590 entry = ch->extacl_entry = NULL;
62e76326 591 }
d9572179 592 }
62e76326 593
d9572179 594 if (!entry) {
c69ce640 595 debugs(82, 9, HERE << "No helper entry available");
2f1431ea 596#if USE_AUTH
62e76326 597 if (acl->def->require_auth) {
62e76326 598 /* Make sure the user is authenticated */
e5f825ea 599 debugs(82, 3, HERE << acl->def->name << " check user authenticated.");
e0f7153c
AR
600 const allow_t ti = AuthenticateAcl(ch);
601 if (ti != ACCESS_ALLOWED) {
e5f825ea 602 debugs(82, 2, HERE << acl->def->name << " user not authenticated (" << ti << ")");
e0f7153c 603 return ti;
62e76326 604 }
e5f825ea 605 debugs(82, 3, HERE << acl->def->name << " user is authenticated.");
62e76326 606 }
2f1431ea 607#endif
6f58d7d7 608 const char *key = makeExternalAclKey(ch, acl);
41218131 609
610 if (!key) {
611 /* Not sufficient data to process */
e5f825ea 612 return ACCESS_DUNNO;
41218131 613 }
614
abdd93d0 615 entry = static_cast<ExternalACLEntry *>(hash_lookup(acl->def->cache, key));
62e76326 616
abdd93d0
AJ
617 const ExternalACLEntryPointer staleEntry = entry;
618 if (entry != NULL && external_acl_entry_expired(acl->def, entry))
e0f7153c
AR
619 entry = NULL;
620
abdd93d0 621 if (entry != NULL && external_acl_grace_expired(acl->def, entry)) {
e0f7153c
AR
622 // refresh in the background
623 ExternalACLLookup::Start(ch, acl, true);
624 debugs(82, 4, HERE << "no need to wait for the refresh of '" <<
625 key << "' in '" << acl->def->name << "' (ch=" << ch << ").");
626 }
627
628 if (!entry) {
e5f825ea 629 debugs(82, 2, HERE << acl->def->name << "(\"" << key << "\") = lookup needed");
95a83c22 630
6082a0e2
EB
631 // TODO: All other helpers allow temporary overload. Should not we?
632 if (!acl->def->theHelper->willOverload()) {
e5f825ea 633 debugs(82, 2, HERE << "\"" << key << "\": queueing a call.");
6f58d7d7
AR
634 if (!ch->goAsync(ExternalACLLookup::Instance()))
635 debugs(82, 2, "\"" << key << "\": no async support!");
e5f825ea 636 debugs(82, 2, HERE << "\"" << key << "\": return -1.");
e0f7153c 637 return ACCESS_DUNNO; // expired cached or simply absent entry
47b0c1fa 638 } else {
e0f7153c 639 if (!staleEntry) {
e5f825ea 640 debugs(82, DBG_IMPORTANT, "WARNING: external ACL '" << acl->def->name <<
6082a0e2 641 "' queue full. Request rejected '" << key << "'.");
4a972fa2 642 external_acl_message = "SYSTEM TOO BUSY, TRY AGAIN LATER";
e5f825ea 643 return ACCESS_DUNNO;
47b0c1fa 644 } else {
e5f825ea 645 debugs(82, DBG_IMPORTANT, "WARNING: external ACL '" << acl->def->name <<
6082a0e2 646 "' queue full. Using stale result. '" << key << "'.");
e0f7153c 647 entry = staleEntry;
47b0c1fa 648 /* Fall thru to processing below */
649 }
650 }
651 }
d9572179 652 }
62e76326 653
e0f7153c
AR
654 debugs(82, 4, HERE << "entry = { date=" <<
655 (long unsigned int) entry->date <<
656 ", result=" << entry->result <<
657 " tag=" << entry->tag <<
658 " log=" << entry->log << " }");
659#if USE_AUTH
660 debugs(82, 4, HERE << "entry user=" << entry->user);
661#endif
662
d9572179 663 external_acl_cache_touch(acl->def, entry);
a7a42b14 664 external_acl_message = entry->message.termedBuf();
4a972fa2 665
e5f825ea 666 debugs(82, 2, HERE << acl->def->name << " = " << entry->result);
1abe0161 667 copyResultsFromEntry(ch->request, entry);
e5f825ea 668 return entry->result;
d9572179 669}
670
b0dd28ba 671int
672ACLExternal::match(ACLChecklist *checklist)
673{
e5f825ea 674 allow_t answer = aclMatchExternal(data, Filled(checklist));
e5f825ea
AJ
675
676 // convert to tri-state ACL match 1,0,-1
9b831d57 677 switch (answer) {
e5f825ea 678 case ACCESS_ALLOWED:
e5f825ea
AJ
679 return 1; // match
680
681 case ACCESS_DENIED:
e5f825ea
AJ
682 return 0; // non-match
683
684 case ACCESS_DUNNO:
685 case ACCESS_AUTH_REQUIRED:
686 default:
e0f7153c 687 // If the answer is not allowed or denied (matches/not matches) and
6f58d7d7
AR
688 // async authentication is not in progress, then we are done.
689 if (checklist->keepMatching())
e0f7153c 690 checklist->markFinished(answer, "aclMatchExternal exception");
e5f825ea
AJ
691 return -1; // other
692 }
b0dd28ba 693}
694
dfad5100 695SBufList
b0dd28ba 696ACLExternal::dump() const
d9572179 697{
b0dd28ba 698 external_acl_data const *acl = data;
dfad5100
FC
699 SBufList rv;
700 rv.push_back(SBuf(acl->def->name));
62e76326 701
dfad5100
FC
702 for (wordlist *arg = acl->arguments; arg; arg = arg->next) {
703 SBuf s;
704 s.Printf(" %s", arg->key);
705 rv.push_back(s);
d9572179 706 }
62e76326 707
dfad5100 708 return rv;
d9572179 709}
710
711/******************************************************************
712 * external_acl cache
713 */
714
d9572179 715static void
abdd93d0 716external_acl_cache_touch(external_acl * def, const ExternalACLEntryPointer &entry)
d9572179 717{
c69ce640
AJ
718 // this must not be done when nothing is being cached.
719 if (def->cache_size <= 0 || (def->ttl <= 0 && entry->result == 1) || (def->negative_ttl <= 0 && entry->result != 1))
720 return;
721
d9572179 722 dlinkDelete(&entry->lru, &def->lru_list);
abdd93d0
AJ
723 ExternalACLEntry *e = const_cast<ExternalACLEntry *>(entry.getRaw()); // XXX: make hash a std::map of Pointer.
724 dlinkAdd(e, &entry->lru, &def->lru_list);
d9572179 725}
726
727static char *
c0941a6a 728makeExternalAclKey(ACLFilledChecklist * ch, external_acl_data * acl_data)
d9572179 729{
032785bf 730 static MemBuf mb;
09e34608 731 mb.reset();
62e76326 732
4e56d7f6
AJ
733 // check for special case tokens in the format
734 for (Format::Token *t = acl_data->def->format.format; t ; t = t->next) {
62e76326 735
4e56d7f6
AJ
736 if (t->type == Format::LFT_EXT_ACL_NAME) {
737 // setup for %ACL
4ff6370b
AJ
738 safe_free(ch->al->lastAclName);
739 ch->al->lastAclName = xstrdup(acl_data->name);
789dda8d
CT
740 }
741
778647be 742 if (t->type == Format::LFT_EXT_ACL_DATA) {
4e56d7f6
AJ
743 // setup string for %DATA
744 SBuf sb;
4e56d7f6 745 for (auto arg = acl_data->arguments; arg; arg = arg->next) {
778647be 746 if (sb.length())
ec2d5242
HN
747 sb.append(" ", 1);
748
95d78f10 749 if (acl_data->def->quote == Format::LOG_QUOTE_URL) {
ec2d5242
HN
750 const char *quoted = rfc1738_escape(arg->key);
751 sb.append(quoted, strlen(quoted));
752 } else {
753 static MemBuf mb2;
754 mb2.init();
755 strwordquote(&mb2, arg->key);
756 sb.append(mb2.buf, mb2.size);
757 mb2.clean();
758 }
ec2d5242 759 }
778647be 760
b0e14ce2 761 ch->al->lastAclData = sb;
dc1af3cf 762 }
62e76326 763
33c2eb4d 764#if USE_IDENT
4e56d7f6 765 if (t->type == Format::LFT_USER_IDENT) {
a51ed963 766 if (!*ch->rfc931) {
4e56d7f6
AJ
767 // if we fail to go async, we still return NULL and the caller
768 // will detect the failure in ACLExternal::match().
769 (void)ch->goAsync(IdentLookup::Instance());
770 return NULL;
ec2d5242 771 }
ec2d5242 772 }
33c2eb4d 773#endif
d9572179 774 }
62e76326 775
712fa21e 776 // assemble the full helper lookup string
4e56d7f6
AJ
777 acl_data->def->format.assemble(mb, ch->al, 0);
778
d9572179 779 return mb.buf;
d9572179 780}
781
782static int
abdd93d0 783external_acl_entry_expired(external_acl * def, const ExternalACLEntryPointer &entry)
d9572179 784{
c69ce640
AJ
785 if (def->cache_size <= 0)
786 return 1;
787
d9572179 788 if (entry->date + (entry->result == 1 ? def->ttl : def->negative_ttl) < squid_curtime)
62e76326 789 return 1;
d9572179 790 else
62e76326 791 return 0;
d9572179 792}
62e76326 793
47b0c1fa 794static int
abdd93d0 795external_acl_grace_expired(external_acl * def, const ExternalACLEntryPointer &entry)
47b0c1fa 796{
c69ce640
AJ
797 if (def->cache_size <= 0)
798 return 1;
799
47b0c1fa 800 int ttl;
801 ttl = entry->result == 1 ? def->ttl : def->negative_ttl;
802 ttl = (ttl * (100 - def->grace)) / 100;
803
98b70eff 804 if (entry->date + ttl <= squid_curtime)
47b0c1fa 805 return 1;
806 else
807 return 0;
808}
809
abdd93d0 810static ExternalACLEntryPointer
1e5562e3 811external_acl_cache_add(external_acl * def, const char *key, ExternalACLEntryData const & data)
d9572179 812{
abdd93d0 813 ExternalACLEntryPointer entry;
c69ce640
AJ
814
815 // do not bother caching this result if TTL is going to expire it immediately
816 if (def->cache_size <= 0 || (def->ttl <= 0 && data.result == 1) || (def->negative_ttl <= 0 && data.result != 1)) {
817 debugs(82,6, HERE);
818 entry = new ExternalACLEntry;
819 entry->key = xstrdup(key);
820 entry->update(data);
821 entry->def = def;
822 return entry;
823 }
824
825 entry = static_cast<ExternalACLEntry *>(hash_lookup(def->cache, key));
bf8fe701 826 debugs(82, 2, "external_acl_cache_add: Adding '" << key << "' = " << data.result);
62e76326 827
abdd93d0
AJ
828 if (entry != NULL) {
829 debugs(82, 3, "updating existing entry");
c69ce640 830 entry->update(data);
62e76326 831 external_acl_cache_touch(def, entry);
62e76326 832 return entry;
d9572179 833 }
62e76326 834
1e5562e3 835 entry = new ExternalACLEntry;
4a8b20e8 836 entry->key = xstrdup(key);
c69ce640 837 entry->update(data);
1e5562e3 838
6ca34f6f 839 def->add(entry);
1e5562e3 840
d9572179 841 return entry;
842}
843
844static void
abdd93d0 845external_acl_cache_delete(external_acl * def, const ExternalACLEntryPointer &entry)
d9572179 846{
abdd93d0 847 assert(entry != NULL);
c69ce640 848 assert(def->cache_size > 0 && entry->def == def);
abdd93d0
AJ
849 ExternalACLEntry *e = const_cast<ExternalACLEntry *>(entry.getRaw()); // XXX: make hash a std::map of Pointer.
850 hash_remove_link(def->cache, e);
851 dlinkDelete(&e->lru, &def->lru_list);
852 e->unlock(); // unlock on behalf of the hash
d9572179 853 def->cache_entries -= 1;
d9572179 854}
855
856/******************************************************************
857 * external_acl helpers
858 */
859
f963b531
AJ
860class externalAclState
861{
862 CBDATA_CLASS(externalAclState);
863
864public:
865 externalAclState(external_acl* aDef, const char *aKey) :
866 callback(NULL),
867 callback_data(NULL),
868 key(xstrdup(aKey)),
869 def(cbdataReference(aDef)),
870 queue(NULL)
871 {}
872 ~externalAclState();
62e76326 873
d9572179 874 EAH *callback;
875 void *callback_data;
876 char *key;
877 external_acl *def;
878 dlink_node list;
879 externalAclState *queue;
880};
881
f963b531
AJ
882CBDATA_CLASS_INIT(externalAclState);
883
884externalAclState::~externalAclState()
d9572179 885{
f963b531
AJ
886 xfree(key);
887 cbdataReferenceDone(callback_data);
888 cbdataReferenceDone(def);
d9572179 889}
890
d9572179 891/*
892 * The helper program receives queries on stdin, one
07eca7e0 893 * per line, and must return the result on on stdout
d9572179 894 *
895 * General result syntax:
896 *
897 * OK/ERR keyword=value ...
898 *
899 * Keywords:
900 *
4a972fa2 901 * user= The users name (login)
902 * message= Message describing the reason
f53969cc
SM
903 * tag= A string tag to be applied to the request that triggered the acl match.
904 * applies to both OK and ERR responses.
905 * Won't override existing request tags.
906 * log= A string to be used in access logging
d9572179 907 *
908 * Other keywords may be added to the protocol later
909 *
05e52854
AJ
910 * value needs to be URL-encoded or enclosed in double quotes (")
911 * with \-escaping on any whitespace, quotes, or slashes (\).
d9572179 912 */
d9572179 913static void
24438ec5 914externalAclHandleReply(void *data, const Helper::Reply &reply)
d9572179 915{
e6ccf245 916 externalAclState *state = static_cast<externalAclState *>(data);
d9572179 917 externalAclState *next;
1e5562e3 918 ExternalACLEntryData entryData;
e5f825ea 919 entryData.result = ACCESS_DENIED;
d9572179 920
0272dd08 921 debugs(82, 2, HERE << "reply=" << reply);
d9572179 922
2428ce02 923 if (reply.result == Helper::Okay)
0272dd08
AJ
924 entryData.result = ACCESS_ALLOWED;
925 // XXX: handle other non-DENIED results better
62e76326 926
24438ec5 927 // XXX: make entryData store a proper Helper::Reply object instead of copying.
ab332e27 928
a0592634
AJ
929 entryData.notes.append(&reply.notes);
930
cf9f0261
CT
931 const char *label = reply.notes.findFirst("tag");
932 if (label != NULL && *label != '\0')
933 entryData.tag = label;
62e76326 934
cf9f0261
CT
935 label = reply.notes.findFirst("message");
936 if (label != NULL && *label != '\0')
937 entryData.message = label;
62e76326 938
cf9f0261
CT
939 label = reply.notes.findFirst("log");
940 if (label != NULL && *label != '\0')
941 entryData.log = label;
62e76326 942
2f1431ea 943#if USE_AUTH
cf9f0261
CT
944 label = reply.notes.findFirst("user");
945 if (label != NULL && *label != '\0')
946 entryData.user = label;
7bbefa01 947
cf9f0261
CT
948 label = reply.notes.findFirst("password");
949 if (label != NULL && *label != '\0')
950 entryData.password = label;
2f1431ea 951#endif
62e76326 952
d9572179 953 dlinkDelete(&state->list, &state->def->queue);
62e76326 954
abdd93d0 955 ExternalACLEntryPointer entry;
4960475f 956 if (cbdataReferenceValid(state->def)) {
0272dd08 957 // only cache OK and ERR results.
2428ce02 958 if (reply.result == Helper::Okay || reply.result == Helper::Error)
1e5562e3 959 entry = external_acl_cache_add(state->def, state->key, entryData);
62e76326 960 else {
abdd93d0 961 const ExternalACLEntryPointer oldentry = static_cast<ExternalACLEntry *>(hash_lookup(state->def->cache, state->key));
62e76326 962
abdd93d0 963 if (oldentry != NULL)
2ae2408b 964 external_acl_cache_delete(state->def, oldentry);
62e76326 965 }
466090ac 966 }
d9572179 967
968 do {
62e76326 969 void *cbdata;
47b0c1fa 970 if (state->callback && cbdataReferenceValidDone(state->callback_data, &cbdata))
62e76326 971 state->callback(cbdata, entry);
972
973 next = state->queue;
f963b531 974 state->queue = NULL;
d9572179 975
f963b531 976 delete state;
d9572179 977
62e76326 978 state = next;
d9572179 979 } while (state);
980}
981
982void
e0f7153c 983ACLExternal::ExternalAclLookup(ACLChecklist *checklist, ACLExternal * me)
d9572179 984{
e0f7153c
AR
985 ExternalACLLookup::Start(checklist, me->data, false);
986}
c8e7608c 987
e0f7153c
AR
988void
989ExternalACLLookup::Start(ACLChecklist *checklist, external_acl_data *acl, bool inBackground)
990{
991 external_acl *def = acl->def;
c0f81932 992
c0941a6a 993 ACLFilledChecklist *ch = Filled(checklist);
c8e7608c 994 const char *key = makeExternalAclKey(ch, acl);
6f58d7d7 995 assert(key); // XXX: will fail if EXT_ACL_IDENT case needs an async lookup
62e76326 996
e0f7153c
AR
997 debugs(82, 2, HERE << (inBackground ? "bg" : "fg") << " lookup in '" <<
998 def->name << "' for '" << key << "'");
62e76326 999
47b0c1fa 1000 /* Check for a pending lookup to hook into */
c69ce640 1001 // only possible if we are caching results.
e0f7153c 1002 externalAclState *oldstate = NULL;
c69ce640 1003 if (def->cache_size > 0) {
e0f7153c 1004 for (dlink_node *node = def->queue.head; node; node = node->next) {
c69ce640 1005 externalAclState *oldstatetmp = static_cast<externalAclState *>(node->data);
62e76326 1006
c69ce640
AJ
1007 if (strcmp(key, oldstatetmp->key) == 0) {
1008 oldstate = oldstatetmp;
1009 break;
1010 }
47b0c1fa 1011 }
1012 }
62e76326 1013
e0f7153c
AR
1014 // A background refresh has no need to piggiback on a pending request:
1015 // When the pending request completes, the cache will be refreshed anyway.
1016 if (oldstate && inBackground) {
1017 debugs(82, 7, HERE << "'" << def->name << "' queue is already being refreshed (ch=" << ch << ")");
47b0c1fa 1018 return;
1019 }
62e76326 1020
f963b531 1021 externalAclState *state = new externalAclState(def, key);
62e76326 1022
e0f7153c
AR
1023 if (!inBackground) {
1024 state->callback = &ExternalACLLookup::LookupDone;
1025 state->callback_data = cbdataReference(checklist);
d9572179 1026 }
62e76326 1027
47b0c1fa 1028 if (oldstate) {
1029 /* Hook into pending lookup */
1030 state->queue = oldstate->queue;
1031 oldstate->queue = state;
1032 } else {
e0f7153c
AR
1033 /* No pending lookup found. Sumbit to helper */
1034
e0f7153c 1035 MemBuf buf;
2fe7eff9 1036 buf.init();
4391cd15 1037 buf.appendf("%s\n", key);
bf8fe701 1038 debugs(82, 4, "externalAclLookup: looking up for '" << key << "' in '" << def->name << "'.");
ab321f4b 1039
6825b101
CT
1040 if (!def->theHelper->trySubmit(buf.buf, externalAclHandleReply, state)) {
1041 debugs(82, 7, HERE << "'" << def->name << "' submit to helper failed");
1042 assert(inBackground); // or the caller should have checked
f963b531 1043 delete state;
6825b101
CT
1044 return;
1045 }
62e76326 1046
47b0c1fa 1047 dlinkAdd(state, &state->list, &def->queue);
62e76326 1048
2fe7eff9 1049 buf.clean();
47b0c1fa 1050 }
62e76326 1051
bf8fe701 1052 debugs(82, 4, "externalAclLookup: will wait for the result of '" << key <<
1053 "' in '" << def->name << "' (ch=" << ch << ").");
d9572179 1054}
1055
1056static void
1057externalAclStats(StoreEntry * sentry)
1058{
f963b531 1059 for (external_acl *p = Config.externalAclHelperList; p; p = p->next) {
62e76326 1060 storeAppendPrintf(sentry, "External ACL Statistics: %s\n", p->name);
1061 storeAppendPrintf(sentry, "Cache size: %d\n", p->cache->count);
bf3e8d5a
AJ
1062 assert(p->theHelper);
1063 p->theHelper->packStatsInto(sentry);
62e76326 1064 storeAppendPrintf(sentry, "\n");
d9572179 1065 }
1066}
1067
6b7d87bb
FC
1068static void
1069externalAclRegisterWithCacheManager(void)
1070{
8822ebee 1071 Mgr::RegisterAction("external_acl",
d9fc6862
A
1072 "External ACL stats",
1073 externalAclStats, 0, 1);
6b7d87bb
FC
1074}
1075
d9572179 1076void
1077externalAclInit(void)
1078{
f963b531 1079 for (external_acl *p = Config.externalAclHelperList; p; p = p->next) {
62e76326 1080 if (!p->cache)
30abd221 1081 p->cache = hash_create((HASHCMP *) strcmp, hashPrime(1024), hash4);
62e76326 1082
1083 if (!p->theHelper)
48d54e4d 1084 p->theHelper = new helper(p->name);
62e76326 1085
1086 p->theHelper->cmdline = p->cmdline;
1087
1af735c7 1088 p->theHelper->childs.updateLimits(p->children);
07eca7e0 1089
62e76326 1090 p->theHelper->ipc_type = IPC_TCP_SOCKET;
1091
cc192b50 1092 p->theHelper->addr = p->local_addr;
1093
62e76326 1094 helperOpenServers(p->theHelper);
d9572179 1095 }
62e76326 1096
ea391f18 1097 externalAclRegisterWithCacheManager();
d9572179 1098}
1099
1100void
1101externalAclShutdown(void)
1102{
1103 external_acl *p;
62e76326 1104
d9572179 1105 for (p = Config.externalAclHelperList; p; p = p->next) {
62e76326 1106 helperShutdown(p->theHelper);
d9572179 1107 }
1108}
225b7b10 1109
1110ExternalACLLookup ExternalACLLookup::instance_;
1111ExternalACLLookup *
1112ExternalACLLookup::Instance()
1113{
1114 return &instance_;
1115}
1116
1117void
1118ExternalACLLookup::checkForAsync(ACLChecklist *checklist)const
1119{
b0dd28ba 1120 /* TODO: optimise this - we probably have a pointer to this
1121 * around somewhere */
97427e90 1122 ACL *acl = ACL::FindByName(AclMatchedName);
ab321f4b 1123 assert(acl);
b0dd28ba 1124 ACLExternal *me = dynamic_cast<ACLExternal *> (acl);
1125 assert (me);
e0f7153c 1126 ACLExternal::ExternalAclLookup(checklist, me);
225b7b10 1127}
1128
e0f7153c 1129/// Called when an async lookup returns
225b7b10 1130void
abdd93d0 1131ExternalACLLookup::LookupDone(void *data, const ExternalACLEntryPointer &result)
225b7b10 1132{
c0941a6a 1133 ACLFilledChecklist *checklist = Filled(static_cast<ACLChecklist*>(data));
abdd93d0 1134 checklist->extacl_entry = result;
6f58d7d7 1135 checklist->resumeNonBlockingCheck(ExternalACLLookup::Instance());
225b7b10 1136}
b0dd28ba 1137
1138/* This registers "external" in the registry. To do dynamic definitions
1139 * of external ACL's, rather than a static prototype, have a Prototype instance
1140 * prototype in the class that defines each external acl 'class'.
1141 * Then, then the external acl instance is created, it self registers under
1142 * it's name.
1143 * Be sure that clone is fully functional for that acl class though!
1144 */
1145ACL::Prototype ACLExternal::RegistryProtoype(&ACLExternal::RegistryEntry_, "external");
1146
1147ACLExternal ACLExternal::RegistryEntry_("external");
1148
1149ACL *
1150ACLExternal::clone() const
1151{
1152 return new ACLExternal(*this);
1153}
1154
86c63190 1155ACLExternal::ACLExternal(char const *theClass) : data(NULL), class_(xstrdup(theClass))
b0dd28ba 1156{}
1157
86c63190 1158ACLExternal::ACLExternal(ACLExternal const & old) : data(NULL), class_(old.class_ ? xstrdup(old.class_) : NULL)
b0dd28ba 1159{
1160 /* we don't have copy constructors for the data yet */
86c63190 1161 assert(!old.data);
b0dd28ba 1162}
1163
b0dd28ba 1164char const *
1165ACLExternal::typeString() const
1166{
1167 return class_;
1168}
1169
e870b1f8 1170bool
1171ACLExternal::isProxyAuth() const
1172{
2f1431ea 1173#if USE_AUTH
e870b1f8 1174 return data->def->require_auth;
2f1431ea
AJ
1175#else
1176 return false;
1177#endif
e870b1f8 1178}
f53969cc 1179