3 * $Id: external_acl.cc,v 1.73 2006/05/29 00:15:02 robertc Exp $
5 * DEBUG: section 82 External ACL
6 * AUTHOR: Henrik Nordstrom, MARA Systems AB
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
11 * The contents of this file is Copyright (C) 2002 by MARA Systems AB,
12 * Sweden, unless otherwise is indicated in the specific function. The
13 * author gives his full permission to include this file into the Squid
14 * software product under the terms of the GNU General Public License as
15 * published by the Free Software Foundation; either version 2 of the
16 * License, or (at your option) any later version.
18 * Squid is the result of efforts by numerous individuals from
19 * the Internet community; see the CONTRIBUTORS file for full
20 * details. Many organizations have provided support for Squid's
21 * development; see the SPONSORS file for full details. Squid is
22 * Copyrighted (C) 2001 by the Regents of the University of
23 * California; see the COPYRIGHT file for full details. Squid
24 * incorporates software developed and/or copyrighted by other
25 * sources; see the CREDITS file for full details.
27 * This program is free software; you can redistribute it and/or modify
28 * it under the terms of the GNU General Public License as published by
29 * the Free Software Foundation; either version 2 of the License, or
30 * (at your option) any later version.
32 * This program is distributed in the hope that it will be useful,
33 * but WITHOUT ANY WARRANTY; without even the implied warranty of
34 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 * GNU General Public License for more details.
37 * You should have received a copy of the GNU General Public License
38 * along with this program; if not, write to the Free Software
39 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
44 #include "CacheManager.h"
45 #include "ExternalACL.h"
46 #include "ExternalACLEntry.h"
47 #include "AuthUserRequest.h"
48 #include "SquidTime.h"
51 #include "ACLChecklist.h"
56 #include "client_side.h"
57 #include "HttpRequest.h"
58 #include "authenticate.h"
60 #include "URLScheme.h"
63 #ifndef DEFAULT_EXTERNAL_ACL_TTL
64 #define DEFAULT_EXTERNAL_ACL_TTL 1 * 60 * 60
66 #ifndef DEFAULT_EXTERNAL_ACL_CHILDREN
67 #define DEFAULT_EXTERNAL_ACL_CHILDREN 5
70 typedef struct _external_acl_format external_acl_format
;
72 static char *makeExternalAclKey(ACLChecklist
* ch
, external_acl_data
* acl_data
);
73 static void external_acl_cache_delete(external_acl
* def
, external_acl_entry
* entry
);
74 static int external_acl_entry_expired(external_acl
* def
, external_acl_entry
* entry
);
75 static int external_acl_grace_expired(external_acl
* def
, external_acl_entry
* entry
);
76 static void external_acl_cache_touch(external_acl
* def
, external_acl_entry
* entry
);
77 static external_acl_entry
*external_acl_cache_add(external_acl
* def
, const char *key
, ExternalACLEntryData
const &data
);
79 /******************************************************************
80 * external_acl directive
102 external_acl_format
*format
;
126 QUOTE_METHOD_SHELL
= 1,
133 struct _external_acl_format
151 EXT_ACL_HEADER_MEMBER
,
153 EXT_ACL_HEADER_ID_MEMBER
,
157 EXT_ACL_USER_CERT_RAW
,
158 EXT_ACL_USER_CERTCHAIN_RAW
,
163 external_acl_format
*next
;
167 http_hdr_type header_id
;
170 /* FIXME: These are not really cbdata, but it is an easy way
171 * to get them pooled, refcounted, accounted and freed properly...
173 CBDATA_TYPE(external_acl
);
174 CBDATA_TYPE(external_acl_format
);
177 free_external_acl_format(void *data
)
179 external_acl_format
*p
= static_cast<external_acl_format
*>(data
);
180 safe_free(p
->header
);
184 free_external_acl(void *data
)
186 external_acl
*p
= static_cast<external_acl
*>(data
);
190 external_acl_format
*f
= p
->format
;
195 wordlistDestroy(&p
->cmdline
);
198 helperShutdown(p
->theHelper
);
199 helperFree(p
->theHelper
);
203 while (p
->lru_list
.tail
)
204 external_acl_cache_delete(p
, static_cast<external_acl_entry
*>(p
->lru_list
.tail
->data
));
206 hashFreeMemory(p
->cache
);
210 parse_externalAclHelper(external_acl
** list
)
214 external_acl_format
**p
;
216 CBDATA_INIT_TYPE_FREECB(external_acl
, free_external_acl
);
217 CBDATA_INIT_TYPE_FREECB(external_acl_format
, free_external_acl_format
);
219 a
= cbdataAlloc(external_acl
);
221 a
->ttl
= DEFAULT_EXTERNAL_ACL_TTL
;
222 a
->negative_ttl
= -1;
223 a
->children
= DEFAULT_EXTERNAL_ACL_CHILDREN
;
225 token
= strtok(NULL
, w_space
);
230 a
->name
= xstrdup(token
);
232 token
= strtok(NULL
, w_space
);
234 a
->quote
= external_acl::QUOTE_METHOD_URL
;
238 if (strncmp(token
, "ttl=", 4) == 0) {
239 a
->ttl
= atoi(token
+ 4);
240 } else if (strncmp(token
, "negative_ttl=", 13) == 0) {
241 a
->negative_ttl
= atoi(token
+ 13);
242 } else if (strncmp(token
, "children=", 9) == 0) {
243 a
->children
= atoi(token
+ 9);
244 } else if (strncmp(token
, "concurrency=", 12) == 0) {
245 a
->concurrency
= atoi(token
+ 12);
246 } else if (strncmp(token
, "cache=", 6) == 0) {
247 a
->cache_size
= atoi(token
+ 6);
248 } else if (strncmp(token
, "grace=", 6) == 0) {
249 a
->grace
= atoi(token
+ 6);
250 } else if (strcmp(token
, "protocol=2.5") == 0) {
251 a
->quote
= external_acl::QUOTE_METHOD_SHELL
;
252 } else if (strcmp(token
, "protocol=3.0") == 0) {
253 a
->quote
= external_acl::QUOTE_METHOD_URL
;
254 } else if (strcmp(token
, "quote=url") == 0) {
255 a
->quote
= external_acl::QUOTE_METHOD_URL
;
256 } else if (strcmp(token
, "quote=shell") == 0) {
257 a
->quote
= external_acl::QUOTE_METHOD_SHELL
;
262 token
= strtok(NULL
, w_space
);
265 if (a
->negative_ttl
== -1)
266 a
->negative_ttl
= a
->ttl
;
272 external_acl_format
*format
;
274 /* stop on first non-format token found */
279 format
= cbdataAlloc(external_acl_format
);
281 if (strncmp(token
, "%{", 2) == 0) {
283 char *header
, *member
, *end
;
285 end
= strchr(header
, '}');
286 /* cut away the closing brace */
288 if (end
&& strlen(end
) == 1)
293 member
= strchr(header
, ':');
296 /* Split in header and member */
299 if (!isalnum(*member
))
300 format
->separator
= *member
++;
302 format
->separator
= ',';
304 format
->member
= xstrdup(member
);
306 format
->type
= _external_acl_format::EXT_ACL_HEADER_MEMBER
;
308 format
->type
= _external_acl_format::EXT_ACL_HEADER
;
311 format
->header
= xstrdup(header
);
312 format
->header_id
= httpHeaderIdByNameDef(header
, strlen(header
));
314 if (format
->header_id
!= -1) {
316 format
->type
= _external_acl_format::EXT_ACL_HEADER_ID_MEMBER
;
318 format
->type
= _external_acl_format::EXT_ACL_HEADER_ID
;
320 } else if (strcmp(token
, "%LOGIN") == 0) {
321 format
->type
= _external_acl_format::EXT_ACL_LOGIN
;
322 a
->require_auth
= true;
326 else if (strcmp(token
, "%IDENT") == 0)
327 format
->type
= _external_acl_format::EXT_ACL_IDENT
;
331 else if (strcmp(token
, "%SRC") == 0)
332 format
->type
= _external_acl_format::EXT_ACL_SRC
;
333 else if (strcmp(token
, "%SRCPORT") == 0)
334 format
->type
= _external_acl_format::EXT_ACL_SRCPORT
;
335 else if (strcmp(token
, "%MYADDR") == 0)
336 format
->type
= _external_acl_format::EXT_ACL_MYADDR
;
337 else if (strcmp(token
, "%MYPORT") == 0)
338 format
->type
= _external_acl_format::EXT_ACL_MYPORT
;
339 else if (strcmp(token
, "%DST") == 0)
340 format
->type
= _external_acl_format::EXT_ACL_DST
;
341 else if (strcmp(token
, "%PROTO") == 0)
342 format
->type
= _external_acl_format::EXT_ACL_PROTO
;
343 else if (strcmp(token
, "%PORT") == 0)
344 format
->type
= _external_acl_format::EXT_ACL_PORT
;
345 else if (strcmp(token
, "%PATH") == 0)
346 format
->type
= _external_acl_format::EXT_ACL_PATH
;
347 else if (strcmp(token
, "%METHOD") == 0)
348 format
->type
= _external_acl_format::EXT_ACL_METHOD
;
352 else if (strcmp(token
, "%USER_CERT") == 0)
353 format
->type
= _external_acl_format::EXT_ACL_USER_CERT_RAW
;
354 else if (strcmp(token
, "%USER_CERTCHAIN") == 0)
355 format
->type
= _external_acl_format::EXT_ACL_USER_CERTCHAIN_RAW
;
356 else if (strncmp(token
, "%USER_CERT_", 11)) {
357 format
->type
= _external_acl_format::EXT_ACL_USER_CERT
;
358 format
->header
= xstrdup(token
+ 11);
359 } else if (strncmp(token
, "%CA_CERT_", 11)) {
360 format
->type
= _external_acl_format::EXT_ACL_USER_CERT
;
361 format
->header
= xstrdup(token
+ 11);
365 else if (strcmp(token
, "%EXT_USER") == 0)
366 format
->type
= _external_acl_format::EXT_ACL_EXT_USER
;
373 token
= strtok(NULL
, w_space
);
376 /* There must be at least one format token */
384 wordlistAdd(&a
->cmdline
, token
);
387 parse_wordlist(&a
->cmdline
);
390 list
= &(*list
)->next
;
396 dump_externalAclHelper(StoreEntry
* sentry
, const char *name
, const external_acl
* list
)
398 const external_acl
*node
;
399 const external_acl_format
*format
;
400 const wordlist
*word
;
402 for (node
= list
; node
; node
= node
->next
) {
403 storeAppendPrintf(sentry
, "%s %s", name
, node
->name
);
405 if (node
->ttl
!= DEFAULT_EXTERNAL_ACL_TTL
)
406 storeAppendPrintf(sentry
, " ttl=%d", node
->ttl
);
408 if (node
->negative_ttl
!= node
->ttl
)
409 storeAppendPrintf(sentry
, " negative_ttl=%d", node
->negative_ttl
);
412 storeAppendPrintf(sentry
, " grace=%d", node
->grace
);
414 if (node
->children
!= DEFAULT_EXTERNAL_ACL_CHILDREN
)
415 storeAppendPrintf(sentry
, " children=%d", node
->children
);
417 if (node
->concurrency
)
418 storeAppendPrintf(sentry
, " concurrency=%d", node
->concurrency
);
421 storeAppendPrintf(sentry
, " cache=%d", node
->cache_size
);
423 for (format
= node
->format
; format
; format
= format
->next
) {
424 switch (format
->type
) {
426 case _external_acl_format::EXT_ACL_HEADER
:
428 case _external_acl_format::EXT_ACL_HEADER_ID
:
429 storeAppendPrintf(sentry
, " %%{%s}", format
->header
);
432 case _external_acl_format::EXT_ACL_HEADER_MEMBER
:
434 case _external_acl_format::EXT_ACL_HEADER_ID_MEMBER
:
435 storeAppendPrintf(sentry
, " %%{%s:%s}", format
->header
, format
->member
);
437 #define DUMP_EXT_ACL_TYPE(a) \
438 case _external_acl_format::EXT_ACL_##a: \
439 storeAppendPrintf(sentry, " %%%s", #a); \
442 DUMP_EXT_ACL_TYPE(LOGIN
);
445 DUMP_EXT_ACL_TYPE(IDENT
);
448 DUMP_EXT_ACL_TYPE(SRC
);
449 DUMP_EXT_ACL_TYPE(SRCPORT
);
450 DUMP_EXT_ACL_TYPE(MYADDR
);
451 DUMP_EXT_ACL_TYPE(MYPORT
);
452 DUMP_EXT_ACL_TYPE(DST
);
453 DUMP_EXT_ACL_TYPE(PROTO
);
454 DUMP_EXT_ACL_TYPE(PORT
);
455 DUMP_EXT_ACL_TYPE(PATH
);
456 DUMP_EXT_ACL_TYPE(METHOD
);
459 case _external_acl_format::EXT_ACL_USER_CERT_RAW
:
460 storeAppendPrintf(sentry
, " %%USER_CERT");
463 case _external_acl_format::EXT_ACL_USER_CERTCHAIN_RAW
:
464 storeAppendPrintf(sentry
, " %%USER_CERTCHAIN");
467 case _external_acl_format::EXT_ACL_USER_CERT
:
468 storeAppendPrintf(sentry
, " %%USER_CERT_%s", format
->header
);
471 case _external_acl_format::EXT_ACL_CA_CERT
:
472 storeAppendPrintf(sentry
, " %%USER_CERT_%s", format
->header
);
476 DUMP_EXT_ACL_TYPE(EXT_USER
);
478 case _external_acl_format::EXT_ACL_UNKNOWN
:
480 case _external_acl_format::EXT_ACL_END
:
481 fatal("unknown external_acl format error");
486 for (word
= node
->cmdline
; word
; word
= word
->next
)
487 storeAppendPrintf(sentry
, " %s", word
->key
);
489 storeAppendPrintf(sentry
, "\n");
494 free_externalAclHelper(external_acl
** list
)
497 external_acl
*node
= *list
;
504 static external_acl
*
505 find_externalAclHelper(const char *name
)
509 for (node
= Config
.externalAclHelperList
; node
; node
= node
->next
) {
510 if (strcmp(node
->name
, name
) == 0)
520 (ExternalACLEntry
*anEntry
)
523 assert (anEntry
->def
== NULL
);
525 hash_join(cache
, anEntry
);
526 dlinkAdd(anEntry
, &anEntry
->lru
, &lru_list
);
531 external_acl::trimCache()
533 if (cache_size
&& cache_entries
>= cache_size
)
534 external_acl_cache_delete(this, static_cast<external_acl_entry
*>(lru_list
.tail
->data
));
538 /******************************************************************
542 struct _external_acl_data
548 CBDATA_TYPE(external_acl_data
);
550 free_external_acl_data(void *data
)
552 external_acl_data
*p
= static_cast<external_acl_data
*>(data
);
553 wordlistDestroy(&p
->arguments
);
554 cbdataReferenceDone(p
->def
);
565 CBDATA_INIT_TYPE_FREECB(external_acl_data
, free_external_acl_data
);
567 data
= cbdataAlloc(external_acl_data
);
569 token
= strtok(NULL
, w_space
);
574 data
->def
= cbdataReference(find_externalAclHelper(token
));
579 while ((token
= strtokFile())) {
580 wordlistAdd(&data
->arguments
, token
);
585 ACLExternal::valid () const
587 if (data
->def
->require_auth
) {
588 if (authenticateSchemeCount() == 0) {
589 debug(28, 0) ("Can't use proxy auth because no authentication schemes were compiled.\n");
593 if (authenticateActiveSchemeCount() == 0) {
594 debug(28, 0) ("Can't use proxy auth because no authentication schemes are fully configured.\n");
603 ACLExternal::empty () const
608 ACLExternal::~ACLExternal()
615 aclMatchExternal(external_acl_data
*acl
, ACLChecklist
* ch
);
617 aclMatchExternal(external_acl_data
*acl
, ACLChecklist
* ch
)
620 external_acl_entry
*entry
;
621 const char *key
= "";
622 debug(82, 9) ("aclMatchExternal: acl=\"%s\"\n", acl
->def
->name
);
623 entry
= ch
->extacl_entry
;
626 if (cbdataReferenceValid(entry
) && entry
->def
== acl
->def
&&
627 strcmp((char *)entry
->key
, key
) == 0) {
630 /* Not valid, or not ours.. get rid of it */
631 cbdataReferenceDone(ch
->extacl_entry
);
636 external_acl_message
= "MISSING REQUIRED INFORMATION";
639 if (acl
->def
->require_auth
) {
641 /* Make sure the user is authenticated */
643 if ((ti
= ch
->authenticated()) != 1) {
644 debug(82, 2) ("aclMatchExternal: %s user not authenticated (%d)\n", acl
->def
->name
, ti
);
649 key
= makeExternalAclKey(ch
, acl
);
651 if (acl
->def
->require_auth
)
652 ch
->auth_user_request
= NULL
;
655 /* Not sufficient data to process */
659 entry
= static_cast<external_acl_entry
*>(hash_lookup(acl
->def
->cache
, key
));
661 if (!entry
|| external_acl_grace_expired(acl
->def
, entry
)) {
662 debug(82, 2) ("aclMatchExternal: %s(\"%s\") = lookup needed\n", acl
->def
->name
, key
);
663 debug(82, 2) ("aclMatchExternal: \"%s\": entry=@%p, age=%ld\n", key
, entry
,
664 entry
? (long int) squid_curtime
- entry
->date
: 0);
666 if (acl
->def
->theHelper
->stats
.queue_size
<= acl
->def
->theHelper
->n_running
) {
667 debug(82, 2) ("aclMatchExternal: \"%s\": queueing a call.\n", key
);
668 ch
->changeState (ExternalACLLookup::Instance());
671 debug(82, 2) ("aclMatchExternal: \"%s\": return -1.\n", key
);
676 debug(82, 1) ("aclMatchExternal: '%s' queue overload. Request rejected '%s'.\n", acl
->def
->name
, key
);
677 external_acl_message
= "SYSTEM TOO BUSY, TRY AGAIN LATER";
680 debug(82, 1) ("aclMatchExternal: '%s' queue overload. Using stale result. '%s'.\n", acl
->def
->name
, key
);
681 /* Fall thru to processing below */
687 external_acl_cache_touch(acl
->def
, entry
);
688 result
= entry
->result
;
689 external_acl_message
= entry
->message
.buf();
691 debug(82, 2) ("aclMatchExternal: %s = %d\n", acl
->def
->name
, result
);
694 if (entry
->user
.size())
695 ch
->request
->extacl_user
= entry
->user
;
697 if (entry
->password
.size())
698 ch
->request
->extacl_passwd
= entry
->password
;
700 if (!ch
->request
->tag
.size())
701 ch
->request
->tag
= entry
->tag
;
703 if (entry
->log
.size())
704 ch
->request
->extacl_log
= entry
->log
;
711 ACLExternal::match(ACLChecklist
*checklist
)
713 return aclMatchExternal (data
, checklist
);
717 ACLExternal::dump() const
719 external_acl_data
const *acl
= data
;
720 wordlist
*result
= NULL
;
724 mb
.Printf("%s", acl
->def
->name
);
726 for (arg
= acl
->arguments
; arg
; arg
= arg
->next
) {
727 mb
.Printf(" %s", arg
->key
);
730 wordlistAdd(&result
, mb
.buf
);
735 /******************************************************************
740 external_acl_cache_touch(external_acl
* def
, external_acl_entry
* entry
)
742 dlinkDelete(&entry
->lru
, &def
->lru_list
);
743 dlinkAdd(entry
, &entry
->lru
, &def
->lru_list
);
747 makeExternalAclKey(ACLChecklist
* ch
, external_acl_data
* acl_data
)
753 external_acl_format
*format
;
754 HttpRequest
*request
= ch
->request
;
757 for (format
= acl_data
->def
->format
; format
; format
= format
->next
) {
758 const char *str
= NULL
;
761 switch (format
->type
) {
763 case _external_acl_format::EXT_ACL_LOGIN
:
764 assert (ch
->auth_user_request
);
765 str
= ch
->auth_user_request
->username();
769 case _external_acl_format::EXT_ACL_IDENT
:
773 ch
->changeState(IdentLookup::Instance());
780 case _external_acl_format::EXT_ACL_SRC
:
781 str
= inet_ntoa(ch
->src_addr
);
784 case _external_acl_format::EXT_ACL_SRCPORT
:
785 snprintf(buf
, sizeof(buf
), "%d", request
->client_port
);
789 case _external_acl_format::EXT_ACL_MYADDR
:
790 str
= inet_ntoa(request
->my_addr
);
793 case _external_acl_format::EXT_ACL_MYPORT
:
794 snprintf(buf
, sizeof(buf
), "%d", request
->my_port
);
798 case _external_acl_format::EXT_ACL_DST
:
802 case _external_acl_format::EXT_ACL_PROTO
:
803 str
= ProtocolStr
[request
->protocol
];
806 case _external_acl_format::EXT_ACL_PORT
:
807 snprintf(buf
, sizeof(buf
), "%d", request
->port
);
811 case _external_acl_format::EXT_ACL_PATH
:
812 str
= request
->urlpath
.buf();
815 case _external_acl_format::EXT_ACL_METHOD
:
816 str
= RequestMethodStr
[request
->method
];
819 case _external_acl_format::EXT_ACL_HEADER
:
820 sb
= request
->header
.getByName(format
->header
);
824 case _external_acl_format::EXT_ACL_HEADER_ID
:
825 sb
= request
->header
.getStrOrList(format
->header_id
);
829 case _external_acl_format::EXT_ACL_HEADER_MEMBER
:
830 sb
= request
->header
.getByNameListMember(format
->header
, format
->member
, format
->separator
);
834 case _external_acl_format::EXT_ACL_HEADER_ID_MEMBER
:
835 sb
= request
->header
.getListMember(format
->header_id
, format
->member
, format
->separator
);
840 case _external_acl_format::EXT_ACL_USER_CERT_RAW
:
842 if (ch
->conn().getRaw()) {
843 SSL
*ssl
= fd_table
[ch
->conn()->fd
].ssl
;
846 str
= sslGetUserCertificatePEM(ssl
);
851 case _external_acl_format::EXT_ACL_USER_CERTCHAIN_RAW
:
853 if (ch
->conn().getRaw()) {
854 SSL
*ssl
= fd_table
[ch
->conn()->fd
].ssl
;
857 str
= sslGetUserCertificateChainPEM(ssl
);
862 case _external_acl_format::EXT_ACL_USER_CERT
:
864 if (ch
->conn().getRaw()) {
865 SSL
*ssl
= fd_table
[ch
->conn()->fd
].ssl
;
868 str
= sslGetUserAttribute(ssl
, format
->header
);
873 case _external_acl_format::EXT_ACL_CA_CERT
:
875 if (ch
->conn().getRaw()) {
876 SSL
*ssl
= fd_table
[ch
->conn()->fd
].ssl
;
879 str
= sslGetCAAttribute(ssl
, format
->header
);
885 case _external_acl_format::EXT_ACL_EXT_USER
:
886 str
= request
->extacl_user
.buf();
889 case _external_acl_format::EXT_ACL_UNKNOWN
:
891 case _external_acl_format::EXT_ACL_END
:
892 fatal("unknown external_acl format error");
906 if (acl_data
->def
->quote
== external_acl::QUOTE_METHOD_URL
) {
907 const char *quoted
= rfc1738_escape(str
);
908 mb
.append(quoted
, strlen(quoted
));
910 strwordquote(&mb
, str
);
918 for (arg
= acl_data
->arguments
; arg
; arg
= arg
->next
) {
922 if (acl_data
->def
->quote
== external_acl::QUOTE_METHOD_URL
) {
923 const char *quoted
= rfc1738_escape(arg
->key
);
924 mb
.append(quoted
, strlen(quoted
));
926 strwordquote(&mb
, arg
->key
);
936 external_acl_entry_expired(external_acl
* def
, external_acl_entry
* entry
)
938 if (entry
->date
+ (entry
->result
== 1 ? def
->ttl
: def
->negative_ttl
) < squid_curtime
)
945 external_acl_grace_expired(external_acl
* def
, external_acl_entry
* entry
)
948 ttl
= entry
->result
== 1 ? def
->ttl
: def
->negative_ttl
;
949 ttl
= (ttl
* (100 - def
->grace
)) / 100;
951 if (entry
->date
+ ttl
< squid_curtime
)
957 static external_acl_entry
*
958 external_acl_cache_add(external_acl
* def
, const char *key
, ExternalACLEntryData
const & data
)
960 ExternalACLEntry
*entry
= static_cast<ExternalACLEntry
*>(hash_lookup(def
->cache
, key
));
961 debug(82, 2) ("external_acl_cache_add: Adding '%s' = %d\n", key
, data
.result
);
964 debug(82, 3) ("ExternalACLEntry::update: updating existing entry\n");
965 entry
->update (data
);
966 external_acl_cache_touch(def
, entry
);
971 entry
= new ExternalACLEntry
;
972 entry
->key
= xstrdup(key
);
973 entry
->update (data
);
982 external_acl_cache_delete(external_acl
* def
, external_acl_entry
* entry
)
984 assert (entry
->def
== def
);
985 hash_remove_link(def
->cache
, entry
);
986 dlinkDelete(&entry
->lru
, &def
->lru_list
);
987 def
->cache_entries
-= 1;
991 /******************************************************************
992 * external_acl helpers
995 typedef struct _externalAclState externalAclState
;
997 struct _externalAclState
1000 void *callback_data
;
1004 externalAclState
*queue
;
1007 CBDATA_TYPE(externalAclState
);
1009 free_externalAclState(void *data
)
1011 externalAclState
*state
= static_cast<externalAclState
*>(data
);
1012 safe_free(state
->key
);
1013 cbdataReferenceDone(state
->callback_data
);
1014 cbdataReferenceDone(state
->def
);
1018 * The helper program receives queries on stdin, one
1019 * per line, and must return the result on on stdout
1021 * General result syntax:
1023 * OK/ERR keyword=value ...
1027 * user= The users name (login)
1028 * message= Message describing the reason
1029 * tag= A string tag to be applied to the request that triggered the acl match.
1030 * applies to both OK and ERR responses.
1031 * Won't override existing request tags.
1032 * log= A string to be used in access logging
1034 * Other keywords may be added to the protocol later
1036 * value needs to be enclosed in quotes if it may contain whitespace, or
1037 * the whitespace escaped using \ (\ escaping obviously also applies to
1042 externalAclHandleReply(void *data
, char *reply
)
1044 externalAclState
*state
= static_cast<externalAclState
*>(data
);
1045 externalAclState
*next
;
1050 ExternalACLEntryData entryData
;
1051 entryData
.result
= 0;
1052 external_acl_entry
*entry
= NULL
;
1054 debug(82, 2) ("externalAclHandleReply: reply=\"%s\"\n", reply
);
1057 status
= strwordtok(reply
, &t
);
1059 if (status
&& strcmp(status
, "OK") == 0)
1060 entryData
.result
= 1;
1062 while ((token
= strwordtok(NULL
, &t
))) {
1063 value
= strchr(token
, '=');
1066 *value
++ = '\0'; /* terminate the token, and move up to the value */
1068 if (state
->def
->quote
== external_acl::QUOTE_METHOD_URL
)
1069 rfc1738_unescape(value
);
1071 if (strcmp(token
, "user") == 0)
1072 entryData
.user
= value
;
1073 else if (strcmp(token
, "message") == 0)
1074 entryData
.message
= value
;
1075 else if (strcmp(token
, "error") == 0)
1076 entryData
.message
= value
;
1077 else if (strcmp(token
, "tag") == 0)
1078 entryData
.tag
= value
;
1079 else if (strcmp(token
, "log") == 0)
1080 entryData
.log
= value
;
1081 else if (strcmp(token
, "password") == 0)
1082 entryData
.password
= value
;
1083 else if (strcmp(token
, "passwd") == 0)
1084 entryData
.password
= value
;
1085 else if (strcmp(token
, "login") == 0)
1086 entryData
.user
= value
;
1091 dlinkDelete(&state
->list
, &state
->def
->queue
);
1093 if (cbdataReferenceValid(state
->def
)) {
1095 entry
= external_acl_cache_add(state
->def
, state
->key
, entryData
);
1097 external_acl_entry
*oldentry
= (external_acl_entry
*)hash_lookup(state
->def
->cache
, state
->key
);
1100 external_acl_cache_delete(state
->def
, oldentry
);
1106 cbdataReferenceDone(state
->def
);
1108 if (state
->callback
&& cbdataReferenceValidDone(state
->callback_data
, &cbdata
))
1109 state
->callback(cbdata
, entry
);
1111 next
= state
->queue
;
1120 ACLExternal::ExternalAclLookup(ACLChecklist
* ch
, ACLExternal
* me
, EAH
* callback
, void *callback_data
)
1123 external_acl_data
*acl
= me
->data
;
1124 external_acl
*def
= acl
->def
;
1125 externalAclState
*state
;
1127 externalAclState
*oldstate
= NULL
;
1130 if (acl
->def
->require_auth
) {
1132 /* Make sure the user is authenticated */
1134 if ((ti
= ch
->authenticated()) != 1) {
1135 debug(82, 1) ("externalAclLookup: %s user authentication failure (%d, ch=%p)\n",
1136 acl
->def
->name
, ti
, ch
);
1137 callback(callback_data
, NULL
);
1142 const char *key
= makeExternalAclKey(ch
, acl
);
1145 debug(82, 1) ("externalAclLookup: lookup in '%s', prerequisit failure (ch=%p)\n", def
->name
, ch
);
1146 callback(callback_data
, NULL
);
1150 debug(82, 2) ("externalAclLookup: lookup in '%s' for '%s'\n", def
->name
, key
);
1151 external_acl_entry
*entry
= static_cast<external_acl_entry
*>(hash_lookup(def
->cache
, key
));
1153 if (entry
&& external_acl_entry_expired(def
, entry
))
1156 /* Check for a pending lookup to hook into */
1157 for (node
= def
->queue
.head
; node
; node
= node
->next
) {
1158 externalAclState
*oldstatetmp
= static_cast<externalAclState
*>(node
->data
);
1160 if (strcmp(key
, oldstatetmp
->key
) == 0) {
1161 oldstate
= oldstatetmp
;
1166 if (entry
&& external_acl_grace_expired(def
, entry
)) {
1168 debug(82, 4) ("externalAclLookup: in grace period, but already pending lookup ('%s', ch=%p)\n",
1170 callback(callback_data
, entry
);
1173 graceful
= 1; // grace expired, (neg)ttl did not, and we must start a new lookup.
1177 // The entry is in the cache, grace_ttl did not expired.
1178 if (!graceful
&& entry
&& !external_acl_grace_expired(def
, entry
)) {
1179 /* Should not really happen, but why not.. */
1180 callback(callback_data
, entry
);
1181 debug(82, 4) ("externalAclLookup: no lookup pending for '%s', and grace not expired\n", key
);
1182 debug(82, 4) ("externalAclLookup: (what tha' hell?)\n");
1186 /* No pending lookup found. Sumbit to helper */
1187 state
= cbdataAlloc(externalAclState
);
1189 state
->def
= cbdataReference(def
);
1191 state
->key
= xstrdup(key
);
1194 state
->callback
= callback
;
1195 state
->callback_data
= cbdataReference(callback_data
);
1199 /* Hook into pending lookup */
1200 state
->queue
= oldstate
->queue
;
1201 oldstate
->queue
= state
;
1203 /* Check for queue overload */
1205 if (def
->theHelper
->stats
.queue_size
>= def
->theHelper
->n_running
) {
1206 debug(82, 1) ("externalAclLookup: '%s' queue overload (ch=%p)\n", def
->name
, ch
);
1208 callback(callback_data
, entry
);
1212 /* Send it off to the helper */
1215 buf
.Printf("%s\n", key
);
1217 debug(82, 4) ("externalAclLookup: looking up for '%s' in '%s'.\n", key
, def
->name
);
1219 helperSubmit(def
->theHelper
, buf
.buf
, externalAclHandleReply
, state
);
1221 dlinkAdd(state
, &state
->list
, &def
->queue
);
1227 /* No need to wait during grace period */
1228 debug(82, 4) ("externalAclLookup: no need to wait for the result of '%s' in '%s' (ch=%p).\n",
1229 key
, def
->name
, ch
);
1230 debug(82, 4) ("externalAclLookup: using cached entry %p\n", entry
);
1232 if (entry
!= NULL
) {
1233 debug(82,4) ("externalAclLookup: entry = { date=%lu, result=%d, user=%s tag=%s log=%s }\n",
1234 (long unsigned int) entry
->date
, entry
->result
, entry
->user
.buf(), entry
->tag
.buf(),
1238 callback(callback_data
, entry
);
1242 debug(82, 4) ("externalAclLookup: will wait for the result of '%s' in '%s' (ch=%p).\n",
1243 key
, def
->name
, ch
);
1247 externalAclStats(StoreEntry
* sentry
)
1251 for (p
= Config
.externalAclHelperList
; p
; p
= p
->next
) {
1252 storeAppendPrintf(sentry
, "External ACL Statistics: %s\n", p
->name
);
1253 storeAppendPrintf(sentry
, "Cache size: %d\n", p
->cache
->count
);
1254 helperStats(sentry
, p
->theHelper
);
1255 storeAppendPrintf(sentry
, "\n");
1260 externalAclInit(void)
1262 static int firstTimeInit
= 1;
1265 for (p
= Config
.externalAclHelperList
; p
; p
= p
->next
) {
1267 p
->cache
= hash_create((HASHCMP
*) strcmp
, hashPrime(1024), hash4
);
1270 p
->theHelper
= helperCreate(p
->name
);
1272 p
->theHelper
->cmdline
= p
->cmdline
;
1274 p
->theHelper
->n_to_start
= p
->children
;
1276 p
->theHelper
->concurrency
= p
->concurrency
;
1278 p
->theHelper
->ipc_type
= IPC_TCP_SOCKET
;
1280 helperOpenServers(p
->theHelper
);
1283 if (firstTimeInit
) {
1285 CBDATA_INIT_TYPE_FREECB(externalAclState
, free_externalAclState
);
1290 externalAclRegisterWithCacheManager(CacheManager
& manager
)
1292 manager
.registerAction("external_acl",
1293 "External ACL stats",
1294 externalAclStats
, 0, 1);
1298 externalAclShutdown(void)
1302 for (p
= Config
.externalAclHelperList
; p
; p
= p
->next
) {
1303 helperShutdown(p
->theHelper
);
1307 ExternalACLLookup
ExternalACLLookup::instance_
;
1309 ExternalACLLookup::Instance()
1315 ExternalACLLookup::checkForAsync(ACLChecklist
*checklist
)const
1317 /* TODO: optimise this - we probably have a pointer to this
1318 * around somewhere */
1319 ACL
*acl
= ACL::FindByName(AclMatchedName
);
1321 ACLExternal
*me
= dynamic_cast<ACLExternal
*> (acl
);
1323 checklist
->asyncInProgress(true);
1324 ACLExternal::ExternalAclLookup(checklist
, me
, LookupDone
, checklist
);
1328 ExternalACLLookup::LookupDone(void *data
, void *result
)
1330 ACLChecklist
*checklist
= (ACLChecklist
*)data
;
1331 checklist
->extacl_entry
= cbdataReference((external_acl_entry
*)result
);
1332 checklist
->asyncInProgress(false);
1333 checklist
->changeState (ACLChecklist::NullState::Instance());
1337 /* This registers "external" in the registry. To do dynamic definitions
1338 * of external ACL's, rather than a static prototype, have a Prototype instance
1339 * prototype in the class that defines each external acl 'class'.
1340 * Then, then the external acl instance is created, it self registers under
1342 * Be sure that clone is fully functional for that acl class though!
1344 ACL::Prototype
ACLExternal::RegistryProtoype(&ACLExternal::RegistryEntry_
, "external");
1346 ACLExternal
ACLExternal::RegistryEntry_("external");
1349 ACLExternal::clone() const
1351 return new ACLExternal(*this);
1354 ACLExternal::ACLExternal (char const *theClass
) : data (NULL
), class_ (xstrdup (theClass
))
1357 ACLExternal::ACLExternal (ACLExternal
const & old
) : data (NULL
), class_ (old
.class_
? xstrdup (old
.class_
) : NULL
)
1359 /* we don't have copy constructors for the data yet */
1364 ACLExternal::typeString() const
1370 ACLExternal::isProxyAuth() const
1372 return data
->def
->require_auth
;