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