2 * Copyright (C) 1996-2015 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
71 MEMPROXY_CLASS(external_acl_format
);
74 typedef RefCount
<external_acl_format
> Pointer
;
76 external_acl_format() :
77 type(Format::LFT_NONE
),
81 header_id(Http::HdrType::BAD_HDR
)
83 ~external_acl_format() {
88 Format::ByteCode_t type
;
89 external_acl_format::Pointer next
;
93 Http::HdrType header_id
;
98 /* FIXME: These are not really cbdata, but it is an easy way
99 * to get them pooled, refcounted, accounted and freed properly...
101 CBDATA_CLASS(external_acl
);
109 void add(const ExternalACLEntryPointer
&);
121 external_acl_format::Pointer format
;
125 Helper::ChildConfig children
;
141 * Configuration flag. May only be altered by the configuration parser.
143 * Indicates that all uses of this external_acl_type helper require authentication
144 * details to be processed. If none are available its a fail match.
150 QUOTE_METHOD_SHELL
= 1,
154 Ip::Address local_addr
;
157 CBDATA_CLASS_INIT(external_acl
);
159 external_acl::external_acl() :
161 ttl(DEFAULT_EXTERNAL_ACL_TTL
),
166 children(DEFAULT_EXTERNAL_ACL_CHILDREN
),
169 cache_size(256*1024),
174 quote(external_acl::QUOTE_METHOD_URL
)
176 local_addr
.setLocalhost();
179 external_acl::~external_acl()
183 wordlistDestroy(&cmdline
);
186 helperShutdown(theHelper
);
191 while (lru_list
.tail
) {
192 ExternalACLEntryPointer
e(static_cast<ExternalACLEntry
*>(lru_list
.tail
->data
));
193 external_acl_cache_delete(this, e
);
196 hashFreeMemory(cache
);
199 external_acl
*node
= next
;
201 node
->next
= NULL
; // prevent recursion
207 * Parse the External ACL format %<{.*} and %>{.*} token(s) to pass a specific
208 * request or reply header to external helper.
210 \param header - the token being parsed (without the identifying prefix)
211 \param type - format enum identifier for this element, pulled from identifying prefix
212 \param format - structure to contain all the info about this format element.
215 parse_header_token(external_acl_format::Pointer format
, char *header
, const Format::ByteCode_t type
)
220 /** Cut away the closing brace */
221 end
= strchr(header
, '}');
222 if (end
&& strlen(end
) == 1)
227 member
= strchr(header
, ':');
230 /* Split in header and member */
234 if (!xisalnum(*member
)) {
235 format
->separator
= *member
;
238 format
->separator
= ',';
241 format
->member
= xstrdup(member
);
243 if (type
== Format::LFT_ADAPTED_REQUEST_HEADER
)
244 format
->type
= Format::LFT_ADAPTED_REQUEST_HEADER_ELEM
;
246 format
->type
= Format::LFT_REPLY_HEADER_ELEM
;
252 format
->header
= xstrdup(header
);
253 format
->header_id
= Http::HeaderLookupTable
.lookup(SBuf(header
)).id
;
257 parse_externalAclHelper(external_acl
** list
)
259 external_acl
*a
= new external_acl
;
260 char *token
= ConfigParser::NextToken();
265 a
->name
= xstrdup(token
);
267 // Allow supported %macros inside quoted tokens
268 ConfigParser::EnableMacros();
269 token
= ConfigParser::NextToken();
273 if (strncmp(token
, "ttl=", 4) == 0) {
274 a
->ttl
= atoi(token
+ 4);
275 } else if (strncmp(token
, "negative_ttl=", 13) == 0) {
276 a
->negative_ttl
= atoi(token
+ 13);
277 } else if (strncmp(token
, "children=", 9) == 0) {
278 a
->children
.n_max
= atoi(token
+ 9);
279 debugs(0, DBG_CRITICAL
, "WARNING: external_acl_type option children=N has been deprecated in favor of children-max=N and children-startup=N");
280 } else if (strncmp(token
, "children-max=", 13) == 0) {
281 a
->children
.n_max
= atoi(token
+ 13);
282 } else if (strncmp(token
, "children-startup=", 17) == 0) {
283 a
->children
.n_startup
= atoi(token
+ 17);
284 } else if (strncmp(token
, "children-idle=", 14) == 0) {
285 a
->children
.n_idle
= atoi(token
+ 14);
286 } else if (strncmp(token
, "concurrency=", 12) == 0) {
287 a
->children
.concurrency
= atoi(token
+ 12);
288 } else if (strncmp(token
, "queue-size=", 11) == 0) {
289 a
->children
.queue_size
= atoi(token
+ 11);
290 a
->children
.defaultQueueSize
= false;
291 } else if (strncmp(token
, "cache=", 6) == 0) {
292 a
->cache_size
= atoi(token
+ 6);
293 } else if (strncmp(token
, "grace=", 6) == 0) {
294 a
->grace
= atoi(token
+ 6);
295 } else if (strcmp(token
, "protocol=2.5") == 0) {
296 a
->quote
= external_acl::QUOTE_METHOD_SHELL
;
297 } else if (strcmp(token
, "protocol=3.0") == 0) {
298 debugs(3, DBG_PARSE_NOTE(2), "WARNING: external_acl_type option protocol=3.0 is deprecated. Remove this from your config.");
299 a
->quote
= external_acl::QUOTE_METHOD_URL
;
300 } else if (strcmp(token
, "quote=url") == 0) {
301 debugs(3, DBG_PARSE_NOTE(2), "WARNING: external_acl_type option quote=url is deprecated. Remove this from your config.");
302 a
->quote
= external_acl::QUOTE_METHOD_URL
;
303 } else if (strcmp(token
, "quote=shell") == 0) {
304 debugs(3, DBG_PARSE_NOTE(2), "WARNING: external_acl_type option quote=shell is deprecated. Use protocol=2.5 if still needed.");
305 a
->quote
= external_acl::QUOTE_METHOD_SHELL
;
307 /* INET6: allow admin to configure some helpers explicitly to
308 bind to IPv4/v6 localhost port. */
309 } else if (strcmp(token
, "ipv4") == 0) {
310 if ( !a
->local_addr
.setIPv4() ) {
311 debugs(3, DBG_CRITICAL
, "WARNING: Error converting " << a
->local_addr
<< " to IPv4 in " << a
->name
);
313 } else if (strcmp(token
, "ipv6") == 0) {
315 debugs(3, DBG_CRITICAL
, "WARNING: --enable-ipv6 required for external ACL helpers to use IPv6: " << a
->name
);
316 // else nothing to do.
321 token
= ConfigParser::NextToken();
323 ConfigParser::DisableMacros();
325 /* check that child startup value is sane. */
326 if (a
->children
.n_startup
> a
->children
.n_max
)
327 a
->children
.n_startup
= a
->children
.n_max
;
329 /* check that child idle value is sane. */
330 if (a
->children
.n_idle
> a
->children
.n_max
)
331 a
->children
.n_idle
= a
->children
.n_max
;
332 if (a
->children
.n_idle
< 1)
333 a
->children
.n_idle
= 1;
335 if (a
->negative_ttl
== -1)
336 a
->negative_ttl
= a
->ttl
;
338 if (a
->children
.defaultQueueSize
)
339 a
->children
.queue_size
= 2 * a
->children
.n_max
;
342 external_acl_format::Pointer
*p
= &a
->format
;
345 /* stop on first non-format token found */
350 external_acl_format::Pointer format
= new external_acl_format
;
352 if (strncmp(token
, "%{", 2) == 0) {
353 // deprecated. but assume the old configs all referred to request headers.
354 debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT
), "WARNING: external_acl_type format %{...} is being replaced by %>ha{...} for : " << token
);
355 parse_header_token(format
, (token
+2), Format::LFT_ADAPTED_REQUEST_HEADER
);
356 } else if (strncmp(token
, "%>{", 3) == 0) {
357 debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT
), "WARNING: external_acl_type format %>{...} is being replaced by %>ha{...} for : " << token
);
358 parse_header_token(format
, (token
+3), Format::LFT_ADAPTED_REQUEST_HEADER
);
359 } else if (strncmp(token
, "%>ha{", 5) == 0) {
360 parse_header_token(format
, (token
+5), Format::LFT_ADAPTED_REQUEST_HEADER
);
361 } else if (strncmp(token
, "%<{", 3) == 0) {
362 debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT
), "WARNING: external_acl_type format %<{...} is being replaced by %<h{...} for : " << token
);
363 parse_header_token(format
, (token
+3), Format::LFT_REPLY_HEADER
);
364 } else if (strncmp(token
, "%<h{", 4) == 0) {
365 parse_header_token(format
, (token
+4), Format::LFT_REPLY_HEADER
);
367 } else if (strcmp(token
, "%LOGIN") == 0 || strcmp(token
, "%ul") == 0) {
368 format
->type
= Format::LFT_USER_LOGIN
;
369 a
->require_auth
= true;
373 else if (strcmp(token
, "%IDENT") == 0 || strcmp(token
, "%ui") == 0)
374 format
->type
= Format::LFT_USER_IDENT
;
376 else if (strcmp(token
, "%SRC") == 0 || strcmp(token
, "%>a") == 0)
377 format
->type
= Format::LFT_CLIENT_IP_ADDRESS
;
378 else if (strcmp(token
, "%SRCPORT") == 0 || strcmp(token
, "%>p") == 0)
379 format
->type
= Format::LFT_CLIENT_PORT
;
381 else if (strcmp(token
, "%SRCEUI48") == 0)
382 format
->type
= Format::LFT_EXT_ACL_CLIENT_EUI48
;
383 else if (strcmp(token
, "%SRCEUI64") == 0)
384 format
->type
= Format::LFT_EXT_ACL_CLIENT_EUI64
;
386 else if (strcmp(token
, "%MYADDR") == 0 || strcmp(token
, "%la") == 0)
387 format
->type
= Format::LFT_LOCAL_LISTENING_IP
;
388 else if (strcmp(token
, "%MYPORT") == 0 || strcmp(token
, "%lp") == 0)
389 format
->type
= Format::LFT_LOCAL_LISTENING_PORT
;
390 else if (strcmp(token
, "%URI") == 0 || strcmp(token
, "%>ru") == 0)
391 format
->type
= Format::LFT_CLIENT_REQ_URI
;
392 else if (strcmp(token
, "%DST") == 0 || strcmp(token
, "%>rd") == 0)
393 format
->type
= Format::LFT_CLIENT_REQ_URLDOMAIN
;
394 else if (strcmp(token
, "%PROTO") == 0 || strcmp(token
, "%>rs") == 0)
395 format
->type
= Format::LFT_CLIENT_REQ_URLSCHEME
;
396 else if (strcmp(token
, "%PORT") == 0) // XXX: add a logformat token
397 format
->type
= Format::LFT_CLIENT_REQ_URLPORT
;
398 else if (strcmp(token
, "%PATH") == 0 || strcmp(token
, "%>rp") == 0)
399 format
->type
= Format::LFT_CLIENT_REQ_URLPATH
;
400 else if (strcmp(token
, "%METHOD") == 0 || strcmp(token
, "%>rm") == 0)
401 format
->type
= Format::LFT_CLIENT_REQ_METHOD
;
403 else if (strcmp(token
, "%USER_CERT") == 0)
404 format
->type
= Format::LFT_EXT_ACL_USER_CERT_RAW
;
405 else if (strcmp(token
, "%USER_CERTCHAIN") == 0)
406 format
->type
= Format::LFT_EXT_ACL_USER_CERTCHAIN_RAW
;
407 else if (strncmp(token
, "%USER_CERT_", 11) == 0) {
408 format
->type
= Format::LFT_EXT_ACL_USER_CERT
;
409 format
->header
= xstrdup(token
+ 11);
410 } else if (strncmp(token
, "%USER_CA_CERT_", 14) == 0) {
411 format
->type
= Format::LFT_EXT_ACL_USER_CA_CERT
;
412 format
->header
= xstrdup(token
+ 14);
413 } else if (strncmp(token
, "%CA_CERT_", 9) == 0) {
414 debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT
), "WARNING: external_acl_type %CA_CERT_* code is obsolete. Use %USER_CA_CERT_* instead");
415 format
->type
= Format::LFT_EXT_ACL_USER_CA_CERT
;
416 format
->header
= xstrdup(token
+ 9);
417 } else if (strcmp(token
, "%ssl::>sni") == 0)
418 format
->type
= Format::LFT_SSL_CLIENT_SNI
;
419 else if (strcmp(token
, "%ssl::<cert_subject") == 0)
420 format
->type
= Format::LFT_SSL_SERVER_CERT_SUBJECT
;
421 else if (strcmp(token
, "%ssl::<cert_issuer") == 0)
422 format
->type
= Format::LFT_SSL_SERVER_CERT_ISSUER
;
425 else if (strcmp(token
, "%EXT_USER") == 0 || strcmp(token
, "%ue") == 0)
426 format
->type
= Format::LFT_USER_EXTERNAL
;
428 #if USE_AUTH || defined(USE_OPENSSL) || defined(USE_IDENT)
429 else if (strcmp(token
, "%un") == 0)
430 format
->type
= Format::LFT_USER_NAME
;
432 else if (strcmp(token
, "%EXT_LOG") == 0 || strcmp(token
, "%ea") == 0)
433 format
->type
= Format::LFT_EXT_LOG
;
434 else if (strcmp(token
, "%TAG") == 0 || strcmp(token
, "%et") == 0)
435 format
->type
= Format::LFT_TAG
;
436 else if (strcmp(token
, "%ACL") == 0)
437 format
->type
= Format::LFT_EXT_ACL_NAME
;
438 else if (strcmp(token
, "%DATA") == 0)
439 format
->type
= Format::LFT_EXT_ACL_DATA
;
440 else if (strcmp(token
, "%%") == 0)
441 format
->type
= Format::LFT_PERCENT
;
443 debugs(0, DBG_CRITICAL
, "ERROR: Unknown Format token " << token
);
449 token
= ConfigParser::NextToken();
452 /* There must be at least one format token */
460 wordlistAdd(&a
->cmdline
, token
);
463 parse_wordlist(&a
->cmdline
);
466 list
= &(*list
)->next
;
472 dump_externalAclHelper(StoreEntry
* sentry
, const char *name
, const external_acl
* list
)
474 const external_acl
*node
;
475 const wordlist
*word
;
477 for (node
= list
; node
; node
= node
->next
) {
478 storeAppendPrintf(sentry
, "%s %s", name
, node
->name
);
480 if (!node
->local_addr
.isIPv6())
481 storeAppendPrintf(sentry
, " ipv4");
483 storeAppendPrintf(sentry
, " ipv6");
485 if (node
->ttl
!= DEFAULT_EXTERNAL_ACL_TTL
)
486 storeAppendPrintf(sentry
, " ttl=%d", node
->ttl
);
488 if (node
->negative_ttl
!= node
->ttl
)
489 storeAppendPrintf(sentry
, " negative_ttl=%d", node
->negative_ttl
);
492 storeAppendPrintf(sentry
, " grace=%d", node
->grace
);
494 if (node
->children
.n_max
!= DEFAULT_EXTERNAL_ACL_CHILDREN
)
495 storeAppendPrintf(sentry
, " children-max=%d", node
->children
.n_max
);
497 if (node
->children
.n_startup
!= 1)
498 storeAppendPrintf(sentry
, " children-startup=%d", node
->children
.n_startup
);
500 if (node
->children
.n_idle
!= (node
->children
.n_max
+ node
->children
.n_startup
) )
501 storeAppendPrintf(sentry
, " children-idle=%d", node
->children
.n_idle
);
503 if (node
->children
.concurrency
)
504 storeAppendPrintf(sentry
, " concurrency=%d", node
->children
.concurrency
);
507 storeAppendPrintf(sentry
, " cache=%d", node
->cache_size
);
509 if (node
->quote
== external_acl::QUOTE_METHOD_SHELL
)
510 storeAppendPrintf(sentry
, " protocol=2.5");
512 for (external_acl_format::Pointer format
= node
->format
; format
!= NULL
; format
= format
->next
) {
513 switch (format
->type
) {
515 case Format::LFT_ADAPTED_REQUEST_HEADER
:
516 storeAppendPrintf(sentry
, " %%>ha{%s}", format
->header
);
519 case Format::LFT_ADAPTED_REQUEST_HEADER_ELEM
:
520 storeAppendPrintf(sentry
, " %%>ha{%s:%s}", format
->header
, format
->member
);
523 case Format::LFT_REPLY_HEADER
:
524 storeAppendPrintf(sentry
, " %%<h{%s}", format
->header
);
527 case Format::LFT_REPLY_HEADER_ELEM
:
528 storeAppendPrintf(sentry
, " %%<h{%s:%s}", format
->header
, format
->member
);
531 #define DUMP_EXT_ACL_TYPE_FMT(a, fmt, ...) \
532 case Format::LFT_##a: \
533 storeAppendPrintf(sentry, fmt, ##__VA_ARGS__); \
536 DUMP_EXT_ACL_TYPE_FMT(USER_LOGIN
," %%ul");
537 DUMP_EXT_ACL_TYPE_FMT(USER_NAME
," %%un");
541 DUMP_EXT_ACL_TYPE_FMT(USER_IDENT
," %%ui");
543 DUMP_EXT_ACL_TYPE_FMT(CLIENT_IP_ADDRESS
," %%>a");
544 DUMP_EXT_ACL_TYPE_FMT(CLIENT_PORT
," %%>p");
546 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_CLIENT_EUI48
," %%SRCEUI48");
547 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_CLIENT_EUI64
," %%SRCEUI64");
549 DUMP_EXT_ACL_TYPE_FMT(LOCAL_LISTENING_IP
," %%>la");
550 DUMP_EXT_ACL_TYPE_FMT(LOCAL_LISTENING_PORT
," %%>lp");
551 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URI
," %%>ru");
552 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLDOMAIN
," %%>rd");
553 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLSCHEME
," %%>rs");
554 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLPORT
," %%>rP");
555 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLPATH
," %%>rp");
556 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_METHOD
," %%>rm");
558 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERT_RAW
, " %%USER_CERT_RAW");
559 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERTCHAIN_RAW
, " %%USER_CERTCHAIN_RAW");
560 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERT
, " %%USER_CERT_%s", format
->header
);
561 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CA_CERT
, " %%USER_CA_CERT_%s", format
->header
);
562 DUMP_EXT_ACL_TYPE_FMT(SSL_CLIENT_SNI
, "%%ssl::>sni");
563 DUMP_EXT_ACL_TYPE_FMT(SSL_SERVER_CERT_SUBJECT
, "%%ssl::<cert_subject");
564 DUMP_EXT_ACL_TYPE_FMT(SSL_SERVER_CERT_ISSUER
, "%%ssl::<cert_issuer");
567 DUMP_EXT_ACL_TYPE_FMT(USER_EXTERNAL
," %%ue");
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");
573 DUMP_EXT_ACL_TYPE_FMT(PERCENT
, " %%%%");
575 fatal("unknown external_acl format error");
580 for (word
= node
->cmdline
; word
; word
= word
->next
)
581 storeAppendPrintf(sentry
, " %s", word
->key
);
583 storeAppendPrintf(sentry
, "\n");
588 free_externalAclHelper(external_acl
** list
)
594 static external_acl
*
595 find_externalAclHelper(const char *name
)
599 for (node
= Config
.externalAclHelperList
; node
; node
= node
->next
) {
600 if (strcmp(node
->name
, name
) == 0)
608 external_acl::add(const ExternalACLEntryPointer
&anEntry
)
611 assert(anEntry
!= NULL
);
612 assert (anEntry
->def
== NULL
);
614 ExternalACLEntry
*e
= const_cast<ExternalACLEntry
*>(anEntry
.getRaw()); // XXX: make hash a std::map of Pointer.
616 dlinkAdd(e
, &e
->lru
, &lru_list
);
617 e
->lock(); //cbdataReference(e); // lock it on behalf of the hash
622 external_acl::trimCache()
624 if (cache_size
&& cache_entries
>= cache_size
) {
625 ExternalACLEntryPointer
e(static_cast<ExternalACLEntry
*>(lru_list
.tail
->data
));
626 external_acl_cache_delete(this, e
);
630 /******************************************************************
634 class external_acl_data
636 CBDATA_CLASS(external_acl_data
);
639 explicit external_acl_data(external_acl
*aDef
) : def(cbdataReference(aDef
)), name(NULL
), arguments(NULL
) {}
640 ~external_acl_data();
647 CBDATA_CLASS_INIT(external_acl_data
);
649 external_acl_data::~external_acl_data()
652 wordlistDestroy(&arguments
);
653 cbdataReferenceDone(def
);
662 char *token
= ConfigParser::strtokFile();
667 data
= new external_acl_data(find_externalAclHelper(token
));
672 // def->name is the name of the external_acl_type.
673 // this is the name of the 'acl' directive being tested
674 data
->name
= xstrdup(AclMatchedName
);
676 while ((token
= ConfigParser::strtokFile())) {
677 wordlistAdd(&data
->arguments
, token
);
682 ACLExternal::valid () const
685 if (data
->def
->require_auth
) {
686 if (authenticateSchemeCount() == 0) {
687 debugs(28, DBG_CRITICAL
, "Can't use proxy auth because no authentication schemes were compiled.");
691 if (authenticateActiveSchemeCount() == 0) {
692 debugs(28, DBG_CRITICAL
, "Can't use proxy auth because no authentication schemes are fully configured.");
702 ACLExternal::empty () const
707 ACLExternal::~ACLExternal()
714 copyResultsFromEntry(HttpRequest
*req
, const ExternalACLEntryPointer
&entry
)
718 if (entry
->user
.size())
719 req
->extacl_user
= entry
->user
;
721 if (entry
->password
.size())
722 req
->extacl_passwd
= entry
->password
;
724 if (!req
->tag
.size())
725 req
->tag
= entry
->tag
;
727 if (entry
->log
.size())
728 req
->extacl_log
= entry
->log
;
730 if (entry
->message
.size())
731 req
->extacl_message
= entry
->message
;
733 // attach the helper kv-pair to the transaction
734 UpdateRequestNotes(req
->clientConnectionManager
.get(), *req
, entry
->notes
);
739 aclMatchExternal(external_acl_data
*acl
, ACLFilledChecklist
*ch
)
741 debugs(82, 9, HERE
<< "acl=\"" << acl
->def
->name
<< "\"");
742 ExternalACLEntryPointer entry
= ch
->extacl_entry
;
744 external_acl_message
= "MISSING REQUIRED INFORMATION";
747 if (entry
->def
== acl
->def
) {
748 /* Ours, use it.. if the key matches */
749 const char *key
= makeExternalAclKey(ch
, acl
);
751 return ACCESS_DUNNO
; // insufficent data to continue
752 if (strcmp(key
, (char*)entry
->key
) != 0) {
753 debugs(82, 9, "entry key='" << (char *)entry
->key
<< "', our key='" << key
<< "' dont match. Discarded.");
754 // too bad. need a new lookup.
755 entry
= ch
->extacl_entry
= NULL
;
758 /* Not ours.. get rid of it */
759 debugs(82, 9, "entry " << entry
<< " not valid or not ours. Discarded.");
761 debugs(82, 9, "entry def=" << entry
->def
<< ", our def=" << acl
->def
);
762 const char *key
= makeExternalAclKey(ch
, acl
); // may be nil
763 debugs(82, 9, "entry key='" << (char *)entry
->key
<< "', our key='" << key
<< "'");
765 entry
= ch
->extacl_entry
= NULL
;
770 debugs(82, 9, HERE
<< "No helper entry available");
772 if (acl
->def
->require_auth
) {
773 /* Make sure the user is authenticated */
774 debugs(82, 3, HERE
<< acl
->def
->name
<< " check user authenticated.");
775 const allow_t ti
= AuthenticateAcl(ch
);
776 if (ti
!= ACCESS_ALLOWED
) {
777 debugs(82, 2, HERE
<< acl
->def
->name
<< " user not authenticated (" << ti
<< ")");
780 debugs(82, 3, HERE
<< acl
->def
->name
<< " user is authenticated.");
783 const char *key
= makeExternalAclKey(ch
, acl
);
786 /* Not sufficient data to process */
790 entry
= static_cast<ExternalACLEntry
*>(hash_lookup(acl
->def
->cache
, key
));
792 const ExternalACLEntryPointer staleEntry
= entry
;
793 if (entry
!= NULL
&& external_acl_entry_expired(acl
->def
, entry
))
796 if (entry
!= NULL
&& external_acl_grace_expired(acl
->def
, entry
)) {
797 // refresh in the background
798 ExternalACLLookup::Start(ch
, acl
, true);
799 debugs(82, 4, HERE
<< "no need to wait for the refresh of '" <<
800 key
<< "' in '" << acl
->def
->name
<< "' (ch=" << ch
<< ").");
804 debugs(82, 2, HERE
<< acl
->def
->name
<< "(\"" << key
<< "\") = lookup needed");
806 if (!acl
->def
->theHelper
->queueFull()) {
807 debugs(82, 2, HERE
<< "\"" << key
<< "\": queueing a call.");
808 if (!ch
->goAsync(ExternalACLLookup::Instance()))
809 debugs(82, 2, "\"" << key
<< "\": no async support!");
810 debugs(82, 2, HERE
<< "\"" << key
<< "\": return -1.");
811 return ACCESS_DUNNO
; // expired cached or simply absent entry
814 debugs(82, DBG_IMPORTANT
, "WARNING: external ACL '" << acl
->def
->name
<<
815 "' queue overload. Request rejected '" << key
<< "'.");
816 external_acl_message
= "SYSTEM TOO BUSY, TRY AGAIN LATER";
819 debugs(82, DBG_IMPORTANT
, "WARNING: external ACL '" << acl
->def
->name
<<
820 "' queue overload. Using stale result. '" << key
<< "'.");
822 /* Fall thru to processing below */
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
<< " }");
834 debugs(82, 4, HERE
<< "entry user=" << entry
->user
);
837 external_acl_cache_touch(acl
->def
, entry
);
838 external_acl_message
= entry
->message
.termedBuf();
840 debugs(82, 2, HERE
<< acl
->def
->name
<< " = " << entry
->result
);
841 copyResultsFromEntry(ch
->request
, entry
);
842 return entry
->result
;
846 ACLExternal::match(ACLChecklist
*checklist
)
848 allow_t answer
= aclMatchExternal(data
, Filled(checklist
));
850 // convert to tri-state ACL match 1,0,-1
856 return 0; // non-match
859 case ACCESS_AUTH_REQUIRED
:
861 // If the answer is not allowed or denied (matches/not matches) and
862 // async authentication is not in progress, then we are done.
863 if (checklist
->keepMatching())
864 checklist
->markFinished(answer
, "aclMatchExternal exception");
870 ACLExternal::dump() const
872 external_acl_data
const *acl
= data
;
874 rv
.push_back(SBuf(acl
->def
->name
));
876 for (wordlist
*arg
= acl
->arguments
; arg
; arg
= arg
->next
) {
878 s
.Printf(" %s", arg
->key
);
885 /******************************************************************
890 external_acl_cache_touch(external_acl
* def
, const ExternalACLEntryPointer
&entry
)
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))
896 dlinkDelete(&entry
->lru
, &def
->lru_list
);
897 ExternalACLEntry
*e
= const_cast<ExternalACLEntry
*>(entry
.getRaw()); // XXX: make hash a std::map of Pointer.
898 dlinkAdd(e
, &entry
->lru
, &def
->lru_list
);
903 external_acl_ssl_get_user_attribute(const ACLFilledChecklist
&ch
, const char *attr
)
905 if (ch
.conn() != NULL
&& Comm::IsConnOpen(ch
.conn()->clientConnection
)) {
906 if (SSL
*ssl
= fd_table
[ch
.conn()->clientConnection
->fd
].ssl
)
907 return sslGetUserAttribute(ssl
, attr
);
914 makeExternalAclKey(ACLFilledChecklist
* ch
, external_acl_data
* acl_data
)
920 HttpRequest
*request
= ch
->request
;
921 HttpReply
*reply
= ch
->reply
;
923 bool data_used
= false;
925 for (external_acl_format::Pointer format
= acl_data
->def
->format
; format
!= NULL
; format
= format
->next
) {
926 const char *str
= NULL
;
929 switch (format
->type
) {
931 case Format::LFT_USER_LOGIN
:
932 // if this ACL line was the cause of credentials fetch
933 // they may not already be in the checklist
934 if (ch
->auth_user_request
== NULL
&& ch
->request
)
935 ch
->auth_user_request
= ch
->request
->auth_user_request
;
937 if (ch
->auth_user_request
!= NULL
)
938 str
= ch
->auth_user_request
->username();
942 case Format::LFT_USER_IDENT
:
946 // if we fail to go async, we still return NULL and the caller
947 // will detect the failure in ACLExternal::match().
948 (void)ch
->goAsync(IdentLookup::Instance());
955 case Format::LFT_CLIENT_IP_ADDRESS
:
956 str
= ch
->src_addr
.toStr(buf
,sizeof(buf
));
959 case Format::LFT_CLIENT_PORT
:
960 snprintf(buf
, sizeof(buf
), "%d", request
->client_addr
.port());
965 case Format::LFT_EXT_ACL_CLIENT_EUI48
:
966 if (request
->clientConnectionManager
.valid() && request
->clientConnectionManager
->clientConnection
!= NULL
&&
967 request
->clientConnectionManager
->clientConnection
->remoteEui48
.encode(buf
, sizeof(buf
)))
971 case Format::LFT_EXT_ACL_CLIENT_EUI64
:
972 if (request
->clientConnectionManager
.valid() && request
->clientConnectionManager
->clientConnection
!= NULL
&&
973 request
->clientConnectionManager
->clientConnection
->remoteEui64
.encode(buf
, sizeof(buf
)))
978 case Format::LFT_LOCAL_LISTENING_IP
:
979 str
= request
->my_addr
.toStr(buf
, sizeof(buf
));
982 case Format::LFT_LOCAL_LISTENING_PORT
:
983 snprintf(buf
, sizeof(buf
), "%d", request
->my_addr
.port());
987 case Format::LFT_CLIENT_REQ_URI
:
988 snprintf(buf
, sizeof(buf
), SQUIDSBUFPH
, SQUIDSBUFPRINT(request
->effectiveRequestUri()));
992 case Format::LFT_CLIENT_REQ_URLDOMAIN
:
993 str
= request
->url
.host();
996 case Format::LFT_CLIENT_REQ_URLSCHEME
:
997 str
= request
->url
.getScheme().c_str();
1000 case Format::LFT_CLIENT_REQ_URLPORT
:
1001 snprintf(buf
, sizeof(buf
), "%u", request
->url
.port());
1005 case Format::LFT_CLIENT_REQ_URLPATH
: {
1006 SBuf tmp
= request
->url
.path();
1011 case Format::LFT_CLIENT_REQ_METHOD
: {
1012 const SBuf
&s
= request
->method
.image();
1013 sb
.append(s
.rawContent(), s
.length());
1015 str
= sb
.termedBuf();
1018 case Format::LFT_ADAPTED_REQUEST_HEADER
:
1019 if (format
->header_id
== Http::HdrType::BAD_HDR
)
1020 sb
= request
->header
.getByName(format
->header
);
1022 sb
= request
->header
.getStrOrList(format
->header_id
);
1023 str
= sb
.termedBuf();
1026 case Format::LFT_ADAPTED_REQUEST_HEADER_ELEM
:
1027 if (format
->header_id
== Http::HdrType::BAD_HDR
)
1028 sb
= request
->header
.getByNameListMember(format
->header
, format
->member
, format
->separator
);
1030 sb
= request
->header
.getListMember(format
->header_id
, format
->member
, format
->separator
);
1031 str
= sb
.termedBuf();
1034 case Format::LFT_REPLY_HEADER
:
1036 if (format
->header_id
== Http::HdrType::BAD_HDR
)
1037 sb
= reply
->header
.getByName(format
->header
);
1039 sb
= reply
->header
.getStrOrList(format
->header_id
);
1040 str
= sb
.termedBuf();
1044 case Format::LFT_REPLY_HEADER_ELEM
:
1046 if (format
->header_id
== Http::HdrType::BAD_HDR
)
1047 sb
= reply
->header
.getByNameListMember(format
->header
, format
->member
, format
->separator
);
1049 sb
= reply
->header
.getListMember(format
->header_id
, format
->member
, format
->separator
);
1050 str
= sb
.termedBuf();
1056 case Format::LFT_EXT_ACL_USER_CERT_RAW
:
1058 if (ch
->conn() != NULL
&& Comm::IsConnOpen(ch
->conn()->clientConnection
)) {
1059 if (auto ssl
= fd_table
[ch
->conn()->clientConnection
->fd
].ssl
)
1060 str
= sslGetUserCertificatePEM(ssl
);
1065 case Format::LFT_EXT_ACL_USER_CERTCHAIN_RAW
:
1067 if (ch
->conn() != NULL
&& Comm::IsConnOpen(ch
->conn()->clientConnection
)) {
1068 if (auto ssl
= fd_table
[ch
->conn()->clientConnection
->fd
].ssl
)
1069 str
= sslGetUserCertificateChainPEM(ssl
);
1074 case Format::LFT_EXT_ACL_USER_CERT
:
1076 str
= external_acl_ssl_get_user_attribute(*ch
, format
->header
);
1079 case Format::LFT_EXT_ACL_USER_CA_CERT
:
1081 if (ch
->conn() != NULL
&& Comm::IsConnOpen(ch
->conn()->clientConnection
)) {
1082 if (auto ssl
= fd_table
[ch
->conn()->clientConnection
->fd
].ssl
)
1083 str
= sslGetCAAttribute(ssl
, format
->header
);
1088 case Format::LFT_SSL_CLIENT_SNI
:
1089 if (ch
->conn() != NULL
) {
1090 if (Ssl::ServerBump
* srvBump
= ch
->conn()->serverBump()) {
1091 if (!srvBump
->clientSni
.isEmpty())
1092 str
= srvBump
->clientSni
.c_str();
1097 case Format::LFT_SSL_SERVER_CERT_SUBJECT
:
1098 case Format::LFT_SSL_SERVER_CERT_ISSUER
: {
1099 X509
*serverCert
= NULL
;
1100 if (ch
->serverCert
.get())
1101 serverCert
= ch
->serverCert
.get();
1102 else if (ch
->conn() && ch
->conn()->serverBump())
1103 serverCert
= ch
->conn()->serverBump()->serverCert
.get();
1106 if (format
->type
== Format::LFT_SSL_SERVER_CERT_SUBJECT
)
1107 str
= Ssl::GetX509UserAttribute(serverCert
, "DN");
1109 str
= Ssl::GetX509CAAttribute(serverCert
, "DN");
1116 case Format::LFT_USER_EXTERNAL
:
1117 str
= request
->extacl_user
.termedBuf();
1120 case Format::LFT_USER_NAME
:
1121 /* find the first available name from various sources */
1123 if (ch
->auth_user_request
!= NULL
)
1124 str
= ch
->auth_user_request
->username();
1125 if ((!str
|| !*str
) &&
1126 (request
->extacl_user
.size() > 0 && request
->extacl_user
[0] != '-'))
1127 str
= request
->extacl_user
.termedBuf();
1131 str
= external_acl_ssl_get_user_attribute(*ch
, "CN");
1138 case Format::LFT_EXT_LOG
:
1139 str
= request
->extacl_log
.termedBuf();
1141 case Format::LFT_TAG
:
1142 str
= request
->tag
.termedBuf();
1144 case Format::LFT_EXT_ACL_NAME
:
1145 str
= acl_data
->name
;
1147 case Format::LFT_EXT_ACL_DATA
:
1149 for (arg
= acl_data
->arguments
; arg
; arg
= arg
->next
) {
1153 if (acl_data
->def
->quote
== external_acl::QUOTE_METHOD_URL
) {
1154 const char *quoted
= rfc1738_escape(arg
->key
);
1155 sb
.append(quoted
, strlen(quoted
));
1159 strwordquote(&mb2
, arg
->key
);
1160 sb
.append(mb2
.buf
, mb2
.size
);
1167 case Format::LFT_PERCENT
:
1172 // TODO: replace this function with Format::assemble()
1173 // For now die on unsupported logformat codes.
1174 fatalf("ERROR: unknown external_acl_type format %u", (uint8_t)format
->type
);
1188 if (acl_data
->def
->quote
== external_acl::QUOTE_METHOD_URL
) {
1189 const char *quoted
= rfc1738_escape(str
);
1190 mb
.append(quoted
, strlen(quoted
));
1192 strwordquote(&mb
, str
);
1201 for (arg
= acl_data
->arguments
; arg
; arg
= arg
->next
) {
1205 if (acl_data
->def
->quote
== external_acl::QUOTE_METHOD_URL
) {
1206 const char *quoted
= rfc1738_escape(arg
->key
);
1207 mb
.append(quoted
, strlen(quoted
));
1209 strwordquote(&mb
, arg
->key
);
1220 external_acl_entry_expired(external_acl
* def
, const ExternalACLEntryPointer
&entry
)
1222 if (def
->cache_size
<= 0)
1225 if (entry
->date
+ (entry
->result
== 1 ? def
->ttl
: def
->negative_ttl
) < squid_curtime
)
1232 external_acl_grace_expired(external_acl
* def
, const ExternalACLEntryPointer
&entry
)
1234 if (def
->cache_size
<= 0)
1238 ttl
= entry
->result
== 1 ? def
->ttl
: def
->negative_ttl
;
1239 ttl
= (ttl
* (100 - def
->grace
)) / 100;
1241 if (entry
->date
+ ttl
<= squid_curtime
)
1247 static ExternalACLEntryPointer
1248 external_acl_cache_add(external_acl
* def
, const char *key
, ExternalACLEntryData
const & data
)
1250 ExternalACLEntryPointer entry
;
1252 // do not bother caching this result if TTL is going to expire it immediately
1253 if (def
->cache_size
<= 0 || (def
->ttl
<= 0 && data
.result
== 1) || (def
->negative_ttl
<= 0 && data
.result
!= 1)) {
1255 entry
= new ExternalACLEntry
;
1256 entry
->key
= xstrdup(key
);
1257 entry
->update(data
);
1262 entry
= static_cast<ExternalACLEntry
*>(hash_lookup(def
->cache
, key
));
1263 debugs(82, 2, "external_acl_cache_add: Adding '" << key
<< "' = " << data
.result
);
1265 if (entry
!= NULL
) {
1266 debugs(82, 3, "updating existing entry");
1267 entry
->update(data
);
1268 external_acl_cache_touch(def
, entry
);
1272 entry
= new ExternalACLEntry
;
1273 entry
->key
= xstrdup(key
);
1274 entry
->update(data
);
1282 external_acl_cache_delete(external_acl
* def
, const ExternalACLEntryPointer
&entry
)
1284 assert(entry
!= NULL
);
1285 assert(def
->cache_size
> 0 && entry
->def
== def
);
1286 ExternalACLEntry
*e
= const_cast<ExternalACLEntry
*>(entry
.getRaw()); // XXX: make hash a std::map of Pointer.
1287 hash_remove_link(def
->cache
, e
);
1288 dlinkDelete(&e
->lru
, &def
->lru_list
);
1289 e
->unlock(); // unlock on behalf of the hash
1290 def
->cache_entries
-= 1;
1293 /******************************************************************
1294 * external_acl helpers
1297 class externalAclState
1299 CBDATA_CLASS(externalAclState
);
1302 externalAclState(external_acl
* aDef
, const char *aKey
) :
1304 callback_data(NULL
),
1306 def(cbdataReference(aDef
)),
1309 ~externalAclState();
1312 void *callback_data
;
1316 externalAclState
*queue
;
1319 CBDATA_CLASS_INIT(externalAclState
);
1321 externalAclState::~externalAclState()
1324 cbdataReferenceDone(callback_data
);
1325 cbdataReferenceDone(def
);
1329 * The helper program receives queries on stdin, one
1330 * per line, and must return the result on on stdout
1332 * General result syntax:
1334 * OK/ERR keyword=value ...
1338 * user= The users name (login)
1339 * message= Message describing the reason
1340 * tag= A string tag to be applied to the request that triggered the acl match.
1341 * applies to both OK and ERR responses.
1342 * Won't override existing request tags.
1343 * log= A string to be used in access logging
1345 * Other keywords may be added to the protocol later
1347 * value needs to be URL-encoded or enclosed in double quotes (")
1348 * with \-escaping on any whitespace, quotes, or slashes (\).
1351 externalAclHandleReply(void *data
, const Helper::Reply
&reply
)
1353 externalAclState
*state
= static_cast<externalAclState
*>(data
);
1354 externalAclState
*next
;
1355 ExternalACLEntryData entryData
;
1356 entryData
.result
= ACCESS_DENIED
;
1358 debugs(82, 2, HERE
<< "reply=" << reply
);
1360 if (reply
.result
== Helper::Okay
)
1361 entryData
.result
= ACCESS_ALLOWED
;
1362 // XXX: handle other non-DENIED results better
1364 // XXX: make entryData store a proper Helper::Reply object instead of copying.
1366 entryData
.notes
.append(&reply
.notes
);
1368 const char *label
= reply
.notes
.findFirst("tag");
1369 if (label
!= NULL
&& *label
!= '\0')
1370 entryData
.tag
= label
;
1372 label
= reply
.notes
.findFirst("message");
1373 if (label
!= NULL
&& *label
!= '\0')
1374 entryData
.message
= label
;
1376 label
= reply
.notes
.findFirst("log");
1377 if (label
!= NULL
&& *label
!= '\0')
1378 entryData
.log
= label
;
1381 label
= reply
.notes
.findFirst("user");
1382 if (label
!= NULL
&& *label
!= '\0')
1383 entryData
.user
= label
;
1385 label
= reply
.notes
.findFirst("password");
1386 if (label
!= NULL
&& *label
!= '\0')
1387 entryData
.password
= label
;
1390 dlinkDelete(&state
->list
, &state
->def
->queue
);
1392 ExternalACLEntryPointer entry
;
1393 if (cbdataReferenceValid(state
->def
)) {
1394 // only cache OK and ERR results.
1395 if (reply
.result
== Helper::Okay
|| reply
.result
== Helper::Error
)
1396 entry
= external_acl_cache_add(state
->def
, state
->key
, entryData
);
1398 const ExternalACLEntryPointer oldentry
= static_cast<ExternalACLEntry
*>(hash_lookup(state
->def
->cache
, state
->key
));
1400 if (oldentry
!= NULL
)
1401 external_acl_cache_delete(state
->def
, oldentry
);
1407 if (state
->callback
&& cbdataReferenceValidDone(state
->callback_data
, &cbdata
))
1408 state
->callback(cbdata
, entry
);
1410 next
= state
->queue
;
1411 state
->queue
= NULL
;
1420 ACLExternal::ExternalAclLookup(ACLChecklist
*checklist
, ACLExternal
* me
)
1422 ExternalACLLookup::Start(checklist
, me
->data
, false);
1426 ExternalACLLookup::Start(ACLChecklist
*checklist
, external_acl_data
*acl
, bool inBackground
)
1428 external_acl
*def
= acl
->def
;
1430 ACLFilledChecklist
*ch
= Filled(checklist
);
1431 const char *key
= makeExternalAclKey(ch
, acl
);
1432 assert(key
); // XXX: will fail if EXT_ACL_IDENT case needs an async lookup
1434 debugs(82, 2, HERE
<< (inBackground
? "bg" : "fg") << " lookup in '" <<
1435 def
->name
<< "' for '" << key
<< "'");
1437 /* Check for a pending lookup to hook into */
1438 // only possible if we are caching results.
1439 externalAclState
*oldstate
= NULL
;
1440 if (def
->cache_size
> 0) {
1441 for (dlink_node
*node
= def
->queue
.head
; node
; node
= node
->next
) {
1442 externalAclState
*oldstatetmp
= static_cast<externalAclState
*>(node
->data
);
1444 if (strcmp(key
, oldstatetmp
->key
) == 0) {
1445 oldstate
= oldstatetmp
;
1451 // A background refresh has no need to piggiback on a pending request:
1452 // When the pending request completes, the cache will be refreshed anyway.
1453 if (oldstate
&& inBackground
) {
1454 debugs(82, 7, HERE
<< "'" << def
->name
<< "' queue is already being refreshed (ch=" << ch
<< ")");
1458 externalAclState
*state
= new externalAclState(def
, key
);
1460 if (!inBackground
) {
1461 state
->callback
= &ExternalACLLookup::LookupDone
;
1462 state
->callback_data
= cbdataReference(checklist
);
1466 /* Hook into pending lookup */
1467 state
->queue
= oldstate
->queue
;
1468 oldstate
->queue
= state
;
1470 /* No pending lookup found. Sumbit to helper */
1474 buf
.appendf("%s\n", key
);
1475 debugs(82, 4, "externalAclLookup: looking up for '" << key
<< "' in '" << def
->name
<< "'.");
1477 if (!def
->theHelper
->trySubmit(buf
.buf
, externalAclHandleReply
, state
)) {
1478 debugs(82, 7, HERE
<< "'" << def
->name
<< "' submit to helper failed");
1479 assert(inBackground
); // or the caller should have checked
1484 dlinkAdd(state
, &state
->list
, &def
->queue
);
1489 debugs(82, 4, "externalAclLookup: will wait for the result of '" << key
<<
1490 "' in '" << def
->name
<< "' (ch=" << ch
<< ").");
1494 externalAclStats(StoreEntry
* sentry
)
1496 for (external_acl
*p
= Config
.externalAclHelperList
; p
; p
= p
->next
) {
1497 storeAppendPrintf(sentry
, "External ACL Statistics: %s\n", p
->name
);
1498 storeAppendPrintf(sentry
, "Cache size: %d\n", p
->cache
->count
);
1499 assert(p
->theHelper
);
1500 p
->theHelper
->packStatsInto(sentry
);
1501 storeAppendPrintf(sentry
, "\n");
1506 externalAclRegisterWithCacheManager(void)
1508 Mgr::RegisterAction("external_acl",
1509 "External ACL stats",
1510 externalAclStats
, 0, 1);
1514 externalAclInit(void)
1516 for (external_acl
*p
= Config
.externalAclHelperList
; p
; p
= p
->next
) {
1518 p
->cache
= hash_create((HASHCMP
*) strcmp
, hashPrime(1024), hash4
);
1521 p
->theHelper
= new helper(p
->name
);
1523 p
->theHelper
->cmdline
= p
->cmdline
;
1525 p
->theHelper
->childs
.updateLimits(p
->children
);
1527 p
->theHelper
->ipc_type
= IPC_TCP_SOCKET
;
1529 p
->theHelper
->addr
= p
->local_addr
;
1531 helperOpenServers(p
->theHelper
);
1534 externalAclRegisterWithCacheManager();
1538 externalAclShutdown(void)
1542 for (p
= Config
.externalAclHelperList
; p
; p
= p
->next
) {
1543 helperShutdown(p
->theHelper
);
1547 ExternalACLLookup
ExternalACLLookup::instance_
;
1549 ExternalACLLookup::Instance()
1555 ExternalACLLookup::checkForAsync(ACLChecklist
*checklist
)const
1557 /* TODO: optimise this - we probably have a pointer to this
1558 * around somewhere */
1559 ACL
*acl
= ACL::FindByName(AclMatchedName
);
1561 ACLExternal
*me
= dynamic_cast<ACLExternal
*> (acl
);
1563 ACLExternal::ExternalAclLookup(checklist
, me
);
1566 /// Called when an async lookup returns
1568 ExternalACLLookup::LookupDone(void *data
, const ExternalACLEntryPointer
&result
)
1570 ACLFilledChecklist
*checklist
= Filled(static_cast<ACLChecklist
*>(data
));
1571 checklist
->extacl_entry
= result
;
1572 checklist
->resumeNonBlockingCheck(ExternalACLLookup::Instance());
1575 /* This registers "external" in the registry. To do dynamic definitions
1576 * of external ACL's, rather than a static prototype, have a Prototype instance
1577 * prototype in the class that defines each external acl 'class'.
1578 * Then, then the external acl instance is created, it self registers under
1580 * Be sure that clone is fully functional for that acl class though!
1582 ACL::Prototype
ACLExternal::RegistryProtoype(&ACLExternal::RegistryEntry_
, "external");
1584 ACLExternal
ACLExternal::RegistryEntry_("external");
1587 ACLExternal::clone() const
1589 return new ACLExternal(*this);
1592 ACLExternal::ACLExternal(char const *theClass
) : data(NULL
), class_(xstrdup(theClass
))
1595 ACLExternal::ACLExternal(ACLExternal
const & old
) : data(NULL
), class_(old
.class_
? xstrdup(old
.class_
) : NULL
)
1597 /* we don't have copy constructors for the data yet */
1602 ACLExternal::typeString() const
1608 ACLExternal::isProxyAuth() const
1611 return data
->def
->require_auth
;