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