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