3 * $Id: external_acl.cc,v 1.53 2003/08/10 11:00:43 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 "ExternalACL.h"
45 #include "ExternalACLEntry.h"
46 #include "authenticate.h"
49 #include "ACLChecklist.h"
54 #include "client_side.h"
55 #include "HttpRequest.h"
57 #ifndef DEFAULT_EXTERNAL_ACL_TTL
58 #define DEFAULT_EXTERNAL_ACL_TTL 1 * 60 * 60
60 #ifndef DEFAULT_EXTERNAL_ACL_CHILDREN
61 #define DEFAULT_EXTERNAL_ACL_CHILDREN 5
64 typedef struct _external_acl_format external_acl_format
;
66 static char *makeExternalAclKey(ACLChecklist
* ch
, external_acl_data
* acl_data
);
67 static void external_acl_cache_delete(external_acl
* def
, external_acl_entry
* entry
);
68 static int external_acl_entry_expired(external_acl
* def
, external_acl_entry
* entry
);
69 static int external_acl_grace_expired(external_acl
* def
, external_acl_entry
* entry
);
70 static void external_acl_cache_touch(external_acl
* def
, external_acl_entry
* entry
);
71 static external_acl_entry
*external_acl_cache_add(external_acl
* def
, const char *key
, ExternalACLEntryData
const &data
);
73 /******************************************************************
74 * external_acl directive
96 external_acl_format
*format
;
119 struct _external_acl_format
137 EXT_ACL_HEADER_MEMBER
,
139 EXT_ACL_HEADER_ID_MEMBER
,
147 external_acl_format
*next
;
151 http_hdr_type header_id
;
154 /* FIXME: These are not really cbdata, but it is an easy way
155 * to get them pooled, refcounted, accounted and freed properly...
157 CBDATA_TYPE(external_acl
);
158 CBDATA_TYPE(external_acl_format
);
161 free_external_acl_format(void *data
)
163 external_acl_format
*p
= static_cast<external_acl_format
*>(data
);
164 safe_free(p
->header
);
168 free_external_acl(void *data
)
170 external_acl
*p
= static_cast<external_acl
*>(data
);
174 external_acl_format
*f
= p
->format
;
179 wordlistDestroy(&p
->cmdline
);
182 helperShutdown(p
->theHelper
);
183 helperFree(p
->theHelper
);
187 while (p
->lru_list
.tail
)
188 external_acl_cache_delete(p
, static_cast<external_acl_entry
*>(p
->lru_list
.tail
->data
));
190 hashFreeMemory(p
->cache
);
194 parse_externalAclHelper(external_acl
** list
)
198 external_acl_format
**p
;
200 CBDATA_INIT_TYPE_FREECB(external_acl
, free_external_acl
);
201 CBDATA_INIT_TYPE_FREECB(external_acl_format
, free_external_acl_format
);
203 a
= cbdataAlloc(external_acl
);
205 a
->ttl
= DEFAULT_EXTERNAL_ACL_TTL
;
206 a
->negative_ttl
= -1;
207 a
->children
= DEFAULT_EXTERNAL_ACL_CHILDREN
;
209 token
= strtok(NULL
, w_space
);
214 a
->name
= xstrdup(token
);
216 token
= strtok(NULL
, w_space
);
220 if (strncmp(token
, "ttl=", 4) == 0) {
221 a
->ttl
= atoi(token
+ 4);
222 } else if (strncmp(token
, "negative_ttl=", 13) == 0) {
223 a
->negative_ttl
= atoi(token
+ 13);
224 } else if (strncmp(token
, "children=", 9) == 0) {
225 a
->children
= atoi(token
+ 9);
226 } else if (strncmp(token
, "concurrency=", 12) == 0) {
227 a
->concurrency
= atoi(token
+ 12);
228 } else if (strncmp(token
, "cache=", 6) == 0) {
229 a
->cache_size
= atoi(token
+ 6);
230 } else if (strncmp(token
, "grace=", 6) == 0) {
231 a
->grace
= atoi(token
+ 6);
236 token
= strtok(NULL
, w_space
);
239 if (a
->negative_ttl
== -1)
240 a
->negative_ttl
= a
->ttl
;
246 external_acl_format
*format
;
248 /* stop on first non-format token found */
253 format
= cbdataAlloc(external_acl_format
);
255 if (strncmp(token
, "%{", 2) == 0) {
257 char *header
, *member
, *end
;
259 end
= strchr(header
, '}');
260 /* cut away the closing brace */
262 if (end
&& strlen(end
) == 1)
267 member
= strchr(header
, ':');
270 /* Split in header and member */
273 if (!isalnum(*member
))
274 format
->separator
= *member
++;
276 format
->separator
= ',';
278 format
->member
= xstrdup(member
);
280 format
->type
= _external_acl_format::EXT_ACL_HEADER_MEMBER
;
282 format
->type
= _external_acl_format::EXT_ACL_HEADER
;
285 format
->header
= xstrdup(header
);
286 format
->header_id
= httpHeaderIdByNameDef(header
, strlen(header
));
288 if (format
->header_id
!= -1) {
290 format
->type
= _external_acl_format::EXT_ACL_HEADER_ID_MEMBER
;
292 format
->type
= _external_acl_format::EXT_ACL_HEADER_ID
;
294 } else if (strcmp(token
, "%LOGIN") == 0) {
295 format
->type
= _external_acl_format::EXT_ACL_LOGIN
;
300 else if (strcmp(token
, "%IDENT") == 0)
301 format
->type
= _external_acl_format::EXT_ACL_IDENT
;
305 else if (strcmp(token
, "%SRC") == 0)
306 format
->type
= _external_acl_format::EXT_ACL_SRC
;
307 else if (strcmp(token
, "%SRCPORT") == 0)
308 format
->type
= _external_acl_format::EXT_ACL_SRCPORT
;
309 else if (strcmp(token
, "%MYADDR") == 0)
310 format
->type
= _external_acl_format::EXT_ACL_MYADDR
;
311 else if (strcmp(token
, "%MYPORT") == 0)
312 format
->type
= _external_acl_format::EXT_ACL_MYPORT
;
313 else if (strcmp(token
, "%DST") == 0)
314 format
->type
= _external_acl_format::EXT_ACL_DST
;
315 else if (strcmp(token
, "%PROTO") == 0)
316 format
->type
= _external_acl_format::EXT_ACL_PROTO
;
317 else if (strcmp(token
, "%PORT") == 0)
318 format
->type
= _external_acl_format::EXT_ACL_PORT
;
319 else if (strcmp(token
, "%PATH") == 0)
320 format
->type
= _external_acl_format::EXT_ACL_PATH
;
321 else if (strcmp(token
, "%METHOD") == 0)
322 format
->type
= _external_acl_format::EXT_ACL_METHOD
;
326 else if (strncmp(token
, "%USER_CERT_", 11)) {
327 format
->type
= _external_acl_format::EXT_ACL_USER_CERT
;
328 format
->header
= xstrdup(token
+ 11);
329 } else if (strncmp(token
, "%CA_CERT_", 11)) {
330 format
->type
= _external_acl_format::EXT_ACL_USER_CERT
;
331 format
->header
= xstrdup(token
+ 11);
335 else if (strcmp(token
, "%EXT_USER") == 0)
336 format
->type
= _external_acl_format::EXT_ACL_EXT_USER
;
343 token
= strtok(NULL
, w_space
);
346 /* There must be at least one format token */
354 wordlistAdd(&a
->cmdline
, token
);
357 parse_wordlist(&a
->cmdline
);
360 list
= &(*list
)->next
;
366 dump_externalAclHelper(StoreEntry
* sentry
, const char *name
, const external_acl
* list
)
368 const external_acl
*node
;
369 const external_acl_format
*format
;
370 const wordlist
*word
;
372 for (node
= list
; node
; node
= node
->next
) {
373 storeAppendPrintf(sentry
, "%s %s", name
, node
->name
);
375 if (node
->ttl
!= DEFAULT_EXTERNAL_ACL_TTL
)
376 storeAppendPrintf(sentry
, " ttl=%d", node
->ttl
);
378 if (node
->negative_ttl
!= node
->ttl
)
379 storeAppendPrintf(sentry
, " negative_ttl=%d", node
->negative_ttl
);
382 storeAppendPrintf(sentry
, " grace=%d", node
->grace
);
384 if (node
->children
!= DEFAULT_EXTERNAL_ACL_CHILDREN
)
385 storeAppendPrintf(sentry
, " children=%d", node
->children
);
387 if (node
->concurrency
)
388 storeAppendPrintf(sentry
, " concurrency=%d", node
->concurrency
);
391 storeAppendPrintf(sentry
, " cache=%d", node
->cache_size
);
393 for (format
= node
->format
; format
; format
= format
->next
) {
394 switch (format
->type
) {
396 case _external_acl_format::EXT_ACL_HEADER
:
398 case _external_acl_format::EXT_ACL_HEADER_ID
:
399 storeAppendPrintf(sentry
, " %%{%s}", format
->header
);
402 case _external_acl_format::EXT_ACL_HEADER_MEMBER
:
404 case _external_acl_format::EXT_ACL_HEADER_ID_MEMBER
:
405 storeAppendPrintf(sentry
, " %%{%s:%s}", format
->header
, format
->member
);
407 #define DUMP_EXT_ACL_TYPE(a) \
408 case _external_acl_format::EXT_ACL_##a: \
409 storeAppendPrintf(sentry, " %%%s", #a); \
412 DUMP_EXT_ACL_TYPE(LOGIN
);
415 DUMP_EXT_ACL_TYPE(IDENT
);
418 DUMP_EXT_ACL_TYPE(SRC
);
419 DUMP_EXT_ACL_TYPE(SRCPORT
);
420 DUMP_EXT_ACL_TYPE(MYADDR
);
421 DUMP_EXT_ACL_TYPE(MYPORT
);
422 DUMP_EXT_ACL_TYPE(DST
);
423 DUMP_EXT_ACL_TYPE(PROTO
);
424 DUMP_EXT_ACL_TYPE(PORT
);
425 DUMP_EXT_ACL_TYPE(PATH
);
426 DUMP_EXT_ACL_TYPE(METHOD
);
429 case _external_acl_format::EXT_ACL_USER_CERT
:
430 storeAppendPrintf(sentry
, " %%USER_CERT_%s", format
->header
);
433 case _external_acl_format::EXT_ACL_CA_CERT
:
434 storeAppendPrintf(sentry
, " %%USER_CERT_%s", format
->header
);
438 DUMP_EXT_ACL_TYPE(EXT_USER
);
440 case _external_acl_format::EXT_ACL_UNKNOWN
:
442 case _external_acl_format::EXT_ACL_END
:
443 fatal("unknown external_acl format error");
448 for (word
= node
->cmdline
; word
; word
= word
->next
)
449 storeAppendPrintf(sentry
, " %s", word
->key
);
451 storeAppendPrintf(sentry
, "\n");
456 free_externalAclHelper(external_acl
** list
)
459 external_acl
*node
= *list
;
466 static external_acl
*
467 find_externalAclHelper(const char *name
)
471 for (node
= Config
.externalAclHelperList
; node
; node
= node
->next
) {
472 if (strcmp(node
->name
, name
) == 0)
482 (ExternalACLEntry
*anEntry
)
485 assert (anEntry
->def
== NULL
);
487 hash_join(cache
, anEntry
);
488 dlinkAdd(anEntry
, &anEntry
->lru
, &lru_list
);
493 external_acl::trimCache()
495 if (cache_size
&& cache_entries
>= cache_size
)
496 external_acl_cache_delete(this, static_cast<external_acl_entry
*>(lru_list
.tail
->data
));
500 /******************************************************************
504 struct _external_acl_data
510 CBDATA_TYPE(external_acl_data
);
512 free_external_acl_data(void *data
)
514 external_acl_data
*p
= static_cast<external_acl_data
*>(data
);
515 wordlistDestroy(&p
->arguments
);
516 cbdataReferenceDone(p
->def
);
527 CBDATA_INIT_TYPE_FREECB(external_acl_data
, free_external_acl_data
);
529 data
= cbdataAlloc(external_acl_data
);
531 token
= strtok(NULL
, w_space
);
536 data
->def
= cbdataReference(find_externalAclHelper(token
));
541 while ((token
= strtokFile())) {
542 wordlistAdd(&data
->arguments
, token
);
546 ACLExternal::~ACLExternal()
553 aclMatchExternal(external_acl_data
*acl
, ACLChecklist
* ch
);
555 aclMatchExternal(external_acl_data
*acl
, ACLChecklist
* ch
)
558 external_acl_entry
*entry
;
559 const char *key
= "";
560 debug(82, 9) ("aclMatchExternal: acl=\"%s\"\n", acl
->def
->name
);
561 entry
= ch
->extacl_entry
;
564 if (cbdataReferenceValid(entry
) && entry
->def
== acl
->def
&&
565 strcmp((char *)entry
->key
, key
) == 0) {
568 /* Not valid, or not ours.. get rid of it */
569 cbdataReferenceDone(ch
->extacl_entry
);
574 external_acl_message
= "MISSING REQUIRED INFORMATION";
577 if (acl
->def
->require_auth
) {
579 /* Make sure the user is authenticated */
581 if ((ti
= ch
->authenticated()) != 1) {
582 debug(82, 2) ("aclMatchExternal: %s user not authenticated (%d)\n", acl
->def
->name
, ti
);
587 key
= makeExternalAclKey(ch
, acl
);
589 ch
->auth_user_request
= NULL
;
592 /* Not sufficient data to process */
596 entry
= static_cast<external_acl_entry
*>(hash_lookup(acl
->def
->cache
, key
));
598 if (!entry
|| external_acl_grace_expired(acl
->def
, entry
)) {
599 debug(82, 2) ("aclMatchExternal: %s(\"%s\") = lookup needed\n", acl
->def
->name
, key
);
601 if (acl
->def
->theHelper
->stats
.queue_size
<= acl
->def
->theHelper
->n_running
) {
602 ch
->changeState (ExternalACLLookup::Instance());
606 debug(82, 1) ("aclMatchExternal: '%s' queue overload. Request rejected '%s'.\n", acl
->def
->name
, key
);
607 external_acl_message
= "SYSTEM TOO BUSY, TRY AGAIN LATER";
610 debug(82, 1) ("aclMatchExternal: '%s' queue overload. Using stale result. '%s'.\n", acl
->def
->name
, key
);
611 /* Fall thru to processing below */
617 external_acl_cache_touch(acl
->def
, entry
);
618 result
= entry
->result
;
619 external_acl_message
= entry
->message
.buf();
621 debug(82, 2) ("aclMatchExternal: %s = %d\n", acl
->def
->name
, result
);
624 if (entry
->user
.size())
625 ch
->request
->extacl_user
= entry
->user
;
627 if (entry
->password
.size())
628 ch
->request
->extacl_passwd
= entry
->password
;
630 if (!ch
->request
->tag
.size())
631 ch
->request
->tag
= entry
->tag
;
633 if (entry
->log
.size())
634 ch
->request
->extacl_log
= entry
->log
;
641 ACLExternal::match(ACLChecklist
*checklist
)
643 return aclMatchExternal (data
, checklist
);
647 ACLExternal::dump() const
649 external_acl_data
const *acl
= data
;
650 wordlist
*result
= NULL
;
654 memBufPrintf(&mb
, "%s", acl
->def
->name
);
656 for (arg
= acl
->arguments
; arg
; arg
= arg
->next
) {
657 memBufPrintf(&mb
, " %s", arg
->key
);
660 wordlistAdd(&result
, mb
.buf
);
665 /******************************************************************
670 external_acl_cache_touch(external_acl
* def
, external_acl_entry
* entry
)
672 dlinkDelete(&entry
->lru
, &def
->lru_list
);
673 dlinkAdd(entry
, &entry
->lru
, &def
->lru_list
);
677 makeExternalAclKey(ACLChecklist
* ch
, external_acl_data
* acl_data
)
679 static MemBuf mb
= MemBufNULL
;
683 external_acl_format
*format
;
684 HttpRequest
*request
= ch
->request
;
687 for (format
= acl_data
->def
->format
; format
; format
= format
->next
) {
688 const char *str
= NULL
;
691 switch (format
->type
) {
693 case _external_acl_format::EXT_ACL_LOGIN
:
694 str
= authenticateUserRequestUsername(ch
->auth_user_request
);
698 case _external_acl_format::EXT_ACL_IDENT
:
702 ch
->changeState(IdentLookup::Instance());
709 case _external_acl_format::EXT_ACL_SRC
:
710 str
= inet_ntoa(ch
->src_addr
);
713 case _external_acl_format::EXT_ACL_SRCPORT
:
714 snprintf(buf
, sizeof(buf
), "%d", request
->client_port
);
718 case _external_acl_format::EXT_ACL_MYADDR
:
719 str
= inet_ntoa(request
->my_addr
);
722 case _external_acl_format::EXT_ACL_MYPORT
:
723 snprintf(buf
, sizeof(buf
), "%d", request
->my_port
);
727 case _external_acl_format::EXT_ACL_DST
:
731 case _external_acl_format::EXT_ACL_PROTO
:
732 str
= ProtocolStr
[request
->protocol
];
735 case _external_acl_format::EXT_ACL_PORT
:
736 snprintf(buf
, sizeof(buf
), "%d", request
->port
);
740 case _external_acl_format::EXT_ACL_PATH
:
741 str
= request
->urlpath
.buf();
744 case _external_acl_format::EXT_ACL_METHOD
:
745 str
= RequestMethodStr
[request
->method
];
748 case _external_acl_format::EXT_ACL_HEADER
:
749 sb
= httpHeaderGetByName(&request
->header
, format
->header
);
753 case _external_acl_format::EXT_ACL_HEADER_ID
:
754 sb
= httpHeaderGetStrOrList(&request
->header
, format
->header_id
);
758 case _external_acl_format::EXT_ACL_HEADER_MEMBER
:
759 sb
= httpHeaderGetByNameListMember(&request
->header
, format
->header
, format
->member
, format
->separator
);
763 case _external_acl_format::EXT_ACL_HEADER_ID_MEMBER
:
764 sb
= httpHeaderGetListMember(&request
->header
, format
->header_id
, format
->member
, format
->separator
);
769 case _external_acl_format::EXT_ACL_USER_CERT
:
771 if (ch
->conn().getRaw()) {
772 SSL
*ssl
= fd_table
[ch
->conn()->fd
].ssl
;
775 str
= sslGetUserAttribute(ssl
, format
->header
);
780 case _external_acl_format::EXT_ACL_CA_CERT
:
782 if (ch
->conn().getRaw()) {
783 SSL
*ssl
= fd_table
[ch
->conn()->fd
].ssl
;
786 str
= sslGetCAAttribute(ssl
, format
->header
);
792 case _external_acl_format::EXT_ACL_EXT_USER
:
793 str
= request
->extacl_user
.buf();
796 case _external_acl_format::EXT_ACL_UNKNOWN
:
798 case _external_acl_format::EXT_ACL_END
:
799 fatal("unknown external_acl format error");
811 memBufAppend(&mb
, " ", 1);
813 strwordquote(&mb
, str
);
820 for (arg
= acl_data
->arguments
; arg
; arg
= arg
->next
) {
822 memBufAppend(&mb
, " ", 1);
824 strwordquote(&mb
, arg
->key
);
833 external_acl_entry_expired(external_acl
* def
, external_acl_entry
* entry
)
835 if (entry
->date
+ (entry
->result
== 1 ? def
->ttl
: def
->negative_ttl
) < squid_curtime
)
842 external_acl_grace_expired(external_acl
* def
, external_acl_entry
* entry
)
845 ttl
= entry
->result
== 1 ? def
->ttl
: def
->negative_ttl
;
846 ttl
= (ttl
* (100 - def
->grace
)) / 100;
848 if (entry
->date
+ ttl
< squid_curtime
)
854 static external_acl_entry
*
855 external_acl_cache_add(external_acl
* def
, const char *key
, ExternalACLEntryData
const & data
)
857 ExternalACLEntry
*entry
= static_cast<ExternalACLEntry
*>(hash_lookup(def
->cache
, key
));
858 debug(82, 2) ("external_acl_cache_add: Adding '%s' = %d\n", key
, data
.result
);
861 debug(82, 3) ("ExternalACLEntry::update: updating existing entry\n");
862 entry
->update (data
);
863 external_acl_cache_touch(def
, entry
);
868 entry
= new ExternalACLEntry
;
869 entry
->key
= xstrdup(key
);
870 entry
->update (data
);
879 external_acl_cache_delete(external_acl
* def
, external_acl_entry
* entry
)
881 assert (entry
->def
== def
);
882 hash_remove_link(def
->cache
, entry
);
883 dlinkDelete(&entry
->lru
, &def
->lru_list
);
884 def
->cache_entries
-= 1;
888 /******************************************************************
889 * external_acl helpers
892 typedef struct _externalAclState externalAclState
;
894 struct _externalAclState
901 externalAclState
*queue
;
904 CBDATA_TYPE(externalAclState
);
906 free_externalAclState(void *data
)
908 externalAclState
*state
= static_cast<externalAclState
*>(data
);
909 safe_free(state
->key
);
910 cbdataReferenceDone(state
->callback_data
);
911 cbdataReferenceDone(state
->def
);
915 * The helper program receives queries on stdin, one
916 * per line, and must return the result on on stdout
918 * General result syntax:
920 * OK/ERR keyword=value ...
924 * user= The users name (login)
925 * message= Message describing the reason
926 * tag= A string tag to be applied to the request that triggered the acl match.
927 * applies to both OK and ERR responses.
928 * Won't override existing request tags.
929 * log= A string to be used in access logging
931 * Other keywords may be added to the protocol later
933 * value needs to be enclosed in quotes if it may contain whitespace, or
934 * the whitespace escaped using \ (\ escaping obviously also applies to
939 externalAclHandleReply(void *data
, char *reply
)
941 externalAclState
*state
= static_cast<externalAclState
*>(data
);
942 externalAclState
*next
;
947 ExternalACLEntryData entryData
;
948 entryData
.result
= 0;
949 external_acl_entry
*entry
= NULL
;
951 debug(82, 2) ("externalAclHandleReply: reply=\"%s\"\n", reply
);
954 status
= strwordtok(reply
, &t
);
956 if (status
&& strcmp(status
, "OK") == 0)
957 entryData
.result
= 1;
959 while ((token
= strwordtok(NULL
, &t
))) {
960 value
= strchr(token
, '=');
963 *value
++ = '\0'; /* terminate the token, and move up to the value */
965 if (strcmp(token
, "user") == 0)
966 entryData
.user
= value
;
967 else if (strcmp(token
, "message") == 0)
968 entryData
.message
= value
;
969 else if (strcmp(token
, "error") == 0)
970 entryData
.message
= value
;
971 else if (strcmp(token
, "tag") == 0)
972 entryData
.tag
= value
;
973 else if (strcmp(token
, "log") == 0)
974 entryData
.log
= value
;
975 else if (strcmp(token
, "password") == 0)
976 entryData
.password
= value
;
977 else if (strcmp(token
, "passwd") == 0)
978 entryData
.password
= value
;
979 else if (strcmp(token
, "login") == 0)
980 entryData
.user
= value
;
985 dlinkDelete(&state
->list
, &state
->def
->queue
);
987 if (cbdataReferenceValid(state
->def
)) {
989 entry
= external_acl_cache_add(state
->def
, state
->key
, entryData
);
992 entry
= (external_acl_entry
*)hash_lookup(state
->def
->cache
, state
->key
);
994 external_acl_entry
*oldentry
= (external_acl_entry
*)hash_lookup(state
->def
->cache
, state
->key
);
997 external_acl_cache_delete(state
->def
, oldentry
);
1004 cbdataReferenceDone(state
->def
);
1006 if (state
->callback
&& cbdataReferenceValidDone(state
->callback_data
, &cbdata
))
1007 state
->callback(cbdata
, entry
);
1009 next
= state
->queue
;
1018 ACLExternal::ExternalAclLookup(ACLChecklist
* ch
, ACLExternal
* me
, EAH
* callback
, void *callback_data
)
1021 external_acl_data
*acl
= static_cast<external_acl_data
*>(me
->data
);
1022 external_acl
*def
= acl
->def
;
1023 externalAclState
*state
;
1025 externalAclState
*oldstate
= NULL
;
1028 if (acl
->def
->require_auth
) {
1030 /* Make sure the user is authenticated */
1032 if ((ti
= ch
->authenticated()) != 1) {
1033 debug(82, 1) ("externalAclLookup: %s user authentication failure (%d)\n", acl
->def
->name
, ti
);
1034 callback(callback_data
, NULL
);
1039 const char *key
= makeExternalAclKey(ch
, acl
);
1042 debug(82, 1) ("externalAclLookup: lookup in '%s', prerequisit failure\n", def
->name
);
1043 callback(callback_data
, NULL
);
1047 debug(82, 2) ("externalAclLookup: lookup in '%s' for '%s'\n", def
->name
, key
);
1048 external_acl_entry
*entry
= static_cast<external_acl_entry
*>(hash_lookup(def
->cache
, key
));
1050 if (entry
&& external_acl_entry_expired(def
, entry
))
1053 /* Check for a pending lookup to hook into */
1054 for (node
= def
->queue
.head
; node
; node
= node
->next
) {
1055 externalAclState
*oldstatetmp
= static_cast<externalAclState
*>(node
->data
);
1057 if (strcmp(key
, oldstatetmp
->key
) == 0) {
1058 oldstate
= oldstatetmp
;
1063 if (entry
&& external_acl_grace_expired(def
, entry
)) {
1065 callback(callback_data
, entry
);
1072 if (!graceful
&& entry
&& !external_acl_grace_expired(def
, entry
)) {
1073 /* Should not really happen, but why not.. */
1074 callback(callback_data
, entry
);
1078 /* No pending lookup found. Sumbit to helper */
1079 state
= cbdataAlloc(externalAclState
);
1081 state
->def
= cbdataReference(def
);
1083 state
->key
= xstrdup(key
);
1086 state
->callback
= callback
;
1087 state
->callback_data
= cbdataReference(callback_data
);
1091 /* Hook into pending lookup */
1092 state
->queue
= oldstate
->queue
;
1093 oldstate
->queue
= state
;
1095 /* Check for queue overload */
1097 if (def
->theHelper
->stats
.queue_size
>= def
->theHelper
->n_running
) {
1098 debug(82, 1) ("externalAclLookup: '%s' queue overload\n", def
->name
);
1100 callback(callback_data
, entry
);
1104 /* Send it off to the helper */
1105 memBufDefInit(&buf
);
1107 memBufPrintf(&buf
, "%s\n", key
);
1109 helperSubmit(def
->theHelper
, buf
.buf
, externalAclHandleReply
, state
);
1111 dlinkAdd(state
, &state
->list
, &def
->queue
);
1117 /* No need to wait during grace period */
1118 callback(callback_data
, entry
);
1124 externalAclStats(StoreEntry
* sentry
)
1128 for (p
= Config
.externalAclHelperList
; p
; p
= p
->next
) {
1129 storeAppendPrintf(sentry
, "External ACL Statistics: %s\n", p
->name
);
1130 storeAppendPrintf(sentry
, "Cache size: %d\n", p
->cache
->count
);
1131 helperStats(sentry
, p
->theHelper
);
1132 storeAppendPrintf(sentry
, "\n");
1137 externalAclInit(void)
1139 static int firstTimeInit
= 1;
1142 for (p
= Config
.externalAclHelperList
; p
; p
= p
->next
) {
1144 p
->cache
= hash_create((HASHCMP
*) strcmp
, hashPrime(1024), hash4
);
1147 p
->theHelper
= helperCreate(p
->name
);
1149 p
->theHelper
->cmdline
= p
->cmdline
;
1151 p
->theHelper
->n_to_start
= p
->children
;
1153 p
->theHelper
->concurrency
= p
->concurrency
;
1155 p
->theHelper
->ipc_type
= IPC_TCP_SOCKET
;
1157 helperOpenServers(p
->theHelper
);
1160 if (firstTimeInit
) {
1162 cachemgrRegister("external_acl",
1163 "External ACL stats",
1164 externalAclStats
, 0, 1);
1165 CBDATA_INIT_TYPE_FREECB(externalAclState
, free_externalAclState
);
1170 externalAclShutdown(void)
1174 for (p
= Config
.externalAclHelperList
; p
; p
= p
->next
) {
1175 helperShutdown(p
->theHelper
);
1179 ExternalACLLookup
ExternalACLLookup::instance_
;
1181 ExternalACLLookup::Instance()
1187 ExternalACLLookup::checkForAsync(ACLChecklist
*checklist
)const
1189 /* TODO: optimise this - we probably have a pointer to this
1190 * around somewhere */
1191 acl
*acl
= ACL::FindByName(AclMatchedName
);
1192 ACLExternal
*me
= dynamic_cast<ACLExternal
*> (acl
);
1194 checklist
->asyncInProgress(true);
1195 ACLExternal::ExternalAclLookup(checklist
, me
, LookupDone
, checklist
);
1199 ExternalACLLookup::LookupDone(void *data
, void *result
)
1201 ACLChecklist
*checklist
= (ACLChecklist
*)data
;
1202 checklist
->extacl_entry
= cbdataReference((external_acl_entry
*)result
);
1203 checklist
->asyncInProgress(false);
1204 checklist
->changeState (ACLChecklist::NullState::Instance());
1208 /* This registers "external" in the registry. To do dynamic definitions
1209 * of external ACL's, rather than a static prototype, have a Prototype instance
1210 * prototype in the class that defines each external acl 'class'.
1211 * Then, then the external acl instance is created, it self registers under
1213 * Be sure that clone is fully functional for that acl class though!
1215 ACL::Prototype
ACLExternal::RegistryProtoype(&ACLExternal::RegistryEntry_
, "external");
1217 ACLExternal
ACLExternal::RegistryEntry_("external");
1220 ACLExternal::clone() const
1222 return new ACLExternal(*this);
1225 ACLExternal::ACLExternal (char const *theClass
) : data (NULL
), class_ (xstrdup (theClass
))
1228 ACLExternal::ACLExternal (ACLExternal
const & old
) : data (NULL
), class_ (old
.class_
? xstrdup (old
.class_
) : NULL
)
1230 /* we don't have copy constructors for the data yet */
1234 MemPool (*ACLExternal::Pool
)(NULL
);
1236 ACLExternal::operator new (size_t byteCount
)
1238 /* derived classes with different sizes must implement their own new */
1239 assert (byteCount
== sizeof (ACLExternal
));
1242 Pool
= memPoolCreate("ACLExternal", sizeof (ACLExternal
));
1244 return memPoolAlloc(Pool
);
1248 ACLExternal::operator delete (void *address
)
1250 memPoolFree (Pool
, address
);
1254 ACLExternal::typeString() const
1260 ACLExternal::valid () const
1262 return data
!= NULL
;