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 "HttpHeaderTools.h"
24 #include "HttpReply.h"
25 #include "HttpRequest.h"
28 #include "mgr/Registration.h"
30 #include "SquidConfig.h"
31 #include "SquidString.h"
32 #include "SquidTime.h"
38 #include "ssl/ServerBump.h"
39 #include "ssl/support.h"
43 #include "auth/Gadgets.h"
44 #include "auth/UserRequest.h"
47 #include "ident/AclIdent.h"
50 #ifndef DEFAULT_EXTERNAL_ACL_TTL
51 #define DEFAULT_EXTERNAL_ACL_TTL 1 * 60 * 60
53 #ifndef DEFAULT_EXTERNAL_ACL_CHILDREN
54 #define DEFAULT_EXTERNAL_ACL_CHILDREN 5
57 typedef struct _external_acl_format external_acl_format
;
59 static char *makeExternalAclKey(ACLFilledChecklist
* ch
, external_acl_data
* acl_data
);
60 static void external_acl_cache_delete(external_acl
* def
, external_acl_entry
* entry
);
61 static int external_acl_entry_expired(external_acl
* def
, external_acl_entry
* entry
);
62 static int external_acl_grace_expired(external_acl
* def
, external_acl_entry
* entry
);
63 static void external_acl_cache_touch(external_acl
* def
, external_acl_entry
* entry
);
64 static external_acl_entry
*external_acl_cache_add(external_acl
* def
, const char *key
, ExternalACLEntryData
const &data
);
66 /******************************************************************
67 * external_acl directive
76 void add(ExternalACLEntry
*);
88 external_acl_format
*format
;
92 HelperChildConfig children
;
108 * Configuration flag. May only be altered by the configuration parser.
110 * Indicates that all uses of this external_acl_type helper require authentication
111 * details to be processed. If none are available its a fail match.
117 QUOTE_METHOD_SHELL
= 1,
121 Ip::Address local_addr
;
124 struct _external_acl_format
{
125 Format::ByteCode_t type
;
126 external_acl_format
*next
;
130 http_hdr_type header_id
;
133 /* FIXME: These are not really cbdata, but it is an easy way
134 * to get them pooled, refcounted, accounted and freed properly...
136 CBDATA_TYPE(external_acl
);
137 CBDATA_TYPE(external_acl_format
);
140 free_external_acl_format(void *data
)
142 external_acl_format
*p
= static_cast<external_acl_format
*>(data
);
143 safe_free(p
->header
);
147 free_external_acl(void *data
)
149 external_acl
*p
= static_cast<external_acl
*>(data
);
153 external_acl_format
*f
= p
->format
;
158 wordlistDestroy(&p
->cmdline
);
161 helperShutdown(p
->theHelper
);
166 while (p
->lru_list
.tail
)
167 external_acl_cache_delete(p
, static_cast<external_acl_entry
*>(p
->lru_list
.tail
->data
));
169 hashFreeMemory(p
->cache
);
173 * Parse the External ACL format %<{.*} and %>{.*} token(s) to pass a specific
174 * request or reply header to external helper.
176 \param header - the token being parsed (without the identifying prefix)
177 \param type - format enum identifier for this element, pulled from identifying prefix
178 \param format - structure to contain all the info about this format element.
181 parse_header_token(external_acl_format
*format
, char *header
, const Format::ByteCode_t type
)
186 /** Cut away the closing brace */
187 end
= strchr(header
, '}');
188 if (end
&& strlen(end
) == 1)
193 member
= strchr(header
, ':');
196 /* Split in header and member */
200 if (!xisalnum(*member
)) {
201 format
->separator
= *member
;
204 format
->separator
= ',';
207 format
->member
= xstrdup(member
);
209 if (type
== Format::LFT_ADAPTED_REQUEST_HEADER
)
210 format
->type
= Format::LFT_ADAPTED_REQUEST_HEADER_ELEM
;
212 format
->type
= Format::LFT_REPLY_HEADER_ELEM
;
218 format
->header
= xstrdup(header
);
219 format
->header_id
= httpHeaderIdByNameDef(header
, strlen(header
));
223 parse_externalAclHelper(external_acl
** list
)
227 external_acl_format
**p
;
229 CBDATA_INIT_TYPE_FREECB(external_acl
, free_external_acl
);
230 CBDATA_INIT_TYPE_FREECB(external_acl_format
, free_external_acl_format
);
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
;
323 external_acl_format
*format
;
325 /* stop on first non-format token found */
330 format
= cbdataAlloc(external_acl_format
);
332 if (strncmp(token
, "%{", 2) == 0) {
333 // deprecated. but assume the old configs all referred to request headers.
334 debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT
), "WARNING: external_acl_type format %{...} is being replaced by %>ha{...} for : " << token
);
335 parse_header_token(format
, (token
+2), Format::LFT_ADAPTED_REQUEST_HEADER
);
336 } else if (strncmp(token
, "%>{", 3) == 0) {
337 debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT
), "WARNING: external_acl_type format %>{...} is being replaced by %>ha{...} for : " << token
);
338 parse_header_token(format
, (token
+3), Format::LFT_ADAPTED_REQUEST_HEADER
);
339 } else if (strncmp(token
, "%>ha{", 5) == 0) {
340 parse_header_token(format
, (token
+3), Format::LFT_ADAPTED_REQUEST_HEADER
);
341 } else if (strncmp(token
, "%<{", 3) == 0) {
342 debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT
), "WARNING: external_acl_type format %<{...} is being replaced by %<h{...} for : " << token
);
343 parse_header_token(format
, (token
+3), Format::LFT_REPLY_HEADER
);
344 } else if (strncmp(token
, "%<h{", 4) == 0) {
345 parse_header_token(format
, (token
+3), Format::LFT_REPLY_HEADER
);
347 } else if (strcmp(token
, "%LOGIN") == 0 || strcmp(token
, "%ul") == 0) {
348 format
->type
= Format::LFT_USER_LOGIN
;
349 a
->require_auth
= true;
353 else if (strcmp(token
, "%IDENT") == 0 || strcmp(token
, "%ui") == 0)
354 format
->type
= Format::LFT_USER_IDENT
;
356 else if (strcmp(token
, "%SRC") == 0 || strcmp(token
, "%>a") == 0)
357 format
->type
= Format::LFT_CLIENT_IP_ADDRESS
;
358 else if (strcmp(token
, "%SRCPORT") == 0 || strcmp(token
, "%>p") == 0)
359 format
->type
= Format::LFT_CLIENT_PORT
;
361 else if (strcmp(token
, "%SRCEUI48") == 0)
362 format
->type
= Format::LFT_EXT_ACL_CLIENT_EUI48
;
363 else if (strcmp(token
, "%SRCEUI64") == 0)
364 format
->type
= Format::LFT_EXT_ACL_CLIENT_EUI64
;
366 else if (strcmp(token
, "%MYADDR") == 0 || strcmp(token
, "%la") == 0)
367 format
->type
= Format::LFT_LOCAL_LISTENING_IP
;
368 else if (strcmp(token
, "%MYPORT") == 0 || strcmp(token
, "%lp") == 0)
369 format
->type
= Format::LFT_LOCAL_LISTENING_PORT
;
370 else if (strcmp(token
, "%URI") == 0 || strcmp(token
, "%>ru") == 0)
371 format
->type
= Format::LFT_CLIENT_REQ_URI
;
372 else if (strcmp(token
, "%DST") == 0 || strcmp(token
, "%>rd") == 0)
373 format
->type
= Format::LFT_CLIENT_REQ_URLDOMAIN
;
374 else if (strcmp(token
, "%PROTO") == 0 || strcmp(token
, "%>rs") == 0)
375 format
->type
= Format::LFT_CLIENT_REQ_URLSCHEME
;
376 else if (strcmp(token
, "%PORT") == 0) // XXX: add a logformat token
377 format
->type
= Format::LFT_CLIENT_REQ_URLPORT
;
378 else if (strcmp(token
, "%PATH") == 0 || strcmp(token
, "%>rp") == 0)
379 format
->type
= Format::LFT_CLIENT_REQ_URLPATH
;
380 else if (strcmp(token
, "%METHOD") == 0 || strcmp(token
, "%>rm") == 0)
381 format
->type
= Format::LFT_CLIENT_REQ_METHOD
;
383 else if (strcmp(token
, "%USER_CERT") == 0)
384 format
->type
= Format::LFT_EXT_ACL_USER_CERT_RAW
;
385 else if (strcmp(token
, "%USER_CERTCHAIN") == 0)
386 format
->type
= Format::LFT_EXT_ACL_USER_CERTCHAIN_RAW
;
387 else if (strncmp(token
, "%USER_CERT_", 11) == 0) {
388 format
->type
= Format::LFT_EXT_ACL_USER_CERT
;
389 format
->header
= xstrdup(token
+ 11);
390 } else if (strncmp(token
, "%USER_CA_CERT_", 14) == 0) {
391 format
->type
= Format::LFT_EXT_ACL_USER_CA_CERT
;
392 format
->header
= xstrdup(token
+ 14);
393 } else if (strncmp(token
, "%CA_CERT_", 9) == 0) {
394 debugs(82, DBG_PARSE_NOTE(DBG_IMPORTANT
), "WARNING: external_acl_type %CA_CERT_* code is obsolete. Use %USER_CA_CERT_* instead");
395 format
->type
= Format::LFT_EXT_ACL_USER_CA_CERT
;
396 format
->header
= xstrdup(token
+ 9);
397 } else if (strcmp(token
, "%ssl::>sni") == 0)
398 format
->type
= Format::LFT_SSL_CLIENT_SNI
;
399 else if (strcmp(token
, "%ssl::<cert_subject") == 0)
400 format
->type
= Format::LFT_SSL_SERVER_CERT_SUBJECT
;
401 else if (strcmp(token
, "%ssl::<cert_issuer") == 0)
402 format
->type
= Format::LFT_SSL_SERVER_CERT_ISSUER
;
405 else if (strcmp(token
, "%EXT_USER") == 0 || strcmp(token
, "%ue") == 0)
406 format
->type
= Format::LFT_USER_EXTERNAL
;
408 else if (strcmp(token
, "%EXT_LOG") == 0 || strcmp(token
, "%ea") == 0)
409 format
->type
= Format::LFT_EXT_LOG
;
410 else if (strcmp(token
, "%TAG") == 0 || strcmp(token
, "%et") == 0)
411 format
->type
= Format::LFT_TAG
;
412 else if (strcmp(token
, "%ACL") == 0)
413 format
->type
= Format::LFT_EXT_ACL_NAME
;
414 else if (strcmp(token
, "%DATA") == 0)
415 format
->type
= Format::LFT_EXT_ACL_DATA
;
416 else if (strcmp(token
, "%%") == 0)
417 format
->type
= Format::LFT_PERCENT
;
419 debugs(0, DBG_CRITICAL
, "ERROR: Unknown Format token " << token
);
425 token
= ConfigParser::NextToken();
428 /* There must be at least one format token */
436 wordlistAdd(&a
->cmdline
, token
);
439 parse_wordlist(&a
->cmdline
);
442 list
= &(*list
)->next
;
448 dump_externalAclHelper(StoreEntry
* sentry
, const char *name
, const external_acl
* list
)
450 const external_acl
*node
;
451 const external_acl_format
*format
;
452 const wordlist
*word
;
454 for (node
= list
; node
; node
= node
->next
) {
455 storeAppendPrintf(sentry
, "%s %s", name
, node
->name
);
457 if (!node
->local_addr
.isIPv6())
458 storeAppendPrintf(sentry
, " ipv4");
460 storeAppendPrintf(sentry
, " ipv6");
462 if (node
->ttl
!= DEFAULT_EXTERNAL_ACL_TTL
)
463 storeAppendPrintf(sentry
, " ttl=%d", node
->ttl
);
465 if (node
->negative_ttl
!= node
->ttl
)
466 storeAppendPrintf(sentry
, " negative_ttl=%d", node
->negative_ttl
);
469 storeAppendPrintf(sentry
, " grace=%d", node
->grace
);
471 if (node
->children
.n_max
!= DEFAULT_EXTERNAL_ACL_CHILDREN
)
472 storeAppendPrintf(sentry
, " children-max=%d", node
->children
.n_max
);
474 if (node
->children
.n_startup
!= 1)
475 storeAppendPrintf(sentry
, " children-startup=%d", node
->children
.n_startup
);
477 if (node
->children
.n_idle
!= (node
->children
.n_max
+ node
->children
.n_startup
) )
478 storeAppendPrintf(sentry
, " children-idle=%d", node
->children
.n_idle
);
480 if (node
->children
.concurrency
)
481 storeAppendPrintf(sentry
, " concurrency=%d", node
->children
.concurrency
);
484 storeAppendPrintf(sentry
, " cache=%d", node
->cache_size
);
486 if (node
->quote
== external_acl::QUOTE_METHOD_SHELL
)
487 storeAppendPrintf(sentry
, " protocol=2.5");
489 for (format
= node
->format
; format
; format
= format
->next
) {
490 switch (format
->type
) {
492 case Format::LFT_ADAPTED_REQUEST_HEADER
:
493 storeAppendPrintf(sentry
, " %%>ha{%s}", format
->header
);
496 case Format::LFT_ADAPTED_REQUEST_HEADER_ELEM
:
497 storeAppendPrintf(sentry
, " %%>ha{%s:%s}", format
->header
, format
->member
);
500 case Format::LFT_REPLY_HEADER
:
501 storeAppendPrintf(sentry
, " %%<h{%s}", format
->header
);
504 case Format::LFT_REPLY_HEADER_ELEM
:
505 storeAppendPrintf(sentry
, " %%<h{%s:%s}", format
->header
, format
->member
);
508 #define DUMP_EXT_ACL_TYPE_FMT(a, fmt, ...) \
509 case Format::LFT_##a: \
510 storeAppendPrintf(sentry, fmt, ##__VA_ARGS__); \
513 DUMP_EXT_ACL_TYPE_FMT(USER_LOGIN
," %%ul");
517 DUMP_EXT_ACL_TYPE_FMT(USER_IDENT
," %%ui");
519 DUMP_EXT_ACL_TYPE_FMT(CLIENT_IP_ADDRESS
," %%>a");
520 DUMP_EXT_ACL_TYPE_FMT(CLIENT_PORT
," %%>p");
522 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_CLIENT_EUI48
," %%SRCEUI48");
523 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_CLIENT_EUI64
," %%SRCEUI64");
525 DUMP_EXT_ACL_TYPE_FMT(LOCAL_LISTENING_IP
," %%>la");
526 DUMP_EXT_ACL_TYPE_FMT(LOCAL_LISTENING_PORT
," %%>lp");
527 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URI
," %%>ru");
528 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLDOMAIN
," %%>rd");
529 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLSCHEME
," %%>rs");
530 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLPORT
," %%>rP");
531 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_URLPATH
," %%>rp");
532 DUMP_EXT_ACL_TYPE_FMT(CLIENT_REQ_METHOD
," %%>rm");
534 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERT_RAW
, " %%USER_CERT_RAW");
535 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERTCHAIN_RAW
, " %%USER_CERTCHAIN_RAW");
536 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CERT
, " %%USER_CERT_%s", format
->header
);
537 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_USER_CA_CERT
, " %%USER_CA_CERT_%s", format
->header
);
538 DUMP_EXT_ACL_TYPE_FMT(SSL_CLIENT_SNI
, "%%ssl::>sni");
539 DUMP_EXT_ACL_TYPE_FMT(SSL_SERVER_CERT_SUBJECT
, "%%ssl::<cert_subject");
540 DUMP_EXT_ACL_TYPE_FMT(SSL_SERVER_CERT_ISSUER
, "%%ssl::<cert_issuer");
543 DUMP_EXT_ACL_TYPE_FMT(USER_EXTERNAL
," %%ue");
545 DUMP_EXT_ACL_TYPE_FMT(EXT_LOG
," %%ea");
546 DUMP_EXT_ACL_TYPE_FMT(TAG
," %%et");
547 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_NAME
," %%ACL");
548 DUMP_EXT_ACL_TYPE_FMT(EXT_ACL_DATA
," %%DATA");
549 DUMP_EXT_ACL_TYPE_FMT(PERCENT
, " %%%%");
551 fatal("unknown external_acl format error");
556 for (word
= node
->cmdline
; word
; word
= word
->next
)
557 storeAppendPrintf(sentry
, " %s", word
->key
);
559 storeAppendPrintf(sentry
, "\n");
564 free_externalAclHelper(external_acl
** list
)
567 external_acl
*node
= *list
;
574 static external_acl
*
575 find_externalAclHelper(const char *name
)
579 for (node
= Config
.externalAclHelperList
; node
; node
= node
->next
) {
580 if (strcmp(node
->name
, name
) == 0)
588 external_acl::add(ExternalACLEntry
*anEntry
)
591 assert (anEntry
->def
== NULL
);
593 hash_join(cache
, anEntry
);
594 dlinkAdd(anEntry
, &anEntry
->lru
, &lru_list
);
599 external_acl::trimCache()
601 if (cache_size
&& cache_entries
>= cache_size
)
602 external_acl_cache_delete(this, static_cast<external_acl_entry
*>(lru_list
.tail
->data
));
605 /******************************************************************
609 struct _external_acl_data
{
615 CBDATA_TYPE(external_acl_data
);
617 free_external_acl_data(void *data
)
619 external_acl_data
*p
= static_cast<external_acl_data
*>(data
);
621 wordlistDestroy(&p
->arguments
);
622 cbdataReferenceDone(p
->def
);
633 CBDATA_INIT_TYPE_FREECB(external_acl_data
, free_external_acl_data
);
635 data
= cbdataAlloc(external_acl_data
);
637 token
= strtokFile();
642 data
->def
= cbdataReference(find_externalAclHelper(token
));
647 // def->name is the name of the external_acl_type.
648 // this is the name of the 'acl' directive being tested
649 data
->name
= xstrdup(AclMatchedName
);
651 while ((token
= strtokFile())) {
652 wordlistAdd(&data
->arguments
, token
);
657 ACLExternal::valid () const
660 if (data
->def
->require_auth
) {
661 if (authenticateSchemeCount() == 0) {
662 debugs(28, DBG_CRITICAL
, "Can't use proxy auth because no authentication schemes were compiled.");
666 if (authenticateActiveSchemeCount() == 0) {
667 debugs(28, DBG_CRITICAL
, "Can't use proxy auth because no authentication schemes are fully configured.");
677 ACLExternal::empty () const
682 ACLExternal::~ACLExternal()
689 copyResultsFromEntry(HttpRequest
*req
, external_acl_entry
*entry
)
693 if (entry
->user
.size())
694 req
->extacl_user
= entry
->user
;
696 if (entry
->password
.size())
697 req
->extacl_passwd
= entry
->password
;
699 if (!req
->tag
.size())
700 req
->tag
= entry
->tag
;
702 if (entry
->log
.size())
703 req
->extacl_log
= entry
->log
;
705 if (entry
->message
.size())
706 req
->extacl_message
= entry
->message
;
711 aclMatchExternal(external_acl_data
*acl
, ACLFilledChecklist
*ch
)
713 debugs(82, 9, HERE
<< "acl=\"" << acl
->def
->name
<< "\"");
714 external_acl_entry
*entry
= ch
->extacl_entry
;
716 external_acl_message
= "MISSING REQUIRED INFORMATION";
719 if (cbdataReferenceValid(entry
) && entry
->def
== acl
->def
) {
720 /* Ours, use it.. if the key matches */
721 const char *key
= makeExternalAclKey(ch
, acl
);
723 return ACCESS_DUNNO
; // insufficent data to continue
724 if (strcmp(key
, (char*)entry
->key
) != 0) {
725 debugs(82, 9, HERE
<< "entry key='" << (char *)entry
->key
<< "', our key='" << key
<< "' dont match. Discarded.");
726 // too bad. need a new lookup.
727 cbdataReferenceDone(ch
->extacl_entry
);
731 /* Not valid, or not ours.. get rid of it */
732 debugs(82, 9, HERE
<< "entry " << entry
<< " not valid or not ours. Discarded.");
734 debugs(82, 9, HERE
<< "entry def=" << entry
->def
<< ", our def=" << acl
->def
);
735 const char *key
= makeExternalAclKey(ch
, acl
); // may be nil
736 debugs(82, 9, HERE
<< "entry key='" << (char *)entry
->key
<< "', our key='" << key
<< "'");
738 cbdataReferenceDone(ch
->extacl_entry
);
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<external_acl_entry
*>(hash_lookup(acl
->def
->cache
, key
));
766 external_acl_entry
*staleEntry
= entry
;
767 if (entry
&& external_acl_entry_expired(acl
->def
, entry
))
770 if (entry
&& 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");
779 debugs(82, 2, HERE
<< "\"" << key
<< "\": entry=@" <<
780 entry
<< ", age=" << (entry
? (long int) squid_curtime
- entry
->date
: 0));
782 if (acl
->def
->theHelper
->stats
.queue_size
< (int)acl
->def
->theHelper
->childs
.n_active
) {
783 debugs(82, 2, HERE
<< "\"" << key
<< "\": queueing a call.");
784 if (!ch
->goAsync(ExternalACLLookup::Instance()))
785 debugs(82, 2, "\"" << key
<< "\": no async support!");
786 debugs(82, 2, HERE
<< "\"" << key
<< "\": return -1.");
787 return ACCESS_DUNNO
; // expired cached or simply absent entry
790 debugs(82, DBG_IMPORTANT
, "WARNING: external ACL '" << acl
->def
->name
<<
791 "' queue overload. Request rejected '" << key
<< "'.");
792 external_acl_message
= "SYSTEM TOO BUSY, TRY AGAIN LATER";
795 debugs(82, DBG_IMPORTANT
, "WARNING: external ACL '" << acl
->def
->name
<<
796 "' queue overload. Using stale result. '" << key
<< "'.");
798 /* Fall thru to processing below */
804 debugs(82, 4, HERE
<< "entry = { date=" <<
805 (long unsigned int) entry
->date
<<
806 ", result=" << entry
->result
<<
807 " tag=" << entry
->tag
<<
808 " log=" << entry
->log
<< " }");
810 debugs(82, 4, HERE
<< "entry user=" << entry
->user
);
813 external_acl_cache_touch(acl
->def
, entry
);
814 external_acl_message
= entry
->message
.termedBuf();
816 debugs(82, 2, HERE
<< acl
->def
->name
<< " = " << entry
->result
);
817 copyResultsFromEntry(ch
->request
, entry
);
818 return entry
->result
;
822 ACLExternal::match(ACLChecklist
*checklist
)
824 allow_t answer
= aclMatchExternal(data
, Filled(checklist
));
826 // convert to tri-state ACL match 1,0,-1
832 return 0; // non-match
835 case ACCESS_AUTH_REQUIRED
:
837 // If the answer is not allowed or denied (matches/not matches) and
838 // async authentication is not in progress, then we are done.
839 if (checklist
->keepMatching())
840 checklist
->markFinished(answer
, "aclMatchExternal exception");
846 ACLExternal::dump() const
848 external_acl_data
const *acl
= data
;
850 rv
.push_back(SBuf(acl
->def
->name
));
852 for (wordlist
*arg
= acl
->arguments
; arg
; arg
= arg
->next
) {
854 s
.Printf(" %s", arg
->key
);
861 /******************************************************************
866 external_acl_cache_touch(external_acl
* def
, external_acl_entry
* entry
)
868 // this must not be done when nothing is being cached.
869 if (def
->cache_size
<= 0 || (def
->ttl
<= 0 && entry
->result
== 1) || (def
->negative_ttl
<= 0 && entry
->result
!= 1))
872 dlinkDelete(&entry
->lru
, &def
->lru_list
);
873 dlinkAdd(entry
, &entry
->lru
, &def
->lru_list
);
877 makeExternalAclKey(ACLFilledChecklist
* ch
, external_acl_data
* acl_data
)
883 external_acl_format
*format
;
884 HttpRequest
*request
= ch
->request
;
885 HttpReply
*reply
= ch
->reply
;
887 bool data_used
= false;
889 for (format
= acl_data
->def
->format
; format
; format
= format
->next
) {
890 const char *str
= NULL
;
893 switch (format
->type
) {
895 case Format::LFT_USER_LOGIN
:
896 // if this ACL line was the cause of credentials fetch
897 // they may not already be in the checklist
898 if (ch
->auth_user_request
== NULL
&& ch
->request
)
899 ch
->auth_user_request
= ch
->request
->auth_user_request
;
901 if (ch
->auth_user_request
!= NULL
)
902 str
= ch
->auth_user_request
->username();
906 case Format::LFT_USER_IDENT
:
910 // if we fail to go async, we still return NULL and the caller
911 // will detect the failure in ACLExternal::match().
912 (void)ch
->goAsync(IdentLookup::Instance());
919 case Format::LFT_CLIENT_IP_ADDRESS
:
920 str
= ch
->src_addr
.toStr(buf
,sizeof(buf
));
923 case Format::LFT_CLIENT_PORT
:
924 snprintf(buf
, sizeof(buf
), "%d", request
->client_addr
.port());
929 case Format::LFT_EXT_ACL_CLIENT_EUI48
:
930 if (request
->clientConnectionManager
.valid() && request
->clientConnectionManager
->clientConnection
!= NULL
&&
931 request
->clientConnectionManager
->clientConnection
->remoteEui48
.encode(buf
, sizeof(buf
)))
935 case Format::LFT_EXT_ACL_CLIENT_EUI64
:
936 if (request
->clientConnectionManager
.valid() && request
->clientConnectionManager
->clientConnection
!= NULL
&&
937 request
->clientConnectionManager
->clientConnection
->remoteEui64
.encode(buf
, sizeof(buf
)))
942 case Format::LFT_LOCAL_LISTENING_IP
:
943 str
= request
->my_addr
.toStr(buf
, sizeof(buf
));
946 case Format::LFT_LOCAL_LISTENING_PORT
:
947 snprintf(buf
, sizeof(buf
), "%d", request
->my_addr
.port());
951 case Format::LFT_CLIENT_REQ_URI
:
952 str
= urlCanonical(request
);
955 case Format::LFT_CLIENT_REQ_URLDOMAIN
:
956 str
= request
->GetHost();
959 case Format::LFT_CLIENT_REQ_URLSCHEME
:
960 str
= request
->url
.getScheme().c_str();
963 case Format::LFT_CLIENT_REQ_URLPORT
:
964 snprintf(buf
, sizeof(buf
), "%d", request
->port
);
968 case Format::LFT_CLIENT_REQ_URLPATH
:
969 str
= request
->urlpath
.termedBuf();
972 case Format::LFT_CLIENT_REQ_METHOD
: {
973 const SBuf
&s
= request
->method
.image();
974 sb
.append(s
.rawContent(), s
.length());
976 str
= sb
.termedBuf();
979 case Format::LFT_ADAPTED_REQUEST_HEADER
:
980 if (format
->header_id
== -1)
981 sb
= request
->header
.getByName(format
->header
);
983 sb
= request
->header
.getStrOrList(format
->header_id
);
984 str
= sb
.termedBuf();
987 case Format::LFT_ADAPTED_REQUEST_HEADER_ELEM
:
988 if (format
->header_id
== -1)
989 sb
= request
->header
.getByNameListMember(format
->header
, format
->member
, format
->separator
);
991 sb
= request
->header
.getListMember(format
->header_id
, format
->member
, format
->separator
);
992 str
= sb
.termedBuf();
995 case Format::LFT_REPLY_HEADER
:
997 if (format
->header_id
== -1)
998 sb
= reply
->header
.getByName(format
->header
);
1000 sb
= reply
->header
.getStrOrList(format
->header_id
);
1001 str
= sb
.termedBuf();
1005 case Format::LFT_REPLY_HEADER_ELEM
:
1007 if (format
->header_id
== -1)
1008 sb
= reply
->header
.getByNameListMember(format
->header
, format
->member
, format
->separator
);
1010 sb
= reply
->header
.getListMember(format
->header_id
, format
->member
, format
->separator
);
1011 str
= sb
.termedBuf();
1017 case Format::LFT_EXT_ACL_USER_CERT_RAW
:
1019 if (ch
->conn() != NULL
&& Comm::IsConnOpen(ch
->conn()->clientConnection
)) {
1020 SSL
*ssl
= fd_table
[ch
->conn()->clientConnection
->fd
].ssl
;
1023 str
= sslGetUserCertificatePEM(ssl
);
1028 case Format::LFT_EXT_ACL_USER_CERTCHAIN_RAW
:
1030 if (ch
->conn() != NULL
&& Comm::IsConnOpen(ch
->conn()->clientConnection
)) {
1031 SSL
*ssl
= fd_table
[ch
->conn()->clientConnection
->fd
].ssl
;
1034 str
= sslGetUserCertificateChainPEM(ssl
);
1039 case Format::LFT_EXT_ACL_USER_CERT
:
1041 if (ch
->conn() != NULL
&& Comm::IsConnOpen(ch
->conn()->clientConnection
)) {
1042 SSL
*ssl
= fd_table
[ch
->conn()->clientConnection
->fd
].ssl
;
1045 str
= sslGetUserAttribute(ssl
, format
->header
);
1050 case Format::LFT_EXT_ACL_USER_CA_CERT
:
1052 if (ch
->conn() != NULL
&& Comm::IsConnOpen(ch
->conn()->clientConnection
)) {
1053 SSL
*ssl
= fd_table
[ch
->conn()->clientConnection
->fd
].ssl
;
1056 str
= sslGetCAAttribute(ssl
, format
->header
);
1061 case Format::LFT_SSL_CLIENT_SNI
:
1062 if (ch
->conn() != NULL
) {
1063 if (Ssl::ServerBump
* srvBump
= ch
->conn()->serverBump()) {
1064 if (!srvBump
->clientSni
.isEmpty())
1065 str
= srvBump
->clientSni
.c_str();
1070 case Format::LFT_SSL_SERVER_CERT_SUBJECT
:
1071 case Format::LFT_SSL_SERVER_CERT_ISSUER
: {
1072 X509
*serverCert
= NULL
;
1073 if (ch
->serverCert
.get())
1074 serverCert
= ch
->serverCert
.get();
1075 else if (ch
->conn()->serverBump())
1076 serverCert
= ch
->conn()->serverBump()->serverCert
.get();
1079 if (format
->type
== Format::LFT_SSL_SERVER_CERT_SUBJECT
)
1080 str
= Ssl::GetX509UserAttribute(serverCert
, "DN");
1082 str
= Ssl::GetX509CAAttribute(serverCert
, "DN");
1089 case Format::LFT_USER_EXTERNAL
:
1090 str
= request
->extacl_user
.termedBuf();
1093 case Format::LFT_EXT_LOG
:
1094 str
= request
->extacl_log
.termedBuf();
1096 case Format::LFT_TAG
:
1097 str
= request
->tag
.termedBuf();
1099 case Format::LFT_EXT_ACL_NAME
:
1100 str
= acl_data
->name
;
1102 case Format::LFT_EXT_ACL_DATA
:
1104 for (arg
= acl_data
->arguments
; arg
; arg
= arg
->next
) {
1108 if (acl_data
->def
->quote
== external_acl::QUOTE_METHOD_URL
) {
1109 const char *quoted
= rfc1738_escape(arg
->key
);
1110 sb
.append(quoted
, strlen(quoted
));
1114 strwordquote(&mb2
, arg
->key
);
1115 sb
.append(mb2
.buf
, mb2
.size
);
1122 case Format::LFT_PERCENT
:
1127 // TODO: replace this function with Format::assemble()
1128 // For now die on unsupported logformat codes.
1129 fatalf("ERROR: unknown external_acl_type format %u", (uint8_t)format
->type
);
1143 if (acl_data
->def
->quote
== external_acl::QUOTE_METHOD_URL
) {
1144 const char *quoted
= rfc1738_escape(str
);
1145 mb
.append(quoted
, strlen(quoted
));
1147 strwordquote(&mb
, str
);
1156 for (arg
= acl_data
->arguments
; arg
; arg
= arg
->next
) {
1160 if (acl_data
->def
->quote
== external_acl::QUOTE_METHOD_URL
) {
1161 const char *quoted
= rfc1738_escape(arg
->key
);
1162 mb
.append(quoted
, strlen(quoted
));
1164 strwordquote(&mb
, arg
->key
);
1175 external_acl_entry_expired(external_acl
* def
, external_acl_entry
* entry
)
1177 if (def
->cache_size
<= 0)
1180 if (entry
->date
+ (entry
->result
== 1 ? def
->ttl
: def
->negative_ttl
) < squid_curtime
)
1187 external_acl_grace_expired(external_acl
* def
, external_acl_entry
* entry
)
1189 if (def
->cache_size
<= 0)
1193 ttl
= entry
->result
== 1 ? def
->ttl
: def
->negative_ttl
;
1194 ttl
= (ttl
* (100 - def
->grace
)) / 100;
1196 if (entry
->date
+ ttl
<= squid_curtime
)
1202 static external_acl_entry
*
1203 external_acl_cache_add(external_acl
* def
, const char *key
, ExternalACLEntryData
const & data
)
1205 ExternalACLEntry
*entry
;
1207 // do not bother caching this result if TTL is going to expire it immediately
1208 if (def
->cache_size
<= 0 || (def
->ttl
<= 0 && data
.result
== 1) || (def
->negative_ttl
<= 0 && data
.result
!= 1)) {
1210 entry
= new ExternalACLEntry
;
1211 entry
->key
= xstrdup(key
);
1212 entry
->update(data
);
1217 entry
= static_cast<ExternalACLEntry
*>(hash_lookup(def
->cache
, key
));
1218 debugs(82, 2, "external_acl_cache_add: Adding '" << key
<< "' = " << data
.result
);
1221 debugs(82, 3, "ExternalACLEntry::update: updating existing entry");
1222 entry
->update(data
);
1223 external_acl_cache_touch(def
, entry
);
1228 entry
= new ExternalACLEntry
;
1229 entry
->key
= xstrdup(key
);
1230 entry
->update(data
);
1238 external_acl_cache_delete(external_acl
* def
, external_acl_entry
* entry
)
1240 assert(def
->cache_size
> 0 && entry
->def
== def
);
1241 hash_remove_link(def
->cache
, entry
);
1242 dlinkDelete(&entry
->lru
, &def
->lru_list
);
1243 def
->cache_entries
-= 1;
1247 /******************************************************************
1248 * external_acl helpers
1251 typedef struct _externalAclState externalAclState
;
1253 struct _externalAclState
{
1255 void *callback_data
;
1259 externalAclState
*queue
;
1262 CBDATA_TYPE(externalAclState
);
1264 free_externalAclState(void *data
)
1266 externalAclState
*state
= static_cast<externalAclState
*>(data
);
1267 safe_free(state
->key
);
1268 cbdataReferenceDone(state
->callback_data
);
1269 cbdataReferenceDone(state
->def
);
1273 * The helper program receives queries on stdin, one
1274 * per line, and must return the result on on stdout
1276 * General result syntax:
1278 * OK/ERR keyword=value ...
1282 * user= The users name (login)
1283 * message= Message describing the reason
1284 * tag= A string tag to be applied to the request that triggered the acl match.
1285 * applies to both OK and ERR responses.
1286 * Won't override existing request tags.
1287 * log= A string to be used in access logging
1289 * Other keywords may be added to the protocol later
1291 * value needs to be URL-encoded or enclosed in double quotes (")
1292 * with \-escaping on any whitespace, quotes, or slashes (\).
1295 externalAclHandleReply(void *data
, const HelperReply
&reply
)
1297 externalAclState
*state
= static_cast<externalAclState
*>(data
);
1298 externalAclState
*next
;
1299 ExternalACLEntryData entryData
;
1300 entryData
.result
= ACCESS_DENIED
;
1301 external_acl_entry
*entry
= NULL
;
1303 debugs(82, 2, HERE
<< "reply=" << reply
);
1305 if (reply
.result
== HelperReply::Okay
)
1306 entryData
.result
= ACCESS_ALLOWED
;
1307 // XXX: handle other non-DENIED results better
1309 // XXX: make entryData store a proper HelperReply object instead of copying.
1311 entryData
.notes
.append(&reply
.notes
);
1313 const char *label
= reply
.notes
.findFirst("tag");
1314 if (label
!= NULL
&& *label
!= '\0')
1315 entryData
.tag
= label
;
1317 label
= reply
.notes
.findFirst("message");
1318 if (label
!= NULL
&& *label
!= '\0')
1319 entryData
.message
= label
;
1321 label
= reply
.notes
.findFirst("log");
1322 if (label
!= NULL
&& *label
!= '\0')
1323 entryData
.log
= label
;
1326 label
= reply
.notes
.findFirst("user");
1327 if (label
!= NULL
&& *label
!= '\0')
1328 entryData
.user
= label
;
1330 label
= reply
.notes
.findFirst("password");
1331 if (label
!= NULL
&& *label
!= '\0')
1332 entryData
.password
= label
;
1335 dlinkDelete(&state
->list
, &state
->def
->queue
);
1337 if (cbdataReferenceValid(state
->def
)) {
1338 // only cache OK and ERR results.
1339 if (reply
.result
== HelperReply::Okay
|| reply
.result
== HelperReply::Error
)
1340 entry
= external_acl_cache_add(state
->def
, state
->key
, entryData
);
1342 external_acl_entry
*oldentry
= (external_acl_entry
*)hash_lookup(state
->def
->cache
, state
->key
);
1345 external_acl_cache_delete(state
->def
, oldentry
);
1351 cbdataReferenceDone(state
->def
);
1353 if (state
->callback
&& cbdataReferenceValidDone(state
->callback_data
, &cbdata
))
1354 state
->callback(cbdata
, entry
);
1356 next
= state
->queue
;
1365 ACLExternal::ExternalAclLookup(ACLChecklist
*checklist
, ACLExternal
* me
)
1367 ExternalACLLookup::Start(checklist
, me
->data
, false);
1371 ExternalACLLookup::Start(ACLChecklist
*checklist
, external_acl_data
*acl
, bool inBackground
)
1373 external_acl
*def
= acl
->def
;
1375 ACLFilledChecklist
*ch
= Filled(checklist
);
1376 const char *key
= makeExternalAclKey(ch
, acl
);
1377 assert(key
); // XXX: will fail if EXT_ACL_IDENT case needs an async lookup
1379 debugs(82, 2, HERE
<< (inBackground
? "bg" : "fg") << " lookup in '" <<
1380 def
->name
<< "' for '" << key
<< "'");
1382 /* Check for a pending lookup to hook into */
1383 // only possible if we are caching results.
1384 externalAclState
*oldstate
= NULL
;
1385 if (def
->cache_size
> 0) {
1386 for (dlink_node
*node
= def
->queue
.head
; node
; node
= node
->next
) {
1387 externalAclState
*oldstatetmp
= static_cast<externalAclState
*>(node
->data
);
1389 if (strcmp(key
, oldstatetmp
->key
) == 0) {
1390 oldstate
= oldstatetmp
;
1396 // A background refresh has no need to piggiback on a pending request:
1397 // When the pending request completes, the cache will be refreshed anyway.
1398 if (oldstate
&& inBackground
) {
1399 debugs(82, 7, HERE
<< "'" << def
->name
<< "' queue is already being refreshed (ch=" << ch
<< ")");
1403 externalAclState
*state
= cbdataAlloc(externalAclState
);
1404 state
->def
= cbdataReference(def
);
1406 state
->key
= xstrdup(key
);
1408 if (!inBackground
) {
1409 state
->callback
= &ExternalACLLookup::LookupDone
;
1410 state
->callback_data
= cbdataReference(checklist
);
1414 /* Hook into pending lookup */
1415 state
->queue
= oldstate
->queue
;
1416 oldstate
->queue
= state
;
1418 /* No pending lookup found. Sumbit to helper */
1420 /* Check for queue overload */
1422 if (def
->theHelper
->stats
.queue_size
>= (int)def
->theHelper
->childs
.n_running
) {
1423 debugs(82, 7, HERE
<< "'" << def
->name
<< "' queue is too long");
1424 assert(inBackground
); // or the caller should have checked
1429 /* Send it off to the helper */
1433 buf
.Printf("%s\n", key
);
1435 debugs(82, 4, "externalAclLookup: looking up for '" << key
<< "' in '" << def
->name
<< "'.");
1437 helperSubmit(def
->theHelper
, buf
.buf
, externalAclHandleReply
, state
);
1439 dlinkAdd(state
, &state
->list
, &def
->queue
);
1444 debugs(82, 4, "externalAclLookup: will wait for the result of '" << key
<<
1445 "' in '" << def
->name
<< "' (ch=" << ch
<< ").");
1449 externalAclStats(StoreEntry
* sentry
)
1453 for (p
= Config
.externalAclHelperList
; p
; p
= p
->next
) {
1454 storeAppendPrintf(sentry
, "External ACL Statistics: %s\n", p
->name
);
1455 storeAppendPrintf(sentry
, "Cache size: %d\n", p
->cache
->count
);
1456 helperStats(sentry
, p
->theHelper
);
1457 storeAppendPrintf(sentry
, "\n");
1462 externalAclRegisterWithCacheManager(void)
1464 Mgr::RegisterAction("external_acl",
1465 "External ACL stats",
1466 externalAclStats
, 0, 1);
1470 externalAclInit(void)
1472 static int firstTimeInit
= 1;
1475 for (p
= Config
.externalAclHelperList
; p
; p
= p
->next
) {
1477 p
->cache
= hash_create((HASHCMP
*) strcmp
, hashPrime(1024), hash4
);
1480 p
->theHelper
= new helper(p
->name
);
1482 p
->theHelper
->cmdline
= p
->cmdline
;
1484 p
->theHelper
->childs
.updateLimits(p
->children
);
1486 p
->theHelper
->ipc_type
= IPC_TCP_SOCKET
;
1488 p
->theHelper
->addr
= p
->local_addr
;
1490 helperOpenServers(p
->theHelper
);
1493 if (firstTimeInit
) {
1495 CBDATA_INIT_TYPE_FREECB(externalAclState
, free_externalAclState
);
1498 externalAclRegisterWithCacheManager();
1502 externalAclShutdown(void)
1506 for (p
= Config
.externalAclHelperList
; p
; p
= p
->next
) {
1507 helperShutdown(p
->theHelper
);
1511 ExternalACLLookup
ExternalACLLookup::instance_
;
1513 ExternalACLLookup::Instance()
1519 ExternalACLLookup::checkForAsync(ACLChecklist
*checklist
)const
1521 /* TODO: optimise this - we probably have a pointer to this
1522 * around somewhere */
1523 ACL
*acl
= ACL::FindByName(AclMatchedName
);
1525 ACLExternal
*me
= dynamic_cast<ACLExternal
*> (acl
);
1527 ACLExternal::ExternalAclLookup(checklist
, me
);
1530 /// Called when an async lookup returns
1532 ExternalACLLookup::LookupDone(void *data
, void *result
)
1534 ACLFilledChecklist
*checklist
= Filled(static_cast<ACLChecklist
*>(data
));
1535 checklist
->extacl_entry
= cbdataReference((external_acl_entry
*)result
);
1537 // attach the helper kv-pair to the transaction
1538 if (checklist
->extacl_entry
) {
1539 if (HttpRequest
* req
= checklist
->request
) {
1540 // XXX: we have no access to the transaction / AccessLogEntry so cant SyncNotes().
1541 // workaround by using anything already set in HttpRequest
1542 // OR use new and rely on a later Sync copying these to AccessLogEntry
1544 UpdateRequestNotes(checklist
->conn(), *req
, checklist
->extacl_entry
->notes
);
1548 checklist
->resumeNonBlockingCheck(ExternalACLLookup::Instance());
1551 /* This registers "external" in the registry. To do dynamic definitions
1552 * of external ACL's, rather than a static prototype, have a Prototype instance
1553 * prototype in the class that defines each external acl 'class'.
1554 * Then, then the external acl instance is created, it self registers under
1556 * Be sure that clone is fully functional for that acl class though!
1558 ACL::Prototype
ACLExternal::RegistryProtoype(&ACLExternal::RegistryEntry_
, "external");
1560 ACLExternal
ACLExternal::RegistryEntry_("external");
1563 ACLExternal::clone() const
1565 return new ACLExternal(*this);
1568 ACLExternal::ACLExternal(char const *theClass
) : data(NULL
), class_(xstrdup(theClass
))
1571 ACLExternal::ACLExternal(ACLExternal
const & old
) : data(NULL
), class_(old
.class_
? xstrdup(old
.class_
) : NULL
)
1573 /* we don't have copy constructors for the data yet */
1578 ACLExternal::typeString() const
1584 ACLExternal::isProxyAuth() const
1587 return data
->def
->require_auth
;