2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 82 External ACL */
13 #include "acl/FilledChecklist.h"
15 #include "client_side.h"
16 #include "comm/Connection.h"
17 #include "ConfigParser.h"
18 #include "ExternalACL.h"
19 #include "ExternalACLEntry.h"
21 #include "format/ByteCode.h"
23 #include "helper/Reply.h"
24 #include "HttpHeaderTools.h"
25 #include "HttpReply.h"
26 #include "HttpRequest.h"
29 #include "mgr/Registration.h"
31 #include "SquidConfig.h"
32 #include "SquidString.h"
33 #include "SquidTime.h"
39 #include "ssl/ServerBump.h"
40 #include "ssl/support.h"
44 #include "auth/Gadgets.h"
45 #include "auth/UserRequest.h"
48 #include "ident/AclIdent.h"
51 #ifndef DEFAULT_EXTERNAL_ACL_TTL
52 #define DEFAULT_EXTERNAL_ACL_TTL 1 * 60 * 60
54 #ifndef DEFAULT_EXTERNAL_ACL_CHILDREN
55 #define DEFAULT_EXTERNAL_ACL_CHILDREN 5
58 static char *makeExternalAclKey(ACLFilledChecklist
* ch
, external_acl_data
* acl_data
);
59 static void external_acl_cache_delete(external_acl
* def
, const ExternalACLEntryPointer
&entry
);
60 static int external_acl_entry_expired(external_acl
* def
, const ExternalACLEntryPointer
&entry
);
61 static int external_acl_grace_expired(external_acl
* def
, const ExternalACLEntryPointer
&entry
);
62 static void external_acl_cache_touch(external_acl
* def
, const ExternalACLEntryPointer
&entry
);
63 static ExternalACLEntryPointer
external_acl_cache_add(external_acl
* def
, const char *key
, ExternalACLEntryData
const &data
);
65 /******************************************************************
66 * external_acl directive
69 class external_acl_format
: public RefCountable
72 typedef RefCount
<external_acl_format
> Pointer
;
73 MEMPROXY_CLASS(external_acl_format
);
75 external_acl_format() : type(Format::LFT_NONE
), header(NULL
), member(NULL
), separator(' '), header_id(HDR_BAD_HDR
) {}
76 ~external_acl_format() {
81 Format::ByteCode_t type
;
82 external_acl_format::Pointer next
;
86 http_hdr_type header_id
;
89 MEMPROXY_CLASS_INLINE(external_acl_format
);
97 void add(const ExternalACLEntryPointer
&);
109 external_acl_format::Pointer format
;
113 Helper::ChildConfig children
;
129 * Configuration flag. May only be altered by the configuration parser.
131 * Indicates that all uses of this external_acl_type helper require authentication
132 * details to be processed. If none are available its a fail match.
138 QUOTE_METHOD_SHELL
= 1,
142 Ip::Address local_addr
;
145 /* FIXME: These are not really cbdata, but it is an easy way
146 * to get them pooled, refcounted, accounted and freed properly...
148 CBDATA_TYPE(external_acl
);
151 free_external_acl(void *data
)
153 external_acl
*p
= static_cast<external_acl
*>(data
);
158 wordlistDestroy(&p
->cmdline
);
161 helperShutdown(p
->theHelper
);
166 while (p
->lru_list
.tail
) {
167 ExternalACLEntryPointer
e(static_cast<ExternalACLEntry
*>(p
->lru_list
.tail
->data
));
168 external_acl_cache_delete(p
, e
);
171 hashFreeMemory(p
->cache
);
175 * Parse the External ACL format %<{.*} and %>{.*} token(s) to pass a specific
176 * request or reply header to external helper.
178 \param header - the token being parsed (without the identifying prefix)
179 \param type - format enum identifier for this element, pulled from identifying prefix
180 \param format - structure to contain all the info about this format element.
183 parse_header_token(external_acl_format::Pointer format
, char *header
, const Format::ByteCode_t type
)
188 /** Cut away the closing brace */
189 end
= strchr(header
, '}');
190 if (end
&& strlen(end
) == 1)
195 member
= strchr(header
, ':');
198 /* Split in header and member */
202 if (!xisalnum(*member
)) {
203 format
->separator
= *member
;
206 format
->separator
= ',';
209 format
->member
= xstrdup(member
);
211 if (type
== Format::LFT_ADAPTED_REQUEST_HEADER
)
212 format
->type
= Format::LFT_ADAPTED_REQUEST_HEADER_ELEM
;
214 format
->type
= Format::LFT_REPLY_HEADER_ELEM
;
220 format
->header
= xstrdup(header
);
221 format
->header_id
= httpHeaderIdByNameDef(header
, strlen(header
));
225 parse_externalAclHelper(external_acl
** list
)
230 CBDATA_INIT_TYPE_FREECB(external_acl
, free_external_acl
);
232 a
= cbdataAlloc(external_acl
);
235 a
->ttl
= DEFAULT_EXTERNAL_ACL_TTL
;
236 a
->negative_ttl
= -1;
237 a
->cache_size
= 256*1024;
238 a
->children
.n_max
= DEFAULT_EXTERNAL_ACL_CHILDREN
;
239 a
->children
.n_startup
= a
->children
.n_max
;
240 a
->children
.n_idle
= 1;
241 a
->local_addr
.setLocalhost();
242 a
->quote
= external_acl::QUOTE_METHOD_URL
;
244 token
= ConfigParser::NextToken();
249 a
->name
= xstrdup(token
);
251 // Allow supported %macros inside quoted tokens
252 ConfigParser::EnableMacros();
253 token
= ConfigParser::NextToken();
257 if (strncmp(token
, "ttl=", 4) == 0) {
258 a
->ttl
= atoi(token
+ 4);
259 } else if (strncmp(token
, "negative_ttl=", 13) == 0) {
260 a
->negative_ttl
= atoi(token
+ 13);
261 } else if (strncmp(token
, "children=", 9) == 0) {
262 a
->children
.n_max
= atoi(token
+ 9);
263 debugs(0, DBG_CRITICAL
, "WARNING: external_acl_type option children=N has been deprecated in favor of children-max=N and children-startup=N");
264 } else if (strncmp(token
, "children-max=", 13) == 0) {
265 a
->children
.n_max
= atoi(token
+ 13);
266 } else if (strncmp(token
, "children-startup=", 17) == 0) {
267 a
->children
.n_startup
= atoi(token
+ 17);
268 } else if (strncmp(token
, "children-idle=", 14) == 0) {
269 a
->children
.n_idle
= atoi(token
+ 14);
270 } else if (strncmp(token
, "concurrency=", 12) == 0) {
271 a
->children
.concurrency
= atoi(token
+ 12);
272 } else if (strncmp(token
, "cache=", 6) == 0) {
273 a
->cache_size
= atoi(token
+ 6);
274 } else if (strncmp(token
, "grace=", 6) == 0) {
275 a
->grace
= atoi(token
+ 6);
276 } else if (strcmp(token
, "protocol=2.5") == 0) {
277 a
->quote
= external_acl::QUOTE_METHOD_SHELL
;
278 } else if (strcmp(token
, "protocol=3.0") == 0) {
279 debugs(3, DBG_PARSE_NOTE(2), "WARNING: external_acl_type option protocol=3.0 is deprecated. Remove this from your config.");
280 a
->quote
= external_acl::QUOTE_METHOD_URL
;
281 } else if (strcmp(token
, "quote=url") == 0) {
282 debugs(3, DBG_PARSE_NOTE(2), "WARNING: external_acl_type option quote=url is deprecated. Remove this from your config.");
283 a
->quote
= external_acl::QUOTE_METHOD_URL
;
284 } else if (strcmp(token
, "quote=shell") == 0) {
285 debugs(3, DBG_PARSE_NOTE(2), "WARNING: external_acl_type option quote=shell is deprecated. Use protocol=2.5 if still needed.");
286 a
->quote
= external_acl::QUOTE_METHOD_SHELL
;
288 /* INET6: allow admin to configure some helpers explicitly to
289 bind to IPv4/v6 localhost port. */
290 } else if (strcmp(token
, "ipv4") == 0) {
291 if ( !a
->local_addr
.setIPv4() ) {
292 debugs(3, DBG_CRITICAL
, "WARNING: Error converting " << a
->local_addr
<< " to IPv4 in " << a
->name
);
294 } else if (strcmp(token
, "ipv6") == 0) {
296 debugs(3, DBG_CRITICAL
, "WARNING: --enable-ipv6 required for external ACL helpers to use IPv6: " << a
->name
);
297 // else nothing to do.
302 token
= ConfigParser::NextToken();
304 ConfigParser::DisableMacros();
306 /* check that child startup value is sane. */
307 if (a
->children
.n_startup
> a
->children
.n_max
)
308 a
->children
.n_startup
= a
->children
.n_max
;
310 /* check that child idle value is sane. */
311 if (a
->children
.n_idle
> a
->children
.n_max
)
312 a
->children
.n_idle
= a
->children
.n_max
;
313 if (a
->children
.n_idle
< 1)
314 a
->children
.n_idle
= 1;
316 if (a
->negative_ttl
== -1)
317 a
->negative_ttl
= a
->ttl
;
320 external_acl_format::Pointer
*p
= &a
->format
;
323 /* stop on first non-format token found */
328 external_acl_format::Pointer format
= new external_acl_format
;
330 if (strncmp(token
, "%{", 2) == 0) {
331 // deprecated. but assume the old configs all referred to request headers.
332 debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT
), "WARNING: external_acl_type format %{...} is being replaced by %>ha{...} for : " << token
);
333 parse_header_token(format
, (token
+2), Format::LFT_ADAPTED_REQUEST_HEADER
);
334 } else if (strncmp(token
, "%>{", 3) == 0) {
335 debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT
), "WARNING: external_acl_type format %>{...} is being replaced by %>ha{...} for : " << token
);
336 parse_header_token(format
, (token
+3), Format::LFT_ADAPTED_REQUEST_HEADER
);
337 } else if (strncmp(token
, "%>ha{", 5) == 0) {
338 parse_header_token(format
, (token
+3), Format::LFT_ADAPTED_REQUEST_HEADER
);
339 } else if (strncmp(token
, "%<{", 3) == 0) {
340 debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT
), "WARNING: external_acl_type format %<{...} is being replaced by %<h{...} for : " << token
);
341 parse_header_token(format
, (token
+3), Format::LFT_REPLY_HEADER
);
342 } else if (strncmp(token
, "%<h{", 4) == 0) {
343 parse_header_token(format
, (token
+3), Format::LFT_REPLY_HEADER
);
345 } else if (strcmp(token
, "%LOGIN") == 0 || strcmp(token
, "%ul") == 0) {
346 format
->type
= Format::LFT_USER_LOGIN
;
347 a
->require_auth
= true;
351 else if (strcmp(token
, "%IDENT") == 0 || strcmp(token
, "%ui") == 0)
352 format
->type
= Format::LFT_USER_IDENT
;
354 else if (strcmp(token
, "%SRC") == 0 || strcmp(token
, "%>a") == 0)
355 format
->type
= Format::LFT_CLIENT_IP_ADDRESS
;
356 else if (strcmp(token
, "%SRCPORT") == 0 || strcmp(token
, "%>p") == 0)
357 format
->type
= Format::LFT_CLIENT_PORT
;
359 else if (strcmp(token
, "%SRCEUI48") == 0)
360 format
->type
= Format::LFT_EXT_ACL_CLIENT_EUI48
;
361 else if (strcmp(token
, "%SRCEUI64") == 0)
362 format
->type
= Format::LFT_EXT_ACL_CLIENT_EUI64
;
364 else if (strcmp(token
, "%MYADDR") == 0 || strcmp(token
, "%la") == 0)
365 format
->type
= Format::LFT_LOCAL_LISTENING_IP
;
366 else if (strcmp(token
, "%MYPORT") == 0 || strcmp(token
, "%lp") == 0)
367 format
->type
= Format::LFT_LOCAL_LISTENING_PORT
;
368 else if (strcmp(token
, "%URI") == 0 || strcmp(token
, "%>ru") == 0)
369 format
->type
= Format::LFT_CLIENT_REQ_URI
;
370 else if (strcmp(token
, "%DST") == 0 || strcmp(token
, "%>rd") == 0)
371 format
->type
= Format::LFT_CLIENT_REQ_URLDOMAIN
;
372 else if (strcmp(token
, "%PROTO") == 0 || strcmp(token
, "%>rs") == 0)
373 format
->type
= Format::LFT_CLIENT_REQ_URLSCHEME
;
374 else if (strcmp(token
, "%PORT") == 0) // XXX: add a logformat token
375 format
->type
= Format::LFT_CLIENT_REQ_URLPORT
;
376 else if (strcmp(token
, "%PATH") == 0 || strcmp(token
, "%>rp") == 0)
377 format
->type
= Format::LFT_CLIENT_REQ_URLPATH
;
378 else if (strcmp(token
, "%METHOD") == 0 || strcmp(token
, "%>rm") == 0)
379 format
->type
= Format::LFT_CLIENT_REQ_METHOD
;
381 else if (strcmp(token
, "%USER_CERT") == 0)
382 format
->type
= Format::LFT_EXT_ACL_USER_CERT_RAW
;
383 else if (strcmp(token
, "%USER_CERTCHAIN") == 0)
384 format
->type
= Format::LFT_EXT_ACL_USER_CERTCHAIN_RAW
;
385 else if (strncmp(token
, "%USER_CERT_", 11) == 0) {
386 format
->type
= Format::LFT_EXT_ACL_USER_CERT
;
387 format
->header
= xstrdup(token
+ 11);
388 } else if (strncmp(token
, "%USER_CA_CERT_", 14) == 0) {
389 format
->type
= Format::LFT_EXT_ACL_USER_CA_CERT
;
390 format
->header
= xstrdup(token
+ 14);
391 } else if (strncmp(token
, "%CA_CERT_", 9) == 0) {
392 debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT
), "WARNING: external_acl_type %CA_CERT_* code is obsolete. Use %USER_CA_CERT_* instead");
393 format
->type
= Format::LFT_EXT_ACL_USER_CA_CERT
;
394 format
->header
= xstrdup(token
+ 9);
395 } else if (strcmp(token
, "%ssl::>sni") == 0)
396 format
->type
= Format::LFT_SSL_CLIENT_SNI
;
397 else if (strcmp(token
, "%ssl::<cert_subject") == 0)
398 format
->type
= Format::LFT_SSL_SERVER_CERT_SUBJECT
;
399 else if (strcmp(token
, "%ssl::<cert_issuer") == 0)
400 format
->type
= Format::LFT_SSL_SERVER_CERT_ISSUER
;
403 else if (strcmp(token
, "%EXT_USER") == 0 || strcmp(token
, "%ue") == 0)
404 format
->type
= Format::LFT_USER_EXTERNAL
;
406 else if (strcmp(token
, "%EXT_LOG") == 0 || strcmp(token
, "%ea") == 0)
407 format
->type
= Format::LFT_EXT_LOG
;
408 else if (strcmp(token
, "%TAG") == 0 || strcmp(token
, "%et") == 0)
409 format
->type
= Format::LFT_TAG
;
410 else if (strcmp(token
, "%ACL") == 0)
411 format
->type
= Format::LFT_EXT_ACL_NAME
;
412 else if (strcmp(token
, "%DATA") == 0)
413 format
->type
= Format::LFT_EXT_ACL_DATA
;
414 else if (strcmp(token
, "%%") == 0)
415 format
->type
= Format::LFT_PERCENT
;
417 debugs(0, DBG_CRITICAL
, "ERROR: Unknown Format token " << token
);
423 token
= ConfigParser::NextToken();
426 /* There must be at least one format token */
434 wordlistAdd(&a
->cmdline
, token
);
437 parse_wordlist(&a
->cmdline
);
440 list
= &(*list
)->next
;
446 dump_externalAclHelper(StoreEntry
* sentry
, const char *name
, const external_acl
* list
)
448 const external_acl
*node
;
449 const wordlist
*word
;
451 for (node
= list
; node
; node
= node
->next
) {
452 storeAppendPrintf(sentry
, "%s %s", name
, node
->name
);
454 if (!node
->local_addr
.isIPv6())
455 storeAppendPrintf(sentry
, " ipv4");
457 storeAppendPrintf(sentry
, " ipv6");
459 if (node
->ttl
!= DEFAULT_EXTERNAL_ACL_TTL
)
460 storeAppendPrintf(sentry
, " ttl=%d", node
->ttl
);
462 if (node
->negative_ttl
!= node
->ttl
)
463 storeAppendPrintf(sentry
, " negative_ttl=%d", node
->negative_ttl
);
466 storeAppendPrintf(sentry
, " grace=%d", node
->grace
);
468 if (node
->children
.n_max
!= DEFAULT_EXTERNAL_ACL_CHILDREN
)
469 storeAppendPrintf(sentry
, " children-max=%d", node
->children
.n_max
);
471 if (node
->children
.n_startup
!= 1)
472 storeAppendPrintf(sentry
, " children-startup=%d", node
->children
.n_startup
);
474 if (node
->children
.n_idle
!= (node
->children
.n_max
+ node
->children
.n_startup
) )
475 storeAppendPrintf(sentry
, " children-idle=%d", node
->children
.n_idle
);
477 if (node
->children
.concurrency
)
478 storeAppendPrintf(sentry
, " concurrency=%d", node
->children
.concurrency
);
481 storeAppendPrintf(sentry
, " cache=%d", node
->cache_size
);
483 if (node
->quote
== external_acl::QUOTE_METHOD_SHELL
)
484 storeAppendPrintf(sentry
, " protocol=2.5");
486 for (external_acl_format::Pointer format
= node
->format
; format
!= NULL
; format
= format
->next
) {
487 switch (format
->type
) {
489 case Format::LFT_ADAPTED_REQUEST_HEADER
:
490 storeAppendPrintf(sentry
, " %%>ha{%s}", format
->header
);
493 case Format::LFT_ADAPTED_REQUEST_HEADER_ELEM
:
494 storeAppendPrintf(sentry
, " %%>ha{%s:%s}", format
->header
, format
->member
);
497 case Format::LFT_REPLY_HEADER
:
498 storeAppendPrintf(sentry
, " %%<h{%s}", format
->header
);
501 case Format::LFT_REPLY_HEADER_ELEM
:
502 storeAppendPrintf(sentry
, " %%<h{%s:%s}", format
->header
, format
->member
);
505 #define DUMP_EXT_ACL_TYPE_FMT(a, fmt, ...) \
506 case Format::LFT_##a: \
507 storeAppendPrintf(sentry, fmt, ##__VA_ARGS__); \
510 DUMP_EXT_ACL_TYPE_FMT(USER_LOGIN
," %%ul");
514 DUMP_EXT_ACL_TYPE_FMT(USER_IDENT
," %%ui");
516 DUMP_EXT_ACL_TYPE_FMT(CLIENT_IP_ADDRESS
," %%>a");
517 DUMP_EXT_ACL_TYPE_FMT(CLIENT_PORT
," %%>p");
519 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_CLIENT_EUI48
," %%SRCEUI48");
520 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_CLIENT_EUI64
," %%SRCEUI64");
522 DUMP_EXT_ACL_TYPE_FMT(LOCAL_LISTENING_IP
," %%>la");
523 DUMP_EXT_ACL_TYPE_FMT(LOCAL_LISTENING_PORT
," %%>lp");
524 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URI
," %%>ru");
525 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLDOMAIN
," %%>rd");
526 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLSCHEME
," %%>rs");
527 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLPORT
," %%>rP");
528 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLPATH
," %%>rp");
529 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_METHOD
," %%>rm");
531 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERT_RAW
, " %%USER_CERT_RAW");
532 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERTCHAIN_RAW
, " %%USER_CERTCHAIN_RAW");
533 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERT
, " %%USER_CERT_%s", format
->header
);
534 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CA_CERT
, " %%USER_CA_CERT_%s", format
->header
);
535 DUMP_EXT_ACL_TYPE_FMT(SSL_CLIENT_SNI
, "%%ssl::>sni");
536 DUMP_EXT_ACL_TYPE_FMT(SSL_SERVER_CERT_SUBJECT
, "%%ssl::<cert_subject");
537 DUMP_EXT_ACL_TYPE_FMT(SSL_SERVER_CERT_ISSUER
, "%%ssl::<cert_issuer");
540 DUMP_EXT_ACL_TYPE_FMT(USER_EXTERNAL
," %%ue");
542 DUMP_EXT_ACL_TYPE_FMT(EXT_LOG
," %%ea");
543 DUMP_EXT_ACL_TYPE_FMT(TAG
," %%et");
544 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_NAME
," %%ACL");
545 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_DATA
," %%DATA");
546 DUMP_EXT_ACL_TYPE_FMT(PERCENT
, " %%%%");
548 fatal("unknown external_acl format error");
553 for (word
= node
->cmdline
; word
; word
= word
->next
)
554 storeAppendPrintf(sentry
, " %s", word
->key
);
556 storeAppendPrintf(sentry
, "\n");
561 free_externalAclHelper(external_acl
** list
)
564 external_acl
*node
= *list
;
571 static external_acl
*
572 find_externalAclHelper(const char *name
)
576 for (node
= Config
.externalAclHelperList
; node
; node
= node
->next
) {
577 if (strcmp(node
->name
, name
) == 0)
585 external_acl::add(const ExternalACLEntryPointer
&anEntry
)
588 assert(anEntry
!= NULL
);
589 assert (anEntry
->def
== NULL
);
591 ExternalACLEntry
*e
= const_cast<ExternalACLEntry
*>(anEntry
.getRaw()); // XXX: make hash a std::map of Pointer.
593 dlinkAdd(e
, &e
->lru
, &lru_list
);
594 e
->lock(); //cbdataReference(e); // lock it on behalf of the hash
599 external_acl::trimCache()
601 if (cache_size
&& cache_entries
>= cache_size
) {
602 ExternalACLEntryPointer
e(static_cast<ExternalACLEntry
*>(lru_list
.tail
->data
));
603 external_acl_cache_delete(this, e
);
607 /******************************************************************
611 struct _external_acl_data
{
617 CBDATA_TYPE(external_acl_data
);
619 free_external_acl_data(void *data
)
621 external_acl_data
*p
= static_cast<external_acl_data
*>(data
);
623 wordlistDestroy(&p
->arguments
);
624 cbdataReferenceDone(p
->def
);
635 CBDATA_INIT_TYPE_FREECB(external_acl_data
, free_external_acl_data
);
637 data
= cbdataAlloc(external_acl_data
);
639 token
= strtokFile();
644 data
->def
= cbdataReference(find_externalAclHelper(token
));
649 // def->name is the name of the external_acl_type.
650 // this is the name of the 'acl' directive being tested
651 data
->name
= xstrdup(AclMatchedName
);
653 while ((token
= strtokFile())) {
654 wordlistAdd(&data
->arguments
, token
);
659 ACLExternal::valid () const
662 if (data
->def
->require_auth
) {
663 if (authenticateSchemeCount() == 0) {
664 debugs(28, DBG_CRITICAL
, "Can't use proxy auth because no authentication schemes were compiled.");
668 if (authenticateActiveSchemeCount() == 0) {
669 debugs(28, DBG_CRITICAL
, "Can't use proxy auth because no authentication schemes are fully configured.");
679 ACLExternal::empty () const
684 ACLExternal::~ACLExternal()
691 copyResultsFromEntry(HttpRequest
*req
, const ExternalACLEntryPointer
&entry
)
695 if (entry
->user
.size())
696 req
->extacl_user
= entry
->user
;
698 if (entry
->password
.size())
699 req
->extacl_passwd
= entry
->password
;
701 if (!req
->tag
.size())
702 req
->tag
= entry
->tag
;
704 if (entry
->log
.size())
705 req
->extacl_log
= entry
->log
;
707 if (entry
->message
.size())
708 req
->extacl_message
= entry
->message
;
713 aclMatchExternal(external_acl_data
*acl
, ACLFilledChecklist
*ch
)
715 debugs(82, 9, HERE
<< "acl=\"" << acl
->def
->name
<< "\"");
716 ExternalACLEntryPointer entry
= ch
->extacl_entry
;
718 external_acl_message
= "MISSING REQUIRED INFORMATION";
721 if (entry
->def
== acl
->def
) {
722 /* Ours, use it.. if the key matches */
723 const char *key
= makeExternalAclKey(ch
, acl
);
725 return ACCESS_DUNNO
; // insufficent data to continue
726 if (strcmp(key
, (char*)entry
->key
) != 0) {
727 debugs(82, 9, "entry key='" << (char *)entry
->key
<< "', our key='" << key
<< "' dont match. Discarded.");
728 // too bad. need a new lookup.
729 entry
= ch
->extacl_entry
= NULL
;
732 /* Not ours.. get rid of it */
733 debugs(82, 9, "entry " << entry
<< " not valid or not ours. Discarded.");
735 debugs(82, 9, "entry def=" << entry
->def
<< ", our def=" << acl
->def
);
736 const char *key
= makeExternalAclKey(ch
, acl
); // may be nil
737 debugs(82, 9, "entry key='" << (char *)entry
->key
<< "', our key='" << key
<< "'");
739 entry
= ch
->extacl_entry
= NULL
;
744 debugs(82, 9, HERE
<< "No helper entry available");
746 if (acl
->def
->require_auth
) {
747 /* Make sure the user is authenticated */
748 debugs(82, 3, HERE
<< acl
->def
->name
<< " check user authenticated.");
749 const allow_t ti
= AuthenticateAcl(ch
);
750 if (ti
!= ACCESS_ALLOWED
) {
751 debugs(82, 2, HERE
<< acl
->def
->name
<< " user not authenticated (" << ti
<< ")");
754 debugs(82, 3, HERE
<< acl
->def
->name
<< " user is authenticated.");
757 const char *key
= makeExternalAclKey(ch
, acl
);
760 /* Not sufficient data to process */
764 entry
= static_cast<ExternalACLEntry
*>(hash_lookup(acl
->def
->cache
, key
));
766 const ExternalACLEntryPointer staleEntry
= entry
;
767 if (entry
!= NULL
&& external_acl_entry_expired(acl
->def
, entry
))
770 if (entry
!= NULL
&& external_acl_grace_expired(acl
->def
, entry
)) {
771 // refresh in the background
772 ExternalACLLookup::Start(ch
, acl
, true);
773 debugs(82, 4, HERE
<< "no need to wait for the refresh of '" <<
774 key
<< "' in '" << acl
->def
->name
<< "' (ch=" << ch
<< ").");
778 debugs(82, 2, HERE
<< acl
->def
->name
<< "(\"" << key
<< "\") = lookup needed");
780 if (acl
->def
->theHelper
->stats
.queue_size
< (int)acl
->def
->theHelper
->childs
.n_active
) {
781 debugs(82, 2, HERE
<< "\"" << key
<< "\": queueing a call.");
782 if (!ch
->goAsync(ExternalACLLookup::Instance()))
783 debugs(82, 2, "\"" << key
<< "\": no async support!");
784 debugs(82, 2, HERE
<< "\"" << key
<< "\": return -1.");
785 return ACCESS_DUNNO
; // expired cached or simply absent entry
788 debugs(82, DBG_IMPORTANT
, "WARNING: external ACL '" << acl
->def
->name
<<
789 "' queue overload. Request rejected '" << key
<< "'.");
790 external_acl_message
= "SYSTEM TOO BUSY, TRY AGAIN LATER";
793 debugs(82, DBG_IMPORTANT
, "WARNING: external ACL '" << acl
->def
->name
<<
794 "' queue overload. Using stale result. '" << key
<< "'.");
796 /* Fall thru to processing below */
802 debugs(82, 4, HERE
<< "entry = { date=" <<
803 (long unsigned int) entry
->date
<<
804 ", result=" << entry
->result
<<
805 " tag=" << entry
->tag
<<
806 " log=" << entry
->log
<< " }");
808 debugs(82, 4, HERE
<< "entry user=" << entry
->user
);
811 external_acl_cache_touch(acl
->def
, entry
);
812 external_acl_message
= entry
->message
.termedBuf();
814 debugs(82, 2, HERE
<< acl
->def
->name
<< " = " << entry
->result
);
815 copyResultsFromEntry(ch
->request
, entry
);
816 return entry
->result
;
820 ACLExternal::match(ACLChecklist
*checklist
)
822 allow_t answer
= aclMatchExternal(data
, Filled(checklist
));
824 // convert to tri-state ACL match 1,0,-1
830 return 0; // non-match
833 case ACCESS_AUTH_REQUIRED
:
835 // If the answer is not allowed or denied (matches/not matches) and
836 // async authentication is not in progress, then we are done.
837 if (checklist
->keepMatching())
838 checklist
->markFinished(answer
, "aclMatchExternal exception");
844 ACLExternal::dump() const
846 external_acl_data
const *acl
= data
;
848 rv
.push_back(SBuf(acl
->def
->name
));
850 for (wordlist
*arg
= acl
->arguments
; arg
; arg
= arg
->next
) {
852 s
.Printf(" %s", arg
->key
);
859 /******************************************************************
864 external_acl_cache_touch(external_acl
* def
, const ExternalACLEntryPointer
&entry
)
866 // this must not be done when nothing is being cached.
867 if (def
->cache_size
<= 0 || (def
->ttl
<= 0 && entry
->result
== 1) || (def
->negative_ttl
<= 0 && entry
->result
!= 1))
870 dlinkDelete(&entry
->lru
, &def
->lru_list
);
871 ExternalACLEntry
*e
= const_cast<ExternalACLEntry
*>(entry
.getRaw()); // XXX: make hash a std::map of Pointer.
872 dlinkAdd(e
, &entry
->lru
, &def
->lru_list
);
876 makeExternalAclKey(ACLFilledChecklist
* ch
, external_acl_data
* acl_data
)
882 HttpRequest
*request
= ch
->request
;
883 HttpReply
*reply
= ch
->reply
;
885 bool data_used
= false;
887 for (external_acl_format::Pointer format
= acl_data
->def
->format
; format
!= NULL
; format
= format
->next
) {
888 const char *str
= NULL
;
891 switch (format
->type
) {
893 case Format::LFT_USER_LOGIN
:
894 // if this ACL line was the cause of credentials fetch
895 // they may not already be in the checklist
896 if (ch
->auth_user_request
== NULL
&& ch
->request
)
897 ch
->auth_user_request
= ch
->request
->auth_user_request
;
899 if (ch
->auth_user_request
!= NULL
)
900 str
= ch
->auth_user_request
->username();
904 case Format::LFT_USER_IDENT
:
908 // if we fail to go async, we still return NULL and the caller
909 // will detect the failure in ACLExternal::match().
910 (void)ch
->goAsync(IdentLookup::Instance());
917 case Format::LFT_CLIENT_IP_ADDRESS
:
918 str
= ch
->src_addr
.toStr(buf
,sizeof(buf
));
921 case Format::LFT_CLIENT_PORT
:
922 snprintf(buf
, sizeof(buf
), "%d", request
->client_addr
.port());
927 case Format::LFT_EXT_ACL_CLIENT_EUI48
:
928 if (request
->clientConnectionManager
.valid() && request
->clientConnectionManager
->clientConnection
!= NULL
&&
929 request
->clientConnectionManager
->clientConnection
->remoteEui48
.encode(buf
, sizeof(buf
)))
933 case Format::LFT_EXT_ACL_CLIENT_EUI64
:
934 if (request
->clientConnectionManager
.valid() && request
->clientConnectionManager
->clientConnection
!= NULL
&&
935 request
->clientConnectionManager
->clientConnection
->remoteEui64
.encode(buf
, sizeof(buf
)))
940 case Format::LFT_LOCAL_LISTENING_IP
:
941 str
= request
->my_addr
.toStr(buf
, sizeof(buf
));
944 case Format::LFT_LOCAL_LISTENING_PORT
:
945 snprintf(buf
, sizeof(buf
), "%d", request
->my_addr
.port());
949 case Format::LFT_CLIENT_REQ_URI
:
950 str
= urlCanonical(request
);
953 case Format::LFT_CLIENT_REQ_URLDOMAIN
:
954 str
= request
->GetHost();
957 case Format::LFT_CLIENT_REQ_URLSCHEME
:
958 str
= request
->url
.getScheme().c_str();
961 case Format::LFT_CLIENT_REQ_URLPORT
:
962 snprintf(buf
, sizeof(buf
), "%d", request
->port
);
966 case Format::LFT_CLIENT_REQ_URLPATH
:
967 str
= request
->urlpath
.termedBuf();
970 case Format::LFT_CLIENT_REQ_METHOD
: {
971 const SBuf
&s
= request
->method
.image();
972 sb
.append(s
.rawContent(), s
.length());
974 str
= sb
.termedBuf();
977 case Format::LFT_ADAPTED_REQUEST_HEADER
:
978 if (format
->header_id
== -1)
979 sb
= request
->header
.getByName(format
->header
);
981 sb
= request
->header
.getStrOrList(format
->header_id
);
982 str
= sb
.termedBuf();
985 case Format::LFT_ADAPTED_REQUEST_HEADER_ELEM
:
986 if (format
->header_id
== -1)
987 sb
= request
->header
.getByNameListMember(format
->header
, format
->member
, format
->separator
);
989 sb
= request
->header
.getListMember(format
->header_id
, format
->member
, format
->separator
);
990 str
= sb
.termedBuf();
993 case Format::LFT_REPLY_HEADER
:
995 if (format
->header_id
== -1)
996 sb
= reply
->header
.getByName(format
->header
);
998 sb
= reply
->header
.getStrOrList(format
->header_id
);
999 str
= sb
.termedBuf();
1003 case Format::LFT_REPLY_HEADER_ELEM
:
1005 if (format
->header_id
== -1)
1006 sb
= reply
->header
.getByNameListMember(format
->header
, format
->member
, format
->separator
);
1008 sb
= reply
->header
.getListMember(format
->header_id
, format
->member
, format
->separator
);
1009 str
= sb
.termedBuf();
1015 case Format::LFT_EXT_ACL_USER_CERT_RAW
:
1017 if (ch
->conn() != NULL
&& Comm::IsConnOpen(ch
->conn()->clientConnection
)) {
1018 SSL
*ssl
= fd_table
[ch
->conn()->clientConnection
->fd
].ssl
;
1021 str
= sslGetUserCertificatePEM(ssl
);
1026 case Format::LFT_EXT_ACL_USER_CERTCHAIN_RAW
:
1028 if (ch
->conn() != NULL
&& Comm::IsConnOpen(ch
->conn()->clientConnection
)) {
1029 SSL
*ssl
= fd_table
[ch
->conn()->clientConnection
->fd
].ssl
;
1032 str
= sslGetUserCertificateChainPEM(ssl
);
1037 case Format::LFT_EXT_ACL_USER_CERT
:
1039 if (ch
->conn() != NULL
&& Comm::IsConnOpen(ch
->conn()->clientConnection
)) {
1040 SSL
*ssl
= fd_table
[ch
->conn()->clientConnection
->fd
].ssl
;
1043 str
= sslGetUserAttribute(ssl
, format
->header
);
1048 case Format::LFT_EXT_ACL_USER_CA_CERT
:
1050 if (ch
->conn() != NULL
&& Comm::IsConnOpen(ch
->conn()->clientConnection
)) {
1051 SSL
*ssl
= fd_table
[ch
->conn()->clientConnection
->fd
].ssl
;
1054 str
= sslGetCAAttribute(ssl
, format
->header
);
1059 case Format::LFT_SSL_CLIENT_SNI
:
1060 if (ch
->conn() != NULL
) {
1061 if (Ssl::ServerBump
* srvBump
= ch
->conn()->serverBump()) {
1062 if (!srvBump
->clientSni
.isEmpty())
1063 str
= srvBump
->clientSni
.c_str();
1068 case Format::LFT_SSL_SERVER_CERT_SUBJECT
:
1069 case Format::LFT_SSL_SERVER_CERT_ISSUER
: {
1070 X509
*serverCert
= NULL
;
1071 if (ch
->serverCert
.get())
1072 serverCert
= ch
->serverCert
.get();
1073 else if (ch
->conn() && ch
->conn()->serverBump())
1074 serverCert
= ch
->conn()->serverBump()->serverCert
.get();
1077 if (format
->type
== Format::LFT_SSL_SERVER_CERT_SUBJECT
)
1078 str
= Ssl::GetX509UserAttribute(serverCert
, "DN");
1080 str
= Ssl::GetX509CAAttribute(serverCert
, "DN");
1087 case Format::LFT_USER_EXTERNAL
:
1088 str
= request
->extacl_user
.termedBuf();
1091 case Format::LFT_EXT_LOG
:
1092 str
= request
->extacl_log
.termedBuf();
1094 case Format::LFT_TAG
:
1095 str
= request
->tag
.termedBuf();
1097 case Format::LFT_EXT_ACL_NAME
:
1098 str
= acl_data
->name
;
1100 case Format::LFT_EXT_ACL_DATA
:
1102 for (arg
= acl_data
->arguments
; arg
; arg
= arg
->next
) {
1106 if (acl_data
->def
->quote
== external_acl::QUOTE_METHOD_URL
) {
1107 const char *quoted
= rfc1738_escape(arg
->key
);
1108 sb
.append(quoted
, strlen(quoted
));
1112 strwordquote(&mb2
, arg
->key
);
1113 sb
.append(mb2
.buf
, mb2
.size
);
1120 case Format::LFT_PERCENT
:
1125 // TODO: replace this function with Format::assemble()
1126 // For now die on unsupported logformat codes.
1127 fatalf("ERROR: unknown external_acl_type format %u", (uint8_t)format
->type
);
1141 if (acl_data
->def
->quote
== external_acl::QUOTE_METHOD_URL
) {
1142 const char *quoted
= rfc1738_escape(str
);
1143 mb
.append(quoted
, strlen(quoted
));
1145 strwordquote(&mb
, str
);
1154 for (arg
= acl_data
->arguments
; arg
; arg
= arg
->next
) {
1158 if (acl_data
->def
->quote
== external_acl::QUOTE_METHOD_URL
) {
1159 const char *quoted
= rfc1738_escape(arg
->key
);
1160 mb
.append(quoted
, strlen(quoted
));
1162 strwordquote(&mb
, arg
->key
);
1173 external_acl_entry_expired(external_acl
* def
, const ExternalACLEntryPointer
&entry
)
1175 if (def
->cache_size
<= 0)
1178 if (entry
->date
+ (entry
->result
== 1 ? def
->ttl
: def
->negative_ttl
) < squid_curtime
)
1185 external_acl_grace_expired(external_acl
* def
, const ExternalACLEntryPointer
&entry
)
1187 if (def
->cache_size
<= 0)
1191 ttl
= entry
->result
== 1 ? def
->ttl
: def
->negative_ttl
;
1192 ttl
= (ttl
* (100 - def
->grace
)) / 100;
1194 if (entry
->date
+ ttl
<= squid_curtime
)
1200 static ExternalACLEntryPointer
1201 external_acl_cache_add(external_acl
* def
, const char *key
, ExternalACLEntryData
const & data
)
1203 ExternalACLEntryPointer entry
;
1205 // do not bother caching this result if TTL is going to expire it immediately
1206 if (def
->cache_size
<= 0 || (def
->ttl
<= 0 && data
.result
== 1) || (def
->negative_ttl
<= 0 && data
.result
!= 1)) {
1208 entry
= new ExternalACLEntry
;
1209 entry
->key
= xstrdup(key
);
1210 entry
->update(data
);
1215 entry
= static_cast<ExternalACLEntry
*>(hash_lookup(def
->cache
, key
));
1216 debugs(82, 2, "external_acl_cache_add: Adding '" << key
<< "' = " << data
.result
);
1218 if (entry
!= NULL
) {
1219 debugs(82, 3, "updating existing entry");
1220 entry
->update(data
);
1221 external_acl_cache_touch(def
, entry
);
1225 entry
= new ExternalACLEntry
;
1226 entry
->key
= xstrdup(key
);
1227 entry
->update(data
);
1235 external_acl_cache_delete(external_acl
* def
, const ExternalACLEntryPointer
&entry
)
1237 assert(entry
!= NULL
);
1238 assert(def
->cache_size
> 0 && entry
->def
== def
);
1239 ExternalACLEntry
*e
= const_cast<ExternalACLEntry
*>(entry
.getRaw()); // XXX: make hash a std::map of Pointer.
1240 hash_remove_link(def
->cache
, e
);
1241 dlinkDelete(&e
->lru
, &def
->lru_list
);
1242 e
->unlock(); // unlock on behalf of the hash
1243 def
->cache_entries
-= 1;
1246 /******************************************************************
1247 * external_acl helpers
1250 typedef struct _externalAclState externalAclState
;
1252 struct _externalAclState
{
1254 void *callback_data
;
1258 externalAclState
*queue
;
1261 CBDATA_TYPE(externalAclState
);
1263 free_externalAclState(void *data
)
1265 externalAclState
*state
= static_cast<externalAclState
*>(data
);
1266 safe_free(state
->key
);
1267 cbdataReferenceDone(state
->callback_data
);
1268 cbdataReferenceDone(state
->def
);
1272 * The helper program receives queries on stdin, one
1273 * per line, and must return the result on on stdout
1275 * General result syntax:
1277 * OK/ERR keyword=value ...
1281 * user= The users name (login)
1282 * message= Message describing the reason
1283 * tag= A string tag to be applied to the request that triggered the acl match.
1284 * applies to both OK and ERR responses.
1285 * Won't override existing request tags.
1286 * log= A string to be used in access logging
1288 * Other keywords may be added to the protocol later
1290 * value needs to be URL-encoded or enclosed in double quotes (")
1291 * with \-escaping on any whitespace, quotes, or slashes (\).
1294 externalAclHandleReply(void *data
, const Helper::Reply
&reply
)
1296 externalAclState
*state
= static_cast<externalAclState
*>(data
);
1297 externalAclState
*next
;
1298 ExternalACLEntryData entryData
;
1299 entryData
.result
= ACCESS_DENIED
;
1301 debugs(82, 2, HERE
<< "reply=" << reply
);
1303 if (reply
.result
== Helper::Okay
)
1304 entryData
.result
= ACCESS_ALLOWED
;
1305 // XXX: handle other non-DENIED results better
1307 // XXX: make entryData store a proper Helper::Reply object instead of copying.
1309 entryData
.notes
.append(&reply
.notes
);
1311 const char *label
= reply
.notes
.findFirst("tag");
1312 if (label
!= NULL
&& *label
!= '\0')
1313 entryData
.tag
= label
;
1315 label
= reply
.notes
.findFirst("message");
1316 if (label
!= NULL
&& *label
!= '\0')
1317 entryData
.message
= label
;
1319 label
= reply
.notes
.findFirst("log");
1320 if (label
!= NULL
&& *label
!= '\0')
1321 entryData
.log
= label
;
1324 label
= reply
.notes
.findFirst("user");
1325 if (label
!= NULL
&& *label
!= '\0')
1326 entryData
.user
= label
;
1328 label
= reply
.notes
.findFirst("password");
1329 if (label
!= NULL
&& *label
!= '\0')
1330 entryData
.password
= label
;
1333 dlinkDelete(&state
->list
, &state
->def
->queue
);
1335 ExternalACLEntryPointer entry
;
1336 if (cbdataReferenceValid(state
->def
)) {
1337 // only cache OK and ERR results.
1338 if (reply
.result
== Helper::Okay
|| reply
.result
== Helper::Error
)
1339 entry
= external_acl_cache_add(state
->def
, state
->key
, entryData
);
1341 const ExternalACLEntryPointer oldentry
= static_cast<ExternalACLEntry
*>(hash_lookup(state
->def
->cache
, state
->key
));
1343 if (oldentry
!= NULL
)
1344 external_acl_cache_delete(state
->def
, oldentry
);
1350 cbdataReferenceDone(state
->def
);
1352 if (state
->callback
&& cbdataReferenceValidDone(state
->callback_data
, &cbdata
))
1353 state
->callback(cbdata
, entry
);
1355 next
= state
->queue
;
1364 ACLExternal::ExternalAclLookup(ACLChecklist
*checklist
, ACLExternal
* me
)
1366 ExternalACLLookup::Start(checklist
, me
->data
, false);
1370 ExternalACLLookup::Start(ACLChecklist
*checklist
, external_acl_data
*acl
, bool inBackground
)
1372 external_acl
*def
= acl
->def
;
1374 ACLFilledChecklist
*ch
= Filled(checklist
);
1375 const char *key
= makeExternalAclKey(ch
, acl
);
1376 assert(key
); // XXX: will fail if EXT_ACL_IDENT case needs an async lookup
1378 debugs(82, 2, HERE
<< (inBackground
? "bg" : "fg") << " lookup in '" <<
1379 def
->name
<< "' for '" << key
<< "'");
1381 /* Check for a pending lookup to hook into */
1382 // only possible if we are caching results.
1383 externalAclState
*oldstate
= NULL
;
1384 if (def
->cache_size
> 0) {
1385 for (dlink_node
*node
= def
->queue
.head
; node
; node
= node
->next
) {
1386 externalAclState
*oldstatetmp
= static_cast<externalAclState
*>(node
->data
);
1388 if (strcmp(key
, oldstatetmp
->key
) == 0) {
1389 oldstate
= oldstatetmp
;
1395 // A background refresh has no need to piggiback on a pending request:
1396 // When the pending request completes, the cache will be refreshed anyway.
1397 if (oldstate
&& inBackground
) {
1398 debugs(82, 7, HERE
<< "'" << def
->name
<< "' queue is already being refreshed (ch=" << ch
<< ")");
1402 externalAclState
*state
= cbdataAlloc(externalAclState
);
1403 state
->def
= cbdataReference(def
);
1405 state
->key
= xstrdup(key
);
1407 if (!inBackground
) {
1408 state
->callback
= &ExternalACLLookup::LookupDone
;
1409 state
->callback_data
= cbdataReference(checklist
);
1413 /* Hook into pending lookup */
1414 state
->queue
= oldstate
->queue
;
1415 oldstate
->queue
= state
;
1417 /* No pending lookup found. Sumbit to helper */
1419 /* Check for queue overload */
1421 if (def
->theHelper
->stats
.queue_size
>= (int)def
->theHelper
->childs
.n_running
) {
1422 debugs(82, 7, HERE
<< "'" << def
->name
<< "' queue is too long");
1423 assert(inBackground
); // or the caller should have checked
1428 /* Send it off to the helper */
1432 buf
.Printf("%s\n", key
);
1434 debugs(82, 4, "externalAclLookup: looking up for '" << key
<< "' in '" << def
->name
<< "'.");
1436 helperSubmit(def
->theHelper
, buf
.buf
, externalAclHandleReply
, state
);
1438 dlinkAdd(state
, &state
->list
, &def
->queue
);
1443 debugs(82, 4, "externalAclLookup: will wait for the result of '" << key
<<
1444 "' in '" << def
->name
<< "' (ch=" << ch
<< ").");
1448 externalAclStats(StoreEntry
* sentry
)
1452 for (p
= Config
.externalAclHelperList
; p
; p
= p
->next
) {
1453 storeAppendPrintf(sentry
, "External ACL Statistics: %s\n", p
->name
);
1454 storeAppendPrintf(sentry
, "Cache size: %d\n", p
->cache
->count
);
1455 helperStats(sentry
, p
->theHelper
);
1456 storeAppendPrintf(sentry
, "\n");
1461 externalAclRegisterWithCacheManager(void)
1463 Mgr::RegisterAction("external_acl",
1464 "External ACL stats",
1465 externalAclStats
, 0, 1);
1469 externalAclInit(void)
1471 static int firstTimeInit
= 1;
1474 for (p
= Config
.externalAclHelperList
; p
; p
= p
->next
) {
1476 p
->cache
= hash_create((HASHCMP
*) strcmp
, hashPrime(1024), hash4
);
1479 p
->theHelper
= new helper(p
->name
);
1481 p
->theHelper
->cmdline
= p
->cmdline
;
1483 p
->theHelper
->childs
.updateLimits(p
->children
);
1485 p
->theHelper
->ipc_type
= IPC_TCP_SOCKET
;
1487 p
->theHelper
->addr
= p
->local_addr
;
1489 helperOpenServers(p
->theHelper
);
1492 if (firstTimeInit
) {
1494 CBDATA_INIT_TYPE_FREECB(externalAclState
, free_externalAclState
);
1497 externalAclRegisterWithCacheManager();
1501 externalAclShutdown(void)
1505 for (p
= Config
.externalAclHelperList
; p
; p
= p
->next
) {
1506 helperShutdown(p
->theHelper
);
1510 ExternalACLLookup
ExternalACLLookup::instance_
;
1512 ExternalACLLookup::Instance()
1518 ExternalACLLookup::checkForAsync(ACLChecklist
*checklist
)const
1520 /* TODO: optimise this - we probably have a pointer to this
1521 * around somewhere */
1522 ACL
*acl
= ACL::FindByName(AclMatchedName
);
1524 ACLExternal
*me
= dynamic_cast<ACLExternal
*> (acl
);
1526 ACLExternal::ExternalAclLookup(checklist
, me
);
1529 /// Called when an async lookup returns
1531 ExternalACLLookup::LookupDone(void *data
, const ExternalACLEntryPointer
&result
)
1533 ACLFilledChecklist
*checklist
= Filled(static_cast<ACLChecklist
*>(data
));
1534 checklist
->extacl_entry
= result
;
1536 // attach the helper kv-pair to the transaction
1537 if (checklist
->extacl_entry
!= NULL
) {
1538 if (HttpRequest
* req
= checklist
->request
) {
1539 // XXX: we have no access to the transaction / AccessLogEntry so cant SyncNotes().
1540 // workaround by using anything already set in HttpRequest
1541 // OR use new and rely on a later Sync copying these to AccessLogEntry
1543 UpdateRequestNotes(checklist
->conn(), *req
, checklist
->extacl_entry
->notes
);
1547 checklist
->resumeNonBlockingCheck(ExternalACLLookup::Instance());
1550 /* This registers "external" in the registry. To do dynamic definitions
1551 * of external ACL's, rather than a static prototype, have a Prototype instance
1552 * prototype in the class that defines each external acl 'class'.
1553 * Then, then the external acl instance is created, it self registers under
1555 * Be sure that clone is fully functional for that acl class though!
1557 ACL::Prototype
ACLExternal::RegistryProtoype(&ACLExternal::RegistryEntry_
, "external");
1559 ACLExternal
ACLExternal::RegistryEntry_("external");
1562 ACLExternal::clone() const
1564 return new ACLExternal(*this);
1567 ACLExternal::ACLExternal(char const *theClass
) : data(NULL
), class_(xstrdup(theClass
))
1570 ACLExternal::ACLExternal(ACLExternal
const & old
) : data(NULL
), class_(old
.class_
? xstrdup(old
.class_
) : NULL
)
1572 /* we don't have copy constructors for the data yet */
1577 ACLExternal::typeString() const
1583 ACLExternal::isProxyAuth() const
1586 return data
->def
->require_auth
;