]> git.ipfire.org Git - thirdparty/squid.git/blame - src/external_acl.cc
Fix diskd debugging to print the correct unlink function name, either
[thirdparty/squid.git] / src / external_acl.cc
CommitLineData
d9572179 1
2/*
a9925b40 3 * $Id: external_acl.cc,v 1.69 2006/05/06 22:13:18 wessels 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"
225b7b10 44#include "ExternalACL.h"
1e5562e3 45#include "ExternalACLEntry.h"
f5691f9c 46#include "AuthUserRequest.h"
e6ccf245 47#include "Store.h"
ecb04c8c 48#include "fde.h"
4fb35c3c 49#include "ACLChecklist.h"
8000a965 50#include "ACL.h"
3841dd46 51#if USE_IDENT
52#include "ACLIdent.h"
53#endif
a46d2c0e 54#include "client_side.h"
a2ac85d9 55#include "HttpRequest.h"
4b0f5de8 56#include "authenticate.h"
0eb49b6d 57#include "MemBuf.h"
d295d770 58#include "wordlist.h"
d9572179 59
60#ifndef DEFAULT_EXTERNAL_ACL_TTL
61#define DEFAULT_EXTERNAL_ACL_TTL 1 * 60 * 60
62#endif
d4f2f353 63#ifndef DEFAULT_EXTERNAL_ACL_CHILDREN
64#define DEFAULT_EXTERNAL_ACL_CHILDREN 5
d9572179 65#endif
66
67typedef struct _external_acl_format external_acl_format;
62e76326 68
4fb35c3c 69static char *makeExternalAclKey(ACLChecklist * ch, external_acl_data * acl_data);
d9572179 70static void external_acl_cache_delete(external_acl * def, external_acl_entry * entry);
71static int external_acl_entry_expired(external_acl * def, external_acl_entry * entry);
47b0c1fa 72static int external_acl_grace_expired(external_acl * def, external_acl_entry * entry);
d9572179 73static void external_acl_cache_touch(external_acl * def, external_acl_entry * entry);
1e5562e3 74static external_acl_entry *external_acl_cache_add(external_acl * def, const char *key, ExternalACLEntryData const &data);
d9572179 75
76/******************************************************************
77 * external_acl directive
78 */
62e76326 79
1e5562e3 80class external_acl
62e76326 81{
1e5562e3 82
83public:
d9572179 84 external_acl *next;
1e5562e3 85
86 void add
87 (ExternalACLEntry *);
88
89 void trimCache();
90
d9572179 91 int ttl;
1e5562e3 92
d9572179 93 int negative_ttl;
1e5562e3 94
47b0c1fa 95 int grace;
96
d9572179 97 char *name;
1e5562e3 98
d9572179 99 external_acl_format *format;
1e5562e3 100
d9572179 101 wordlist *cmdline;
1e5562e3 102
d9572179 103 int children;
1e5562e3 104
07eca7e0 105 int concurrency;
106
e6ccf245 107 helper *theHelper;
1e5562e3 108
d9572179 109 hash_table *cache;
1e5562e3 110
d9572179 111 dlink_list lru_list;
1e5562e3 112
d9572179 113 int cache_size;
1e5562e3 114
d9572179 115 int cache_entries;
1e5562e3 116
d9572179 117 dlink_list queue;
1e5562e3 118
e870b1f8 119 bool require_auth;
dc1af3cf 120
121 enum
122 {
123 QUOTE_METHOD_SHELL = 1,
124 QUOTE_METHOD_URL
125 }
126
127 quote;
d9572179 128};
129
62e76326 130struct _external_acl_format
131{
d9572179 132 enum {
62e76326 133 EXT_ACL_UNKNOWN,
134 EXT_ACL_LOGIN,
7a16973f 135#if USE_IDENT
62e76326 136 EXT_ACL_IDENT,
7a16973f 137#endif
62e76326 138 EXT_ACL_SRC,
47b0c1fa 139 EXT_ACL_SRCPORT,
140 EXT_ACL_MYADDR,
141 EXT_ACL_MYPORT,
62e76326 142 EXT_ACL_DST,
143 EXT_ACL_PROTO,
144 EXT_ACL_PORT,
145 EXT_ACL_PATH,
146 EXT_ACL_METHOD,
147 EXT_ACL_HEADER,
148 EXT_ACL_HEADER_MEMBER,
149 EXT_ACL_HEADER_ID,
150 EXT_ACL_HEADER_ID_MEMBER,
a7ad6e4e 151#if USE_SSL
62e76326 152 EXT_ACL_USER_CERT,
153 EXT_ACL_CA_CERT,
4ac9968f 154 EXT_ACL_USER_CERT_RAW,
3d61c476 155 EXT_ACL_USER_CERTCHAIN_RAW,
a7ad6e4e 156#endif
abb929f0 157 EXT_ACL_EXT_USER,
62e76326 158 EXT_ACL_END
d9572179 159 } type;
160 external_acl_format *next;
161 char *header;
162 char *member;
163 char separator;
164 http_hdr_type header_id;
165};
166
167/* FIXME: These are not really cbdata, but it is an easy way
168 * to get them pooled, refcounted, accounted and freed properly...
169 */
170CBDATA_TYPE(external_acl);
171CBDATA_TYPE(external_acl_format);
172
173static void
174free_external_acl_format(void *data)
175{
e6ccf245 176 external_acl_format *p = static_cast<external_acl_format *>(data);
d9572179 177 safe_free(p->header);
178}
179
180static void
181free_external_acl(void *data)
182{
e6ccf245 183 external_acl *p = static_cast<external_acl *>(data);
d9572179 184 safe_free(p->name);
62e76326 185
d9572179 186 while (p->format) {
62e76326 187 external_acl_format *f = p->format;
188 p->format = f->next;
189 cbdataFree(f);
d9572179 190 }
62e76326 191
d9572179 192 wordlistDestroy(&p->cmdline);
62e76326 193
e6ccf245 194 if (p->theHelper) {
62e76326 195 helperShutdown(p->theHelper);
196 helperFree(p->theHelper);
197 p->theHelper = NULL;
d9572179 198 }
62e76326 199
d9572179 200 while (p->lru_list.tail)
62e76326 201 external_acl_cache_delete(p, static_cast<external_acl_entry *>(p->lru_list.tail->data));
d9572179 202 if (p->cache)
62e76326 203 hashFreeMemory(p->cache);
d9572179 204}
205
206void
207parse_externalAclHelper(external_acl ** list)
208{
209 external_acl *a;
210 char *token;
211 external_acl_format **p;
212
213 CBDATA_INIT_TYPE_FREECB(external_acl, free_external_acl);
214 CBDATA_INIT_TYPE_FREECB(external_acl_format, free_external_acl_format);
215
216 a = cbdataAlloc(external_acl);
217
218 a->ttl = DEFAULT_EXTERNAL_ACL_TTL;
219 a->negative_ttl = -1;
d4f2f353 220 a->children = DEFAULT_EXTERNAL_ACL_CHILDREN;
d9572179 221
222 token = strtok(NULL, w_space);
62e76326 223
d9572179 224 if (!token)
62e76326 225 self_destruct();
226
d9572179 227 a->name = xstrdup(token);
228
229 token = strtok(NULL, w_space);
62e76326 230
dc1af3cf 231 a->quote = external_acl::QUOTE_METHOD_URL;
232
d9572179 233 /* Parse options */
234 while (token) {
62e76326 235 if (strncmp(token, "ttl=", 4) == 0) {
236 a->ttl = atoi(token + 4);
237 } else if (strncmp(token, "negative_ttl=", 13) == 0) {
238 a->negative_ttl = atoi(token + 13);
07eca7e0 239 } else if (strncmp(token, "children=", 9) == 0) {
240 a->children = atoi(token + 9);
241 } else if (strncmp(token, "concurrency=", 12) == 0) {
242 a->concurrency = atoi(token + 12);
62e76326 243 } else if (strncmp(token, "cache=", 6) == 0) {
244 a->cache_size = atoi(token + 6);
47b0c1fa 245 } else if (strncmp(token, "grace=", 6) == 0) {
246 a->grace = atoi(token + 6);
dc1af3cf 247 } else if (strcmp(token, "protocol=2.5") == 0) {
248 a->quote = external_acl::QUOTE_METHOD_SHELL;
249 } else if (strcmp(token, "protocol=3.0") == 0) {
250 a->quote = external_acl::QUOTE_METHOD_URL;
251 } else if (strcmp(token, "quote=url") == 0) {
252 a->quote = external_acl::QUOTE_METHOD_URL;
253 } else if (strcmp(token, "quote=shell") == 0) {
254 a->quote = external_acl::QUOTE_METHOD_SHELL;
62e76326 255 } else {
256 break;
257 }
258
259 token = strtok(NULL, w_space);
d9572179 260 }
62e76326 261
d9572179 262 if (a->negative_ttl == -1)
62e76326 263 a->negative_ttl = a->ttl;
d9572179 264
265 /* Parse format */
266 p = &a->format;
62e76326 267
d9572179 268 while (token) {
62e76326 269 external_acl_format *format;
270
271 /* stop on first non-format token found */
272
273 if (*token != '%')
274 break;
275
276 format = cbdataAlloc(external_acl_format);
277
278 if (strncmp(token, "%{", 2) == 0) {
279 /* header format */
280 char *header, *member, *end;
281 header = token + 2;
282 end = strchr(header, '}');
283 /* cut away the closing brace */
284
285 if (end && strlen(end) == 1)
286 *end = '\0';
287 else
288 self_destruct();
289
290 member = strchr(header, ':');
291
292 if (member) {
293 /* Split in header and member */
294 *member++ = '\0';
295
296 if (!isalnum(*member))
297 format->separator = *member++;
298 else
299 format->separator = ',';
300
301 format->member = xstrdup(member);
302
303 format->type = _external_acl_format::EXT_ACL_HEADER_MEMBER;
304 } else {
305 format->type = _external_acl_format::EXT_ACL_HEADER;
306 }
307
308 format->header = xstrdup(header);
309 format->header_id = httpHeaderIdByNameDef(header, strlen(header));
310
311 if (format->header_id != -1) {
312 if (member)
313 format->type = _external_acl_format::EXT_ACL_HEADER_ID_MEMBER;
314 else
315 format->type = _external_acl_format::EXT_ACL_HEADER_ID;
316 }
317 } else if (strcmp(token, "%LOGIN") == 0) {
318 format->type = _external_acl_format::EXT_ACL_LOGIN;
e870b1f8 319 a->require_auth = true;
62e76326 320 }
321
7a16973f 322#if USE_IDENT
62e76326 323 else if (strcmp(token, "%IDENT") == 0)
324 format->type = _external_acl_format::EXT_ACL_IDENT;
325
7a16973f 326#endif
62e76326 327
328 else if (strcmp(token, "%SRC") == 0)
329 format->type = _external_acl_format::EXT_ACL_SRC;
47b0c1fa 330 else if (strcmp(token, "%SRCPORT") == 0)
331 format->type = _external_acl_format::EXT_ACL_SRCPORT;
332 else if (strcmp(token, "%MYADDR") == 0)
333 format->type = _external_acl_format::EXT_ACL_MYADDR;
334 else if (strcmp(token, "%MYPORT") == 0)
335 format->type = _external_acl_format::EXT_ACL_MYPORT;
62e76326 336 else if (strcmp(token, "%DST") == 0)
337 format->type = _external_acl_format::EXT_ACL_DST;
338 else if (strcmp(token, "%PROTO") == 0)
339 format->type = _external_acl_format::EXT_ACL_PROTO;
340 else if (strcmp(token, "%PORT") == 0)
341 format->type = _external_acl_format::EXT_ACL_PORT;
342 else if (strcmp(token, "%PATH") == 0)
343 format->type = _external_acl_format::EXT_ACL_PATH;
344 else if (strcmp(token, "%METHOD") == 0)
345 format->type = _external_acl_format::EXT_ACL_METHOD;
346
a7ad6e4e 347#if USE_SSL
62e76326 348
4ac9968f 349 else if (strcmp(token, "%USER_CERT") == 0)
3de409f0 350 format->type = _external_acl_format::EXT_ACL_USER_CERT_RAW;
3d61c476 351 else if (strcmp(token, "%USER_CERTCHAIN") == 0)
3de409f0 352 format->type = _external_acl_format::EXT_ACL_USER_CERTCHAIN_RAW;
62e76326 353 else if (strncmp(token, "%USER_CERT_", 11)) {
354 format->type = _external_acl_format::EXT_ACL_USER_CERT;
355 format->header = xstrdup(token + 11);
356 } else if (strncmp(token, "%CA_CERT_", 11)) {
357 format->type = _external_acl_format::EXT_ACL_USER_CERT;
358 format->header = xstrdup(token + 11);
359 }
360
a7ad6e4e 361#endif
abb929f0 362 else if (strcmp(token, "%EXT_USER") == 0)
363 format->type = _external_acl_format::EXT_ACL_EXT_USER;
62e76326 364 else {
365 self_destruct();
366 }
367
368 *p = format;
369 p = &format->next;
370 token = strtok(NULL, w_space);
d9572179 371 }
372
373 /* There must be at least one format token */
374 if (!a->format)
62e76326 375 self_destruct();
d9572179 376
377 /* helper */
378 if (!token)
62e76326 379 self_destruct();
380
d9572179 381 wordlistAdd(&a->cmdline, token);
382
383 /* arguments */
a336d130 384 parse_wordlist(&a->cmdline);
d9572179 385
386 while (*list)
62e76326 387 list = &(*list)->next;
388
d9572179 389 *list = a;
390}
391
392void
393dump_externalAclHelper(StoreEntry * sentry, const char *name, const external_acl * list)
394{
395 const external_acl *node;
396 const external_acl_format *format;
397 const wordlist *word;
62e76326 398
d9572179 399 for (node = list; node; node = node->next) {
62e76326 400 storeAppendPrintf(sentry, "%s %s", name, node->name);
401
402 if (node->ttl != DEFAULT_EXTERNAL_ACL_TTL)
403 storeAppendPrintf(sentry, " ttl=%d", node->ttl);
404
405 if (node->negative_ttl != node->ttl)
406 storeAppendPrintf(sentry, " negative_ttl=%d", node->negative_ttl);
407
47b0c1fa 408 if (node->grace)
409 storeAppendPrintf(sentry, " grace=%d", node->grace);
410
d4f2f353 411 if (node->children != DEFAULT_EXTERNAL_ACL_CHILDREN)
412 storeAppendPrintf(sentry, " children=%d", node->children);
62e76326 413
07eca7e0 414 if (node->concurrency)
415 storeAppendPrintf(sentry, " concurrency=%d", node->concurrency);
416
47b0c1fa 417 if (node->cache)
418 storeAppendPrintf(sentry, " cache=%d", node->cache_size);
419
62e76326 420 for (format = node->format; format; format = format->next) {
421 switch (format->type) {
422
423 case _external_acl_format::EXT_ACL_HEADER:
424
425 case _external_acl_format::EXT_ACL_HEADER_ID:
426 storeAppendPrintf(sentry, " %%{%s}", format->header);
427 break;
428
429 case _external_acl_format::EXT_ACL_HEADER_MEMBER:
430
431 case _external_acl_format::EXT_ACL_HEADER_ID_MEMBER:
432 storeAppendPrintf(sentry, " %%{%s:%s}", format->header, format->member);
433 break;
d9572179 434#define DUMP_EXT_ACL_TYPE(a) \
dc1af3cf 435 case _external_acl_format::EXT_ACL_##a: \
436 storeAppendPrintf(sentry, " %%%s", #a); \
437 break
62e76326 438
439 DUMP_EXT_ACL_TYPE(LOGIN);
8f45b34c 440#if USE_IDENT
62e76326 441
442 DUMP_EXT_ACL_TYPE(IDENT);
8f45b34c 443#endif
62e76326 444
445 DUMP_EXT_ACL_TYPE(SRC);
47b0c1fa 446 DUMP_EXT_ACL_TYPE(SRCPORT);
447 DUMP_EXT_ACL_TYPE(MYADDR);
448 DUMP_EXT_ACL_TYPE(MYPORT);
62e76326 449 DUMP_EXT_ACL_TYPE(DST);
450 DUMP_EXT_ACL_TYPE(PROTO);
451 DUMP_EXT_ACL_TYPE(PORT);
452 DUMP_EXT_ACL_TYPE(PATH);
453 DUMP_EXT_ACL_TYPE(METHOD);
1f183752 454#if USE_SSL
62e76326 455
4ac9968f 456 case _external_acl_format::EXT_ACL_USER_CERT_RAW:
457 storeAppendPrintf(sentry, " %%USER_CERT");
458 break;
459
3d61c476 460 case _external_acl_format::EXT_ACL_USER_CERTCHAIN_RAW:
461 storeAppendPrintf(sentry, " %%USER_CERTCHAIN");
462 break;
463
62e76326 464 case _external_acl_format::EXT_ACL_USER_CERT:
465 storeAppendPrintf(sentry, " %%USER_CERT_%s", format->header);
466 break;
467
468 case _external_acl_format::EXT_ACL_CA_CERT:
469 storeAppendPrintf(sentry, " %%USER_CERT_%s", format->header);
470 break;
1f183752 471#endif
62e76326 472
abb929f0 473 DUMP_EXT_ACL_TYPE(EXT_USER);
474
62e76326 475 case _external_acl_format::EXT_ACL_UNKNOWN:
476
477 case _external_acl_format::EXT_ACL_END:
478 fatal("unknown external_acl format error");
479 break;
480 }
481 }
482
483 for (word = node->cmdline; word; word = word->next)
484 storeAppendPrintf(sentry, " %s", word->key);
485
486 storeAppendPrintf(sentry, "\n");
d9572179 487 }
488}
489
490void
491free_externalAclHelper(external_acl ** list)
492{
493 while (*list) {
62e76326 494 external_acl *node = *list;
495 *list = node->next;
496 node->next = NULL;
497 cbdataFree(node);
d9572179 498 }
499}
500
501static external_acl *
502find_externalAclHelper(const char *name)
503{
504 external_acl *node;
505
506 for (node = Config.externalAclHelperList; node; node = node->next) {
62e76326 507 if (strcmp(node->name, name) == 0)
508 return node;
d9572179 509 }
62e76326 510
d9572179 511 return NULL;
512}
513
1e5562e3 514void
515
516external_acl::add
517 (ExternalACLEntry *anEntry)
518{
519 trimCache();
520 assert (anEntry->def == NULL);
521 anEntry->def = this;
522 hash_join(cache, anEntry);
523 dlinkAdd(anEntry, &anEntry->lru, &lru_list);
524 cache_entries++;
525}
526
527void
528external_acl::trimCache()
529{
530 if (cache_size && cache_entries >= cache_size)
531 external_acl_cache_delete(this, static_cast<external_acl_entry *>(lru_list.tail->data));
532}
533
d9572179 534
535/******************************************************************
536 * external acl type
537 */
538
62e76326 539struct _external_acl_data
540{
d9572179 541 external_acl *def;
542 wordlist *arguments;
543};
544
545CBDATA_TYPE(external_acl_data);
546static void
547free_external_acl_data(void *data)
548{
e6ccf245 549 external_acl_data *p = static_cast<external_acl_data *>(data);
d9572179 550 wordlistDestroy(&p->arguments);
551 cbdataReferenceDone(p->def);
552}
553
554void
b0dd28ba 555ACLExternal::parse()
d9572179 556{
d9572179 557 char *token;
62e76326 558
b0dd28ba 559 if (data)
62e76326 560 self_destruct();
561
d9572179 562 CBDATA_INIT_TYPE_FREECB(external_acl_data, free_external_acl_data);
62e76326 563
d9572179 564 data = cbdataAlloc(external_acl_data);
62e76326 565
d9572179 566 token = strtok(NULL, w_space);
62e76326 567
d9572179 568 if (!token)
62e76326 569 self_destruct();
570
d9572179 571 data->def = cbdataReference(find_externalAclHelper(token));
62e76326 572
d9572179 573 if (!data->def)
62e76326 574 self_destruct();
575
4d091667 576 while ((token = strtokFile())) {
62e76326 577 wordlistAdd(&data->arguments, token);
d9572179 578 }
d9572179 579}
580
4b0f5de8 581bool
582ACLExternal::valid () const
583{
584 if (data->def->require_auth) {
585 if (authenticateSchemeCount() == 0) {
586 debug(28, 0) ("Can't use proxy auth because no authentication schemes were compiled.\n");
587 return false;
588 }
589
590 if (authenticateActiveSchemeCount() == 0) {
591 debug(28, 0) ("Can't use proxy auth because no authentication schemes are fully configured.\n");
592 return false;
593 }
594 }
595
596 return true;
597}
598
599bool
600ACLExternal::empty () const
601{
602 return false;
603}
604
b0dd28ba 605ACLExternal::~ACLExternal()
d9572179 606{
b0dd28ba 607 cbdataFree(data);
608 safe_free (class_);
d9572179 609}
610
b0dd28ba 611static int
612aclMatchExternal(external_acl_data *acl, ACLChecklist * ch);
613static int
614aclMatchExternal(external_acl_data *acl, ACLChecklist * ch)
d9572179 615{
616 int result;
617 external_acl_entry *entry;
d9572179 618 const char *key = "";
619 debug(82, 9) ("aclMatchExternal: acl=\"%s\"\n", acl->def->name);
620 entry = ch->extacl_entry;
62e76326 621
d9572179 622 if (entry) {
62e76326 623 if (cbdataReferenceValid(entry) && entry->def == acl->def &&
624 strcmp((char *)entry->key, key) == 0) {
625 /* Ours, use it.. */
626 } else {
627 /* Not valid, or not ours.. get rid of it */
628 cbdataReferenceDone(ch->extacl_entry);
629 entry = NULL;
630 }
d9572179 631 }
62e76326 632
4a972fa2 633 external_acl_message = "MISSING REQUIRED INFORMATION";
634
d9572179 635 if (!entry) {
62e76326 636 if (acl->def->require_auth) {
637 int ti;
638 /* Make sure the user is authenticated */
639
640 if ((ti = ch->authenticated()) != 1) {
641 debug(82, 2) ("aclMatchExternal: %s user not authenticated (%d)\n", acl->def->name, ti);
642 return ti;
643 }
644 }
645
646 key = makeExternalAclKey(ch, acl);
41218131 647
73c18b42 648 if (acl->def->require_auth)
649 ch->auth_user_request = NULL;
47b0c1fa 650
41218131 651 if (!key) {
652 /* Not sufficient data to process */
653 return -1;
654 }
655
62e76326 656 entry = static_cast<external_acl_entry *>(hash_lookup(acl->def->cache, key));
657
47b0c1fa 658 if (!entry || external_acl_grace_expired(acl->def, entry)) {
659 debug(82, 2) ("aclMatchExternal: %s(\"%s\") = lookup needed\n", acl->def->name, key);
95a83c22 660
47b0c1fa 661 if (acl->def->theHelper->stats.queue_size <= acl->def->theHelper->n_running) {
662 ch->changeState (ExternalACLLookup::Instance());
663 return -1;
664 } else {
665 if (!entry) {
666 debug(82, 1) ("aclMatchExternal: '%s' queue overload. Request rejected '%s'.\n", acl->def->name, key);
4a972fa2 667 external_acl_message = "SYSTEM TOO BUSY, TRY AGAIN LATER";
47b0c1fa 668 return -1;
669 } else {
670 debug(82, 1) ("aclMatchExternal: '%s' queue overload. Using stale result. '%s'.\n", acl->def->name, key);
671 /* Fall thru to processing below */
672 }
673 }
674 }
d9572179 675 }
62e76326 676
d9572179 677 external_acl_cache_touch(acl->def, entry);
678 result = entry->result;
4a972fa2 679 external_acl_message = entry->message.buf();
680
d9572179 681 debug(82, 2) ("aclMatchExternal: %s = %d\n", acl->def->name, result);
62e76326 682
abb929f0 683 if (ch->request) {
684 if (entry->user.size())
685 ch->request->extacl_user = entry->user;
cbd2dc91 686
abb929f0 687 if (entry->password.size())
688 ch->request->extacl_passwd = entry->password;
62e76326 689
abb929f0 690 if (!ch->request->tag.size())
691 ch->request->tag = entry->tag;
4a972fa2 692
693 if (entry->log.size())
694 ch->request->extacl_log = entry->log;
abb929f0 695 }
1e5562e3 696
d9572179 697 return result;
698}
699
b0dd28ba 700int
701ACLExternal::match(ACLChecklist *checklist)
702{
703 return aclMatchExternal (data, checklist);
704}
705
d9572179 706wordlist *
b0dd28ba 707ACLExternal::dump() const
d9572179 708{
b0dd28ba 709 external_acl_data const *acl = data;
d9572179 710 wordlist *result = NULL;
711 wordlist *arg;
712 MemBuf mb;
2fe7eff9 713 mb.init();
714 mb.Printf("%s", acl->def->name);
62e76326 715
d9572179 716 for (arg = acl->arguments; arg; arg = arg->next) {
2fe7eff9 717 mb.Printf(" %s", arg->key);
d9572179 718 }
62e76326 719
d9572179 720 wordlistAdd(&result, mb.buf);
2fe7eff9 721 mb.clean();
d9572179 722 return result;
723}
724
725/******************************************************************
726 * external_acl cache
727 */
728
d9572179 729static void
730external_acl_cache_touch(external_acl * def, external_acl_entry * entry)
731{
732 dlinkDelete(&entry->lru, &def->lru_list);
733 dlinkAdd(entry, &entry->lru, &def->lru_list);
734}
735
736static char *
4fb35c3c 737makeExternalAclKey(ACLChecklist * ch, external_acl_data * acl_data)
d9572179 738{
032785bf 739 static MemBuf mb;
d9572179 740 char buf[256];
741 int first = 1;
742 wordlist *arg;
743 external_acl_format *format;
190154cf 744 HttpRequest *request = ch->request;
2fe7eff9 745 mb.reset();
62e76326 746
d9572179 747 for (format = acl_data->def->format; format; format = format->next) {
62e76326 748 const char *str = NULL;
650c4b88 749 String sb;
62e76326 750
751 switch (format->type) {
752
753 case _external_acl_format::EXT_ACL_LOGIN:
f5691f9c 754 assert (ch->auth_user_request);
755 str = ch->auth_user_request->username();
62e76326 756 break;
7a16973f 757#if USE_IDENT
62e76326 758
759 case _external_acl_format::EXT_ACL_IDENT:
760 str = ch->rfc931;
761
41218131 762 if (!str || !*str) {
62e76326 763 ch->changeState(IdentLookup::Instance());
764 return NULL;
765 }
766
767 break;
7a16973f 768#endif
62e76326 769
770 case _external_acl_format::EXT_ACL_SRC:
771 str = inet_ntoa(ch->src_addr);
772 break;
773
47b0c1fa 774 case _external_acl_format::EXT_ACL_SRCPORT:
775 snprintf(buf, sizeof(buf), "%d", request->client_port);
776 str = buf;
777 break;
778
779 case _external_acl_format::EXT_ACL_MYADDR:
780 str = inet_ntoa(request->my_addr);
781 break;
782
783 case _external_acl_format::EXT_ACL_MYPORT:
784 snprintf(buf, sizeof(buf), "%d", request->my_port);
785 str = buf;
786 break;
787
62e76326 788 case _external_acl_format::EXT_ACL_DST:
789 str = request->host;
790 break;
791
792 case _external_acl_format::EXT_ACL_PROTO:
793 str = ProtocolStr[request->protocol];
794 break;
795
796 case _external_acl_format::EXT_ACL_PORT:
797 snprintf(buf, sizeof(buf), "%d", request->port);
798 str = buf;
799 break;
800
801 case _external_acl_format::EXT_ACL_PATH:
802 str = request->urlpath.buf();
803 break;
804
805 case _external_acl_format::EXT_ACL_METHOD:
806 str = RequestMethodStr[request->method];
807 break;
808
809 case _external_acl_format::EXT_ACL_HEADER:
a9925b40 810 sb = request->header.getByName(format->header);
62e76326 811 str = sb.buf();
812 break;
813
814 case _external_acl_format::EXT_ACL_HEADER_ID:
a9925b40 815 sb = request->header.getStrOrList(format->header_id);
62e76326 816 str = sb.buf();
817 break;
818
819 case _external_acl_format::EXT_ACL_HEADER_MEMBER:
a9925b40 820 sb = request->header.getByNameListMember(format->header, format->member, format->separator);
62e76326 821 str = sb.buf();
822 break;
823
824 case _external_acl_format::EXT_ACL_HEADER_ID_MEMBER:
a9925b40 825 sb = request->header.getListMember(format->header_id, format->member, format->separator);
62e76326 826 str = sb.buf();
827 break;
a7ad6e4e 828#if USE_SSL
62e76326 829
4ac9968f 830 case _external_acl_format::EXT_ACL_USER_CERT_RAW:
831
832 if (ch->conn().getRaw()) {
833 SSL *ssl = fd_table[ch->conn()->fd].ssl;
834
835 if (ssl)
836 str = sslGetUserCertificatePEM(ssl);
837 }
838
839 break;
840
3d61c476 841 case _external_acl_format::EXT_ACL_USER_CERTCHAIN_RAW:
842
843 if (ch->conn().getRaw()) {
844 SSL *ssl = fd_table[ch->conn()->fd].ssl;
845
846 if (ssl)
847 str = sslGetUserCertificateChainPEM(ssl);
848 }
849
850 break;
851
62e76326 852 case _external_acl_format::EXT_ACL_USER_CERT:
853
c588c5ba 854 if (ch->conn().getRaw()) {
62e76326 855 SSL *ssl = fd_table[ch->conn()->fd].ssl;
856
857 if (ssl)
858 str = sslGetUserAttribute(ssl, format->header);
859 }
860
861 break;
862
863 case _external_acl_format::EXT_ACL_CA_CERT:
864
c588c5ba 865 if (ch->conn().getRaw()) {
62e76326 866 SSL *ssl = fd_table[ch->conn()->fd].ssl;
867
868 if (ssl)
869 str = sslGetCAAttribute(ssl, format->header);
870 }
871
872 break;
a7ad6e4e 873#endif
62e76326 874
abb929f0 875 case _external_acl_format::EXT_ACL_EXT_USER:
876 str = request->extacl_user.buf();
877 break;
878
62e76326 879 case _external_acl_format::EXT_ACL_UNKNOWN:
880
881 case _external_acl_format::EXT_ACL_END:
882 fatal("unknown external_acl format error");
883 break;
884 }
885
886 if (str)
887 if (!*str)
888 str = NULL;
889
890 if (!str)
891 str = "-";
892
893 if (!first)
2fe7eff9 894 mb.append(" ", 1);
62e76326 895
dc1af3cf 896 if (acl_data->def->quote == external_acl::QUOTE_METHOD_URL) {
897 const char *quoted = rfc1738_escape(str);
2fe7eff9 898 mb.append(quoted, strlen(quoted));
dc1af3cf 899 } else {
900 strwordquote(&mb, str);
901 }
62e76326 902
903 sb.clean();
904
905 first = 0;
d9572179 906 }
62e76326 907
d9572179 908 for (arg = acl_data->arguments; arg; arg = arg->next) {
62e76326 909 if (!first)
2fe7eff9 910 mb.append(" ", 1);
62e76326 911
dc1af3cf 912 if (acl_data->def->quote == external_acl::QUOTE_METHOD_URL) {
913 const char *quoted = rfc1738_escape(arg->key);
2fe7eff9 914 mb.append(quoted, strlen(quoted));
dc1af3cf 915 } else {
916 strwordquote(&mb, arg->key);
917 }
62e76326 918
919 first = 0;
d9572179 920 }
62e76326 921
d9572179 922 return mb.buf;
d9572179 923}
924
925static int
926external_acl_entry_expired(external_acl * def, external_acl_entry * entry)
927{
928 if (entry->date + (entry->result == 1 ? def->ttl : def->negative_ttl) < squid_curtime)
62e76326 929 return 1;
d9572179 930 else
62e76326 931 return 0;
d9572179 932}
62e76326 933
47b0c1fa 934static int
935external_acl_grace_expired(external_acl * def, external_acl_entry * entry)
936{
937 int ttl;
938 ttl = entry->result == 1 ? def->ttl : def->negative_ttl;
939 ttl = (ttl * (100 - def->grace)) / 100;
940
941 if (entry->date + ttl < squid_curtime)
942 return 1;
943 else
944 return 0;
945}
946
d9572179 947static external_acl_entry *
1e5562e3 948external_acl_cache_add(external_acl * def, const char *key, ExternalACLEntryData const & data)
d9572179 949{
1e5562e3 950 ExternalACLEntry *entry = static_cast<ExternalACLEntry *>(hash_lookup(def->cache, key));
951 debug(82, 2) ("external_acl_cache_add: Adding '%s' = %d\n", key, data.result);
62e76326 952
d9572179 953 if (entry) {
1e5562e3 954 debug(82, 3) ("ExternalACLEntry::update: updating existing entry\n");
955 entry->update (data);
62e76326 956 external_acl_cache_touch(def, entry);
957
958 return entry;
d9572179 959 }
62e76326 960
1e5562e3 961 entry = new ExternalACLEntry;
4a8b20e8 962 entry->key = xstrdup(key);
1e5562e3 963 entry->update (data);
964
965 def->add
966 (entry);
967
d9572179 968 return entry;
969}
970
971static void
972external_acl_cache_delete(external_acl * def, external_acl_entry * entry)
973{
1e5562e3 974 assert (entry->def == def);
4a8b20e8 975 hash_remove_link(def->cache, entry);
d9572179 976 dlinkDelete(&entry->lru, &def->lru_list);
977 def->cache_entries -= 1;
00d77d6b 978 delete entry;
d9572179 979}
980
981/******************************************************************
982 * external_acl helpers
983 */
984
985typedef struct _externalAclState externalAclState;
62e76326 986
987struct _externalAclState
988{
d9572179 989 EAH *callback;
990 void *callback_data;
991 char *key;
992 external_acl *def;
993 dlink_node list;
994 externalAclState *queue;
995};
996
997CBDATA_TYPE(externalAclState);
998static void
999free_externalAclState(void *data)
1000{
e6ccf245 1001 externalAclState *state = static_cast<externalAclState *>(data);
d9572179 1002 safe_free(state->key);
1003 cbdataReferenceDone(state->callback_data);
1004 cbdataReferenceDone(state->def);
1005}
1006
d9572179 1007/*
1008 * The helper program receives queries on stdin, one
07eca7e0 1009 * per line, and must return the result on on stdout
d9572179 1010 *
1011 * General result syntax:
1012 *
1013 * OK/ERR keyword=value ...
1014 *
1015 * Keywords:
1016 *
4a972fa2 1017 * user= The users name (login)
1018 * message= Message describing the reason
1019 * tag= A string tag to be applied to the request that triggered the acl match.
1020 * applies to both OK and ERR responses.
1021 * Won't override existing request tags.
1022 * log= A string to be used in access logging
d9572179 1023 *
1024 * Other keywords may be added to the protocol later
1025 *
1026 * value needs to be enclosed in quotes if it may contain whitespace, or
1027 * the whitespace escaped using \ (\ escaping obviously also applies to
1028 * any " characters)
1029 */
1030
1031static void
1032externalAclHandleReply(void *data, char *reply)
1033{
e6ccf245 1034 externalAclState *state = static_cast<externalAclState *>(data);
d9572179 1035 externalAclState *next;
d9572179 1036 char *status;
1037 char *token;
1038 char *value;
1039 char *t;
1e5562e3 1040 ExternalACLEntryData entryData;
1041 entryData.result = 0;
466090ac 1042 external_acl_entry *entry = NULL;
d9572179 1043
1044 debug(82, 2) ("externalAclHandleReply: reply=\"%s\"\n", reply);
1045
4960475f 1046 if (reply) {
62e76326 1047 status = strwordtok(reply, &t);
1048
1049 if (status && strcmp(status, "OK") == 0)
1e5562e3 1050 entryData.result = 1;
62e76326 1051
1052 while ((token = strwordtok(NULL, &t))) {
1053 value = strchr(token, '=');
1054
1055 if (value) {
1056 *value++ = '\0'; /* terminate the token, and move up to the value */
1057
dc1af3cf 1058 if (state->def->quote == external_acl::QUOTE_METHOD_URL)
1059 rfc1738_unescape(value);
1060
62e76326 1061 if (strcmp(token, "user") == 0)
1e5562e3 1062 entryData.user = value;
d7bd27db 1063 else if (strcmp(token, "message") == 0)
1064 entryData.message = value;
62e76326 1065 else if (strcmp(token, "error") == 0)
d7bd27db 1066 entryData.message = value;
abb929f0 1067 else if (strcmp(token, "tag") == 0)
1068 entryData.tag = value;
4a972fa2 1069 else if (strcmp(token, "log") == 0)
1070 entryData.log = value;
abb929f0 1071 else if (strcmp(token, "password") == 0)
1072 entryData.password = value;
1073 else if (strcmp(token, "passwd") == 0)
1074 entryData.password = value;
1075 else if (strcmp(token, "login") == 0)
1076 entryData.user = value;
62e76326 1077 }
1078 }
d9572179 1079 }
62e76326 1080
d9572179 1081 dlinkDelete(&state->list, &state->def->queue);
62e76326 1082
4960475f 1083 if (cbdataReferenceValid(state->def)) {
62e76326 1084 if (reply)
1e5562e3 1085 entry = external_acl_cache_add(state->def, state->key, entryData);
62e76326 1086 else {
2ae2408b 1087 external_acl_entry *oldentry = (external_acl_entry *)hash_lookup(state->def->cache, state->key);
62e76326 1088
2ae2408b 1089 if (oldentry)
1090 external_acl_cache_delete(state->def, oldentry);
62e76326 1091 }
466090ac 1092 }
d9572179 1093
1094 do {
62e76326 1095 void *cbdata;
1096 cbdataReferenceDone(state->def);
1097
47b0c1fa 1098 if (state->callback && cbdataReferenceValidDone(state->callback_data, &cbdata))
62e76326 1099 state->callback(cbdata, entry);
1100
1101 next = state->queue;
d9572179 1102
62e76326 1103 cbdataFree(state);
d9572179 1104
62e76326 1105 state = next;
d9572179 1106 } while (state);
1107}
1108
1109void
b0dd28ba 1110ACLExternal::ExternalAclLookup(ACLChecklist * ch, ACLExternal * me, EAH * callback, void *callback_data)
d9572179 1111{
1112 MemBuf buf;
e870b1f8 1113 external_acl_data *acl = me->data;
d9572179 1114 external_acl *def = acl->def;
d9572179 1115 externalAclState *state;
47b0c1fa 1116 dlink_node *node;
1117 externalAclState *oldstate = NULL;
1118 bool graceful = 0;
c8e7608c 1119
1120 if (acl->def->require_auth) {
1121 int ti;
1122 /* Make sure the user is authenticated */
1123
1124 if ((ti = ch->authenticated()) != 1) {
1125 debug(82, 1) ("externalAclLookup: %s user authentication failure (%d)\n", acl->def->name, ti);
1126 callback(callback_data, NULL);
1127 return;
1128 }
1129 }
1130
1131 const char *key = makeExternalAclKey(ch, acl);
62e76326 1132
d9572179 1133 if (!key) {
62e76326 1134 debug(82, 1) ("externalAclLookup: lookup in '%s', prerequisit failure\n", def->name);
1135 callback(callback_data, NULL);
1136 return;
d9572179 1137 }
62e76326 1138
c8e7608c 1139 debug(82, 2) ("externalAclLookup: lookup in '%s' for '%s'\n", def->name, key);
1140 external_acl_entry *entry = static_cast<external_acl_entry *>(hash_lookup(def->cache, key));
1141
47b0c1fa 1142 if (entry && external_acl_entry_expired(def, entry))
1143 entry = NULL;
62e76326 1144
47b0c1fa 1145 /* Check for a pending lookup to hook into */
1146 for (node = def->queue.head; node; node = node->next) {
1147 externalAclState *oldstatetmp = static_cast<externalAclState *>(node->data);
62e76326 1148
47b0c1fa 1149 if (strcmp(key, oldstatetmp->key) == 0) {
1150 oldstate = oldstatetmp;
1151 break;
1152 }
1153 }
62e76326 1154
47b0c1fa 1155 if (entry && external_acl_grace_expired(def, entry)) {
1156 if (oldstate) {
62e76326 1157 callback(callback_data, entry);
62e76326 1158 return;
47b0c1fa 1159 } else {
1160 graceful = 1;
62e76326 1161 }
d9572179 1162 }
62e76326 1163
47b0c1fa 1164 if (!graceful && entry && !external_acl_grace_expired(def, entry)) {
1165 /* Should not really happen, but why not.. */
1166 callback(callback_data, entry);
1167 return;
1168 }
62e76326 1169
47b0c1fa 1170 /* No pending lookup found. Sumbit to helper */
1171 state = cbdataAlloc(externalAclState);
62e76326 1172
47b0c1fa 1173 state->def = cbdataReference(def);
62e76326 1174
47b0c1fa 1175 state->key = xstrdup(key);
62e76326 1176
47b0c1fa 1177 if (!graceful) {
1178 state->callback = callback;
1179 state->callback_data = cbdataReference(callback_data);
d9572179 1180 }
62e76326 1181
47b0c1fa 1182 if (oldstate) {
1183 /* Hook into pending lookup */
1184 state->queue = oldstate->queue;
1185 oldstate->queue = state;
1186 } else {
1187 /* Check for queue overload */
1188
1189 if (def->theHelper->stats.queue_size >= def->theHelper->n_running) {
1190 debug(82, 1) ("externalAclLookup: '%s' queue overload\n", def->name);
1191 cbdataFree(state);
1192 callback(callback_data, entry);
1193 return;
1194 }
1195
1196 /* Send it off to the helper */
2fe7eff9 1197 buf.init();
62e76326 1198
2fe7eff9 1199 buf.Printf("%s\n", key);
62e76326 1200
47b0c1fa 1201 helperSubmit(def->theHelper, buf.buf, externalAclHandleReply, state);
62e76326 1202
47b0c1fa 1203 dlinkAdd(state, &state->list, &def->queue);
62e76326 1204
2fe7eff9 1205 buf.clean();
47b0c1fa 1206 }
62e76326 1207
47b0c1fa 1208 if (graceful) {
1209 /* No need to wait during grace period */
1210 callback(callback_data, entry);
1211 return;
1212 }
d9572179 1213}
1214
1215static void
1216externalAclStats(StoreEntry * sentry)
1217{
1218 external_acl *p;
1219
1220 for (p = Config.externalAclHelperList; p; p = p->next) {
62e76326 1221 storeAppendPrintf(sentry, "External ACL Statistics: %s\n", p->name);
1222 storeAppendPrintf(sentry, "Cache size: %d\n", p->cache->count);
1223 helperStats(sentry, p->theHelper);
1224 storeAppendPrintf(sentry, "\n");
d9572179 1225 }
1226}
1227
1228void
1229externalAclInit(void)
1230{
1231 static int firstTimeInit = 1;
1232 external_acl *p;
1233
1234 for (p = Config.externalAclHelperList; p; p = p->next) {
62e76326 1235 if (!p->cache)
1236 p->cache = hash_create((HASHCMP *) strcmp, hashPrime(1024), hash4);
1237
1238 if (!p->theHelper)
1239 p->theHelper = helperCreate(p->name);
1240
1241 p->theHelper->cmdline = p->cmdline;
1242
1243 p->theHelper->n_to_start = p->children;
1244
07eca7e0 1245 p->theHelper->concurrency = p->concurrency;
1246
62e76326 1247 p->theHelper->ipc_type = IPC_TCP_SOCKET;
1248
1249 helperOpenServers(p->theHelper);
d9572179 1250 }
62e76326 1251
d9572179 1252 if (firstTimeInit) {
62e76326 1253 firstTimeInit = 0;
1254 cachemgrRegister("external_acl",
1255 "External ACL stats",
1256 externalAclStats, 0, 1);
1257 CBDATA_INIT_TYPE_FREECB(externalAclState, free_externalAclState);
d9572179 1258 }
1259}
1260
1261void
1262externalAclShutdown(void)
1263{
1264 external_acl *p;
62e76326 1265
d9572179 1266 for (p = Config.externalAclHelperList; p; p = p->next) {
62e76326 1267 helperShutdown(p->theHelper);
d9572179 1268 }
1269}
225b7b10 1270
1271ExternalACLLookup ExternalACLLookup::instance_;
1272ExternalACLLookup *
1273ExternalACLLookup::Instance()
1274{
1275 return &instance_;
1276}
1277
1278void
1279ExternalACLLookup::checkForAsync(ACLChecklist *checklist)const
1280{
b0dd28ba 1281 /* TODO: optimise this - we probably have a pointer to this
1282 * around somewhere */
97427e90 1283 ACL *acl = ACL::FindByName(AclMatchedName);
b0dd28ba 1284 ACLExternal *me = dynamic_cast<ACLExternal *> (acl);
1285 assert (me);
225b7b10 1286 checklist->asyncInProgress(true);
b0dd28ba 1287 ACLExternal::ExternalAclLookup(checklist, me, LookupDone, checklist);
225b7b10 1288}
1289
1290void
1291ExternalACLLookup::LookupDone(void *data, void *result)
1292{
1293 ACLChecklist *checklist = (ACLChecklist *)data;
225b7b10 1294 checklist->extacl_entry = cbdataReference((external_acl_entry *)result);
1295 checklist->asyncInProgress(false);
c059ed04 1296 checklist->changeState (ACLChecklist::NullState::Instance());
225b7b10 1297 checklist->check();
1298}
b0dd28ba 1299
1300/* This registers "external" in the registry. To do dynamic definitions
1301 * of external ACL's, rather than a static prototype, have a Prototype instance
1302 * prototype in the class that defines each external acl 'class'.
1303 * Then, then the external acl instance is created, it self registers under
1304 * it's name.
1305 * Be sure that clone is fully functional for that acl class though!
1306 */
1307ACL::Prototype ACLExternal::RegistryProtoype(&ACLExternal::RegistryEntry_, "external");
1308
1309ACLExternal ACLExternal::RegistryEntry_("external");
1310
1311ACL *
1312ACLExternal::clone() const
1313{
1314 return new ACLExternal(*this);
1315}
1316
1317ACLExternal::ACLExternal (char const *theClass) : data (NULL), class_ (xstrdup (theClass))
1318{}
1319
1320ACLExternal::ACLExternal (ACLExternal const & old) : data (NULL), class_ (old.class_ ? xstrdup (old.class_) : NULL)
1321{
1322 /* we don't have copy constructors for the data yet */
1323 assert (!old.data);
1324}
1325
b0dd28ba 1326char const *
1327ACLExternal::typeString() const
1328{
1329 return class_;
1330}
1331
e870b1f8 1332bool
1333ACLExternal::isProxyAuth() const
1334{
1335 return data->def->require_auth;
1336}