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