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