]> git.ipfire.org Git - thirdparty/squid.git/blame - src/external_acl.cc
bootstrapped
[thirdparty/squid.git] / src / external_acl.cc
CommitLineData
d9572179 1
2/*
8000a965 3 * $Id: external_acl.cc,v 1.25 2003/02/12 06:11:03 robertc Exp $
d9572179 4 *
5 * DEBUG: section 82 External ACL
6 * AUTHOR: Henrik Nordstrom, MARA Systems AB
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
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.
17 *
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.
26 *
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.
31 *
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.
36 *
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.
40 *
41 */
42
43#include "squid.h"
e6ccf245 44#include "authenticate.h"
45#include "Store.h"
ecb04c8c 46#include "fde.h"
4fb35c3c 47#include "ACLChecklist.h"
8000a965 48#include "ACL.h"
d9572179 49
50#ifndef DEFAULT_EXTERNAL_ACL_TTL
51#define DEFAULT_EXTERNAL_ACL_TTL 1 * 60 * 60
52#endif
53#ifndef DEFAULT_EXTERNAL_ACL_CONCURRENCY
54#define DEFAULT_EXTERNAL_ACL_CONCURRENCY 5
55#endif
56
57typedef struct _external_acl_format external_acl_format;
58typedef struct _external_acl_data external_acl_data;
59
4fb35c3c 60static char *makeExternalAclKey(ACLChecklist * ch, external_acl_data * acl_data);
d9572179 61static void external_acl_cache_delete(external_acl * def, external_acl_entry * entry);
62static int external_acl_entry_expired(external_acl * def, external_acl_entry * entry);
63static void external_acl_cache_touch(external_acl * def, external_acl_entry * entry);
64
65/*******************************************************************
66 * external_acl cache entry
67 * Used opaqueue in the interface
68 */
4a8b20e8 69struct _external_acl_entry: public hash_link {
d9572179 70 dlink_node lru;
71 int result;
72 time_t date;
73 char *user;
74 char *error;
75 external_acl *def;
76};
77
78/******************************************************************
79 * external_acl directive
80 */
81struct _external_acl {
82 external_acl *next;
83 int ttl;
84 int negative_ttl;
85 char *name;
86 external_acl_format *format;
87 wordlist *cmdline;
88 int children;
e6ccf245 89 helper *theHelper;
d9572179 90 hash_table *cache;
91 dlink_list lru_list;
92 int cache_size;
93 int cache_entries;
94 dlink_list queue;
95 int require_auth;
96};
97
98struct _external_acl_format {
99 enum {
a7ad6e4e 100 EXT_ACL_UNKNOWN,
101 EXT_ACL_LOGIN,
7a16973f 102#if USE_IDENT
d9572179 103 EXT_ACL_IDENT,
7a16973f 104#endif
d9572179 105 EXT_ACL_SRC,
106 EXT_ACL_DST,
107 EXT_ACL_PROTO,
108 EXT_ACL_PORT,
7a31d9e8 109 EXT_ACL_PATH,
d9572179 110 EXT_ACL_METHOD,
111 EXT_ACL_HEADER,
112 EXT_ACL_HEADER_MEMBER,
113 EXT_ACL_HEADER_ID,
a7ad6e4e 114 EXT_ACL_HEADER_ID_MEMBER,
115#if USE_SSL
116 EXT_ACL_USER_CERT,
117 EXT_ACL_CA_CERT,
118#endif
119 EXT_ACL_END
d9572179 120 } type;
121 external_acl_format *next;
122 char *header;
123 char *member;
124 char separator;
125 http_hdr_type header_id;
126};
127
128/* FIXME: These are not really cbdata, but it is an easy way
129 * to get them pooled, refcounted, accounted and freed properly...
130 */
131CBDATA_TYPE(external_acl);
132CBDATA_TYPE(external_acl_format);
133
134static void
135free_external_acl_format(void *data)
136{
e6ccf245 137 external_acl_format *p = static_cast<external_acl_format *>(data);
d9572179 138 safe_free(p->header);
139}
140
141static void
142free_external_acl(void *data)
143{
e6ccf245 144 external_acl *p = static_cast<external_acl *>(data);
d9572179 145 safe_free(p->name);
146 while (p->format) {
147 external_acl_format *f = p->format;
148 p->format = f->next;
149 cbdataFree(f);
150 }
151 wordlistDestroy(&p->cmdline);
e6ccf245 152 if (p->theHelper) {
153 helperShutdown(p->theHelper);
154 helperFree(p->theHelper);
155 p->theHelper = NULL;
d9572179 156 }
157 while (p->lru_list.tail)
e6ccf245 158 external_acl_cache_delete(p, static_cast<external_acl_entry *>(p->lru_list.tail->data));
d9572179 159 if (p->cache)
160 hashFreeMemory(p->cache);
161}
162
163void
164parse_externalAclHelper(external_acl ** list)
165{
166 external_acl *a;
167 char *token;
168 external_acl_format **p;
169
170 CBDATA_INIT_TYPE_FREECB(external_acl, free_external_acl);
171 CBDATA_INIT_TYPE_FREECB(external_acl_format, free_external_acl_format);
172
173 a = cbdataAlloc(external_acl);
174
175 a->ttl = DEFAULT_EXTERNAL_ACL_TTL;
176 a->negative_ttl = -1;
177 a->children = DEFAULT_EXTERNAL_ACL_CONCURRENCY;
178
179 token = strtok(NULL, w_space);
180 if (!token)
181 self_destruct();
182 a->name = xstrdup(token);
183
184 token = strtok(NULL, w_space);
185 /* Parse options */
186 while (token) {
187 if (strncmp(token, "ttl=", 4) == 0) {
188 a->ttl = atoi(token + 4);
189 } else if (strncmp(token, "negative_ttl=", 13) == 0) {
190 a->negative_ttl = atoi(token + 13);
191 } else if (strncmp(token, "concurrency=", 12) == 0) {
192 a->children = atoi(token + 12);
193 } else if (strncmp(token, "cache=", 6) == 0) {
194 a->cache_size = atoi(token + 6);
195 } else {
196 break;
197 }
198
199 token = strtok(NULL, w_space);
200 }
201 if (a->negative_ttl == -1)
202 a->negative_ttl = a->ttl;
203
204 /* Parse format */
205 p = &a->format;
206 while (token) {
207 external_acl_format *format;
208
209 /* stop on first non-format token found */
210 if (*token != '%')
211 break;
212
213 format = cbdataAlloc(external_acl_format);
214
215 if (strncmp(token, "%{", 2) == 0) {
216 /* header format */
217 char *header, *member, *end;
218 header = token + 2;
219 end = strchr(header, '}');
7a31d9e8 220 /* cut away the closing brace */
d9572179 221 if (end && strlen(end) == 1)
222 *end = '\0';
223 else
224 self_destruct();
225
226 member = strchr(header, ':');
227 if (member) {
228 /* Split in header and member */
229 *member++ = '\0';
230 if (!isalnum(*member))
231 format->separator = *member++;
232 else
233 format->separator = ',';
234 format->member = xstrdup(member);
e6ccf245 235 format->type = _external_acl_format::EXT_ACL_HEADER_MEMBER;
d9572179 236 } else {
e6ccf245 237 format->type = _external_acl_format::EXT_ACL_HEADER;
d9572179 238 }
239 format->header = xstrdup(header);
240 format->header_id = httpHeaderIdByNameDef(header, strlen(header));
241 if (format->header_id != -1) {
242 if (member)
e6ccf245 243 format->type = _external_acl_format::EXT_ACL_HEADER_ID_MEMBER;
d9572179 244 else
e6ccf245 245 format->type = _external_acl_format::EXT_ACL_HEADER_ID;
d9572179 246 }
bac6d4bd 247 } else if (strcmp(token, "%LOGIN") == 0) {
e6ccf245 248 format->type = _external_acl_format::EXT_ACL_LOGIN;
d9572179 249 a->require_auth = 1;
7a16973f 250 }
251#if USE_IDENT
8f45b34c 252 else if (strcmp(token, "%IDENT") == 0)
e6ccf245 253 format->type = _external_acl_format::EXT_ACL_IDENT;
7a16973f 254#endif
d9572179 255 else if (strcmp(token, "%SRC") == 0)
e6ccf245 256 format->type = _external_acl_format::EXT_ACL_SRC;
d9572179 257 else if (strcmp(token, "%DST") == 0)
e6ccf245 258 format->type = _external_acl_format::EXT_ACL_DST;
d9572179 259 else if (strcmp(token, "%PROTO") == 0)
e6ccf245 260 format->type = _external_acl_format::EXT_ACL_PROTO;
d9572179 261 else if (strcmp(token, "%PORT") == 0)
e6ccf245 262 format->type = _external_acl_format::EXT_ACL_PORT;
7a31d9e8 263 else if (strcmp(token, "%PATH") == 0)
e6ccf245 264 format->type = _external_acl_format::EXT_ACL_PATH;
d9572179 265 else if (strcmp(token, "%METHOD") == 0)
e6ccf245 266 format->type = _external_acl_format::EXT_ACL_METHOD;
a7ad6e4e 267#if USE_SSL
268 else if (strncmp(token, "%USER_CERT_", 11)) {
269 format->type = _external_acl_format::EXT_ACL_USER_CERT;
270 format->header = xstrdup(token + 11);
271 } else if (strncmp(token, "%CA_CERT_", 11)) {
272 format->type = _external_acl_format::EXT_ACL_USER_CERT;
273 format->header = xstrdup(token + 11);
274 }
275#endif
d9572179 276 else {
277 self_destruct();
278 }
279 *p = format;
280 p = &format->next;
281 token = strtok(NULL, w_space);
282 }
283
284 /* There must be at least one format token */
285 if (!a->format)
286 self_destruct();
287
288 /* helper */
289 if (!token)
290 self_destruct();
291 wordlistAdd(&a->cmdline, token);
292
293 /* arguments */
a336d130 294 parse_wordlist(&a->cmdline);
d9572179 295
296 while (*list)
297 list = &(*list)->next;
298 *list = a;
299}
300
301void
302dump_externalAclHelper(StoreEntry * sentry, const char *name, const external_acl * list)
303{
304 const external_acl *node;
305 const external_acl_format *format;
306 const wordlist *word;
307 for (node = list; node; node = node->next) {
308 storeAppendPrintf(sentry, "%s %s", name, node->name);
309 if (node->ttl != DEFAULT_EXTERNAL_ACL_TTL)
310 storeAppendPrintf(sentry, " ttl=%d", node->ttl);
311 if (node->negative_ttl != node->ttl)
312 storeAppendPrintf(sentry, " negative_ttl=%d", node->negative_ttl);
313 if (node->children != DEFAULT_EXTERNAL_ACL_CONCURRENCY)
314 storeAppendPrintf(sentry, " concurrency=%d", node->children);
315 for (format = node->format; format; format = format->next) {
316 switch (format->type) {
e6ccf245 317 case _external_acl_format::EXT_ACL_HEADER:
318 case _external_acl_format::EXT_ACL_HEADER_ID:
d9572179 319 storeAppendPrintf(sentry, " %%{%s}", format->header);
320 break;
e6ccf245 321 case _external_acl_format::EXT_ACL_HEADER_MEMBER:
322 case _external_acl_format::EXT_ACL_HEADER_ID_MEMBER:
d9572179 323 storeAppendPrintf(sentry, " %%{%s:%s}", format->header, format->member);
324 break;
325#define DUMP_EXT_ACL_TYPE(a) \
e6ccf245 326 case _external_acl_format::EXT_ACL_##a: \
d9572179 327 storeAppendPrintf(sentry, " %%%s", #a); \
328 break
329 DUMP_EXT_ACL_TYPE(LOGIN);
8f45b34c 330#if USE_IDENT
d9572179 331 DUMP_EXT_ACL_TYPE(IDENT);
8f45b34c 332#endif
d9572179 333 DUMP_EXT_ACL_TYPE(SRC);
334 DUMP_EXT_ACL_TYPE(DST);
335 DUMP_EXT_ACL_TYPE(PROTO);
336 DUMP_EXT_ACL_TYPE(PORT);
bd62836d 337 DUMP_EXT_ACL_TYPE(PATH);
d9572179 338 DUMP_EXT_ACL_TYPE(METHOD);
1f183752 339#if USE_SSL
a7ad6e4e 340 case _external_acl_format::EXT_ACL_USER_CERT:
341 storeAppendPrintf(sentry, " %%USER_CERT_%s", format->header);
342 break;
343 case _external_acl_format::EXT_ACL_CA_CERT:
344 storeAppendPrintf(sentry, " %%USER_CERT_%s", format->header);
345 break;
1f183752 346#endif
a7ad6e4e 347 case _external_acl_format::EXT_ACL_UNKNOWN:
348 case _external_acl_format::EXT_ACL_END:
349 fatal("unknown external_acl format error");
350 break;
d9572179 351 }
352 }
353 for (word = node->cmdline; word; word = word->next)
354 storeAppendPrintf(sentry, " %s", word->key);
355 storeAppendPrintf(sentry, "\n");
356 }
357}
358
359void
360free_externalAclHelper(external_acl ** list)
361{
362 while (*list) {
363 external_acl *node = *list;
364 *list = node->next;
365 node->next = NULL;
366 cbdataFree(node);
367 }
368}
369
370static external_acl *
371find_externalAclHelper(const char *name)
372{
373 external_acl *node;
374
375 for (node = Config.externalAclHelperList; node; node = node->next) {
376 if (strcmp(node->name, name) == 0)
377 return node;
378 }
379 return NULL;
380}
381
382
383/******************************************************************
384 * external acl type
385 */
386
387struct _external_acl_data {
388 external_acl *def;
389 wordlist *arguments;
390};
391
392CBDATA_TYPE(external_acl_data);
393static void
394free_external_acl_data(void *data)
395{
e6ccf245 396 external_acl_data *p = static_cast<external_acl_data *>(data);
d9572179 397 wordlistDestroy(&p->arguments);
398 cbdataReferenceDone(p->def);
399}
400
401void
402aclParseExternal(void *dataptr)
403{
e6ccf245 404 external_acl_data **datap = static_cast<external_acl_data **>(dataptr);
d9572179 405 external_acl_data *data;
406 char *token;
407 if (*datap)
408 self_destruct();
409 CBDATA_INIT_TYPE_FREECB(external_acl_data, free_external_acl_data);
410 data = cbdataAlloc(external_acl_data);
411 token = strtok(NULL, w_space);
412 if (!token)
413 self_destruct();
414 data->def = cbdataReference(find_externalAclHelper(token));
415 if (!data->def)
416 self_destruct();
4d091667 417 while ((token = strtokFile())) {
d9572179 418 wordlistAdd(&data->arguments, token);
419 }
420 *datap = data;
421}
422
423void
424aclDestroyExternal(void **dataptr)
425{
426 cbdataFree(*dataptr);
427}
428
429int
4fb35c3c 430aclMatchExternal(void *data, ACLChecklist * ch)
d9572179 431{
432 int result;
433 external_acl_entry *entry;
e6ccf245 434 external_acl_data *acl = static_cast<external_acl_data *>(data);
d9572179 435 const char *key = "";
436 debug(82, 9) ("aclMatchExternal: acl=\"%s\"\n", acl->def->name);
437 entry = ch->extacl_entry;
438 if (entry) {
439 if (cbdataReferenceValid(entry) && entry->def == acl->def &&
4a8b20e8 440 strcmp((char *)entry->key, key) == 0) {
d9572179 441 /* Ours, use it.. */
442 } else {
443 /* Not valid, or not ours.. get rid of it */
444 cbdataReferenceDone(ch->extacl_entry);
445 entry = NULL;
446 }
447 }
448 if (!entry) {
449 if (acl->def->require_auth) {
450 int ti;
451 /* Make sure the user is authenticated */
452 if ((ti = aclAuthenticated(ch)) != 1) {
453 debug(82, 2) ("aclMatchExternal: %s user not authenticated (%d)\n", acl->def->name, ti);
454 return ti;
455 }
456 }
457 key = makeExternalAclKey(ch, acl);
e6ccf245 458 entry = static_cast<external_acl_entry *>(hash_lookup(acl->def->cache, key));
d9572179 459 if (entry && external_acl_entry_expired(acl->def, entry)) {
460 /* Expired entry, ignore */
461 debug(82, 2) ("external_acl_cache_lookup: '%s' = expired\n", key);
462 entry = NULL;
463 }
464 ch->auth_user_request = NULL;
465 }
466 if (!entry) {
467 debug(82, 2) ("aclMatchExternal: %s(\"%s\") = lookup needed\n", acl->def->name, key);
468 ch->state[ACL_EXTERNAL] = ACL_LOOKUP_NEEDED;
469 return 0;
470 }
471 external_acl_cache_touch(acl->def, entry);
472 result = entry->result;
473 debug(82, 2) ("aclMatchExternal: %s = %d\n", acl->def->name, result);
474 /* FIXME: This should allocate it's own storage in the request. This
475 * piggy backs on ident, and may fail if there is child proxies..
476 * Register the username for logging purposes
477 */
8000a965 478 if (entry->user && cbdataReferenceValid(ch->conn()) && !ch->conn()->rfc931[0])
479 xstrncpy(ch->conn()->rfc931, entry->user, USER_IDENT_SZ);
d9572179 480 return result;
481}
482
483wordlist *
484aclDumpExternal(void *data)
485{
e6ccf245 486 external_acl_data *acl = static_cast<external_acl_data *>(data);
d9572179 487 wordlist *result = NULL;
488 wordlist *arg;
489 MemBuf mb;
490 memBufDefInit(&mb);
491 memBufPrintf(&mb, "%s", acl->def->name);
492 for (arg = acl->arguments; arg; arg = arg->next) {
493 memBufPrintf(&mb, " %s", arg->key);
494 }
495 wordlistAdd(&result, mb.buf);
496 memBufClean(&mb);
497 return result;
498}
499
500/******************************************************************
501 * external_acl cache
502 */
503
504CBDATA_TYPE(external_acl_entry);
505
506static void
507external_acl_cache_touch(external_acl * def, external_acl_entry * entry)
508{
509 dlinkDelete(&entry->lru, &def->lru_list);
510 dlinkAdd(entry, &entry->lru, &def->lru_list);
511}
512
513static char *
4fb35c3c 514makeExternalAclKey(ACLChecklist * ch, external_acl_data * acl_data)
d9572179 515{
516 static MemBuf mb = MemBufNULL;
517 char buf[256];
518 int first = 1;
519 wordlist *arg;
520 external_acl_format *format;
521 request_t *request = ch->request;
522 String sb = StringNull;
523 memBufReset(&mb);
524 for (format = acl_data->def->format; format; format = format->next) {
525 const char *str = NULL;
526 switch (format->type) {
e6ccf245 527 case _external_acl_format::EXT_ACL_LOGIN:
d9572179 528 str = authenticateUserRequestUsername(request->auth_user_request);
529 break;
7a16973f 530#if USE_IDENT
e6ccf245 531 case _external_acl_format::EXT_ACL_IDENT:
d9572179 532 str = ch->rfc931;
533 if (!str) {
534 ch->state[ACL_IDENT] = ACL_LOOKUP_NEEDED;
2300dd02 535 return NULL;
d9572179 536 }
537 break;
7a16973f 538#endif
e6ccf245 539 case _external_acl_format::EXT_ACL_SRC:
d9572179 540 str = inet_ntoa(ch->src_addr);
541 break;
e6ccf245 542 case _external_acl_format::EXT_ACL_DST:
d9572179 543 str = request->host;
544 break;
e6ccf245 545 case _external_acl_format::EXT_ACL_PROTO:
d9572179 546 str = ProtocolStr[request->protocol];
547 break;
e6ccf245 548 case _external_acl_format::EXT_ACL_PORT:
d9572179 549 snprintf(buf, sizeof(buf), "%d", request->port);
550 str = buf;
8cc0736e 551 break;
e6ccf245 552 case _external_acl_format::EXT_ACL_PATH:
528b2c61 553 str = request->urlpath.buf();
7a31d9e8 554 break;
e6ccf245 555 case _external_acl_format::EXT_ACL_METHOD:
d9572179 556 str = RequestMethodStr[request->method];
557 break;
e6ccf245 558 case _external_acl_format::EXT_ACL_HEADER:
d9572179 559 sb = httpHeaderGetByName(&request->header, format->header);
528b2c61 560 str = sb.buf();
d9572179 561 break;
e6ccf245 562 case _external_acl_format::EXT_ACL_HEADER_ID:
d9572179 563 sb = httpHeaderGetStrOrList(&request->header, format->header_id);
528b2c61 564 str = sb.buf();
d9572179 565 break;
e6ccf245 566 case _external_acl_format::EXT_ACL_HEADER_MEMBER:
d9572179 567 sb = httpHeaderGetByNameListMember(&request->header, format->header, format->member, format->separator);
528b2c61 568 str = sb.buf();
d9572179 569 break;
e6ccf245 570 case _external_acl_format::EXT_ACL_HEADER_ID_MEMBER:
d9572179 571 sb = httpHeaderGetListMember(&request->header, format->header_id, format->member, format->separator);
528b2c61 572 str = sb.buf();
d9572179 573 break;
a7ad6e4e 574#if USE_SSL
575 case _external_acl_format::EXT_ACL_USER_CERT:
576 if (cbdataReferenceValid(ch->conn)) {
577 SSL *ssl = fd_table[ch->conn->fd].ssl;
578 if (ssl)
579 str = sslGetUserAttribute(ssl, format->header);
580 }
581 break;
582 case _external_acl_format::EXT_ACL_CA_CERT:
583 if (cbdataReferenceValid(ch->conn)) {
584 SSL *ssl = fd_table[ch->conn->fd].ssl;
585 if (ssl)
586 str = sslGetCAAttribute(ssl, format->header);
587 }
588 break;
589#endif
590 case _external_acl_format::EXT_ACL_UNKNOWN:
591 case _external_acl_format::EXT_ACL_END:
592 fatal("unknown external_acl format error");
593 break;
d9572179 594 }
595 if (str)
596 if (!*str)
597 str = NULL;
598 if (!str)
599 str = "-";
255a8142 600 if (!first)
601 memBufAppend(&mb, " ", 1);
602 strwordquote(&mb, str);
528b2c61 603 sb.clean();
d9572179 604 first = 0;
605 }
606 for (arg = acl_data->arguments; arg; arg = arg->next) {
255a8142 607 if (!first)
608 memBufAppend(&mb, " ", 1);
609 strwordquote(&mb, arg->key);
d9572179 610 first = 0;
611 }
612 return mb.buf;
d9572179 613}
614
615static int
616external_acl_entry_expired(external_acl * def, external_acl_entry * entry)
617{
618 if (entry->date + (entry->result == 1 ? def->ttl : def->negative_ttl) < squid_curtime)
619 return 1;
620 else
621 return 0;
622}
e6ccf245 623
d9572179 624static void
625free_external_acl_entry(void *data)
626{
e6ccf245 627 external_acl_entry *entry = static_cast<external_acl_entry *>(data);
4a8b20e8 628 safe_free(entry->key);
d9572179 629 safe_free(entry->user);
630 safe_free(entry->error);
631}
632
633static external_acl_entry *
634external_acl_cache_add(external_acl * def, const char *key, int result, char *user, char *error)
635{
e6ccf245 636 external_acl_entry *entry = static_cast<external_acl_entry *>(hash_lookup(def->cache, key));
d9572179 637 debug(82, 2) ("external_acl_cache_add: Adding '%s' = %d\n", key, result);
638 if (entry) {
639 debug(82, 3) ("external_acl_cache_add: updating existing entry\n");
640 entry->date = squid_curtime;
641 entry->result = result;
642 safe_free(entry->user);
643 safe_free(entry->error);
644 if (user)
645 entry->user = xstrdup(user);
646 if (error)
647 entry->error = xstrdup(error);
648 external_acl_cache_touch(def, entry);
649 return entry;
650 }
651 CBDATA_INIT_TYPE_FREECB(external_acl_entry, free_external_acl_entry);
652 /* Maintain cache size */
653 if (def->cache_size && def->cache_entries >= def->cache_size)
e6ccf245 654 external_acl_cache_delete(def, static_cast<external_acl_entry *>(def->lru_list.tail->data));
d9572179 655 entry = cbdataAlloc(external_acl_entry);
4a8b20e8 656 entry->key = xstrdup(key);
d9572179 657 entry->date = squid_curtime;
658 entry->result = result;
659 if (user)
660 entry->user = xstrdup(user);
661 if (error)
662 entry->error = xstrdup(error);
663 entry->def = def;
4a8b20e8 664 hash_join(def->cache, entry);
d9572179 665 dlinkAdd(entry, &entry->lru, &def->lru_list);
666 def->cache_entries += 1;
667 return entry;
668}
669
670static void
671external_acl_cache_delete(external_acl * def, external_acl_entry * entry)
672{
4a8b20e8 673 hash_remove_link(def->cache, entry);
d9572179 674 dlinkDelete(&entry->lru, &def->lru_list);
675 def->cache_entries -= 1;
676 cbdataFree(entry);
677}
678
679/******************************************************************
680 * external_acl helpers
681 */
682
683typedef struct _externalAclState externalAclState;
684struct _externalAclState {
685 EAH *callback;
686 void *callback_data;
687 char *key;
688 external_acl *def;
689 dlink_node list;
690 externalAclState *queue;
691};
692
693CBDATA_TYPE(externalAclState);
694static void
695free_externalAclState(void *data)
696{
e6ccf245 697 externalAclState *state = static_cast<externalAclState *>(data);
d9572179 698 safe_free(state->key);
699 cbdataReferenceDone(state->callback_data);
700 cbdataReferenceDone(state->def);
701}
702
d9572179 703/*
704 * The helper program receives queries on stdin, one
705 * per line, and must return the result on on stdout as
706 * OK user="Users login name"
707 * on success, and
708 * ERR error="Description of the error"
709 * on error (the user/error options are optional)
710 *
711 * General result syntax:
712 *
713 * OK/ERR keyword=value ...
714 *
715 * Keywords:
716 *
717 * user= The users name (login)
718 * error= Error description (only defined for ERR results)
719 *
720 * Other keywords may be added to the protocol later
721 *
722 * value needs to be enclosed in quotes if it may contain whitespace, or
723 * the whitespace escaped using \ (\ escaping obviously also applies to
724 * any " characters)
725 */
726
727static void
728externalAclHandleReply(void *data, char *reply)
729{
e6ccf245 730 externalAclState *state = static_cast<externalAclState *>(data);
d9572179 731 externalAclState *next;
732 int result = 0;
733 char *status;
734 char *token;
735 char *value;
736 char *t;
737 char *user = NULL;
738 char *error = NULL;
739 external_acl_entry *entry;
740
741 debug(82, 2) ("externalAclHandleReply: reply=\"%s\"\n", reply);
742
4960475f 743 if (reply) {
744 status = strwordtok(reply, &t);
745 if (status && strcmp(status, "OK") == 0)
746 result = 1;
747
748 while ((token = strwordtok(NULL, &t))) {
749 value = strchr(token, '=');
750 if (value) {
751 *value++ = '\0'; /* terminate the token, and move up to the value */
752 if (strcmp(token, "user") == 0)
753 user = value;
754 else if (strcmp(token, "error") == 0)
755 error = value;
756 }
d9572179 757 }
758 }
d9572179 759 dlinkDelete(&state->list, &state->def->queue);
4960475f 760 if (cbdataReferenceValid(state->def)) {
761 if (reply)
762 entry = external_acl_cache_add(state->def, state->key, result, user, error);
763 else {
302138c5 764 entry = (external_acl_entry *)hash_lookup(state->def->cache, state->key);
4960475f 765 if (entry)
766 external_acl_cache_delete(state->def, entry);
767 }
768 } else
d9572179 769 entry = NULL;
770
771 do {
772 void *cbdata;
773 cbdataReferenceDone(state->def);
774
775 if (cbdataReferenceValidDone(state->callback_data, &cbdata))
776 state->callback(cbdata, entry);
777
778 next = state->queue;
779 cbdataFree(state);
780 state = next;
781 } while (state);
782}
783
784void
4fb35c3c 785externalAclLookup(ACLChecklist * ch, void *acl_data, EAH * callback, void *callback_data)
d9572179 786{
787 MemBuf buf;
e6ccf245 788 external_acl_data *acl = static_cast<external_acl_data *>(acl_data);
d9572179 789 external_acl *def = acl->def;
790 const char *key = makeExternalAclKey(ch, acl);
e6ccf245 791 external_acl_entry *entry = static_cast<external_acl_entry *>(hash_lookup(def->cache, key));
d9572179 792 externalAclState *state;
793 debug(82, 2) ("externalAclLookup: lookup in '%s' for '%s'\n", def->name, key);
794 if (!key) {
795 debug(82, 1) ("externalAclLookup: lookup in '%s', prerequisit failure\n", def->name);
796 callback(callback_data, NULL);
797 return;
798 }
799 state = cbdataAlloc(externalAclState);
800 state->def = cbdataReference(def);
801 state->callback = callback;
802 state->callback_data = cbdataReference(callback_data);
803 state->key = xstrdup(key);
804 if (entry && !external_acl_entry_expired(def, entry)) {
805 if (entry->result == -1) {
806 /* There is a pending lookup. Hook into it */
807 dlink_node *node;
808 for (node = def->queue.head; node; node = node->next) {
e6ccf245 809 externalAclState *oldstate = static_cast<externalAclState *>(node->data);
d9572179 810 if (strcmp(state->key, oldstate->key) == 0) {
811 state->queue = oldstate->queue;
812 oldstate->queue = state;
813 return;
814 }
815 }
816 } else {
817 /* There is a cached valid result.. use it */
818 /* This should not really happen, but what the heck.. */
819 callback(callback_data, entry);
820 cbdataFree(state);
821 return;
822 }
823 }
824 /* Check for queue overload */
e6ccf245 825 if (def->theHelper->stats.queue_size >= def->theHelper->n_running) {
d9572179 826 int result = -1;
e6ccf245 827 external_acl_entry *entry = static_cast<external_acl_entry *>(hash_lookup(def->cache, key));
d9572179 828 debug(82, 1) ("externalAclLookup: '%s' queue overload\n", def->name);
829 if (entry)
830 result = entry->result;
831 cbdataFree(state);
832 callback(callback_data, entry);
833 return;
834 }
835 /* Send it off to the helper */
836 memBufDefInit(&buf);
837 memBufPrintf(&buf, "%s\n", key);
e6ccf245 838 helperSubmit(def->theHelper, buf.buf, externalAclHandleReply, state);
d9572179 839 external_acl_cache_add(def, key, -1, NULL, NULL);
840 dlinkAdd(state, &state->list, &def->queue);
841 memBufClean(&buf);
842}
843
844static void
845externalAclStats(StoreEntry * sentry)
846{
847 external_acl *p;
848
849 for (p = Config.externalAclHelperList; p; p = p->next) {
850 storeAppendPrintf(sentry, "External ACL Statistics: %s\n", p->name);
851 storeAppendPrintf(sentry, "Cache size: %d\n", p->cache->count);
e6ccf245 852 helperStats(sentry, p->theHelper);
d9572179 853 storeAppendPrintf(sentry, "\n");
854 }
855}
856
857void
858externalAclInit(void)
859{
860 static int firstTimeInit = 1;
861 external_acl *p;
862
863 for (p = Config.externalAclHelperList; p; p = p->next) {
864 if (!p->cache)
865 p->cache = hash_create((HASHCMP *) strcmp, hashPrime(1024), hash4);
e6ccf245 866 if (!p->theHelper)
867 p->theHelper = helperCreate(p->name);
868 p->theHelper->cmdline = p->cmdline;
869 p->theHelper->n_to_start = p->children;
870 p->theHelper->ipc_type = IPC_TCP_SOCKET;
871 helperOpenServers(p->theHelper);
d9572179 872 }
873 if (firstTimeInit) {
874 firstTimeInit = 0;
875 cachemgrRegister("external_acl",
876 "External ACL stats",
877 externalAclStats, 0, 1);
878 CBDATA_INIT_TYPE_FREECB(externalAclState, free_externalAclState);
879 }
880}
881
882void
883externalAclShutdown(void)
884{
885 external_acl *p;
886 for (p = Config.externalAclHelperList; p; p = p->next) {
e6ccf245 887 helperShutdown(p->theHelper);
d9572179 888 }
889}