]> git.ipfire.org Git - thirdparty/squid.git/blob - tools/cachemgr.cc
Import IPv6 support from squid3-ipv6 branch to 3-HEAD.
[thirdparty/squid.git] / tools / cachemgr.cc
1
2 /*
3 * $Id: cachemgr.cc,v 1.6 2007/12/14 23:11:53 amosjeffries Exp $
4 *
5 * DEBUG: section 0 CGI Cache Manager
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 #include "config.h"
37
38 #if HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 #if HAVE_STDLIB_H
42 #include <stdlib.h>
43 #endif
44 #if HAVE_STDIO_H
45 #include <stdio.h>
46 #endif
47 #if HAVE_SYS_TYPES_H
48 #include <sys/types.h>
49 #endif
50 #if HAVE_CTYPE_H
51 #include <ctype.h>
52 #endif
53 #if HAVE_ERRNO_H
54 #include <errno.h>
55 #endif
56 #if HAVE_FCNTL_H
57 #include <fcntl.h>
58 #endif
59 #if HAVE_GRP_H
60 #include <grp.h>
61 #endif
62 #if HAVE_GNUMALLOC_H
63 #include <gnumalloc.h>
64 #elif HAVE_MALLOC_H
65 #include <malloc.h>
66 #endif
67 #if HAVE_MEMORY_H
68 #include <memory.h>
69 #endif
70 #if HAVE_NETDB_H && !defined(_SQUID_NETDB_H_) /* protect NEXTSTEP */
71 #define _SQUID_NETDB_H_
72 #include <netdb.h>
73 #endif
74 #if HAVE_PWD_H
75 #include <pwd.h>
76 #endif
77 #if HAVE_SIGNAL_H
78 #include <signal.h>
79 #endif
80 #if HAVE_TIME_H
81 #include <time.h>
82 #endif
83 #if HAVE_SYS_PARAM_H
84 #include <sys/param.h>
85 #endif
86 #if HAVE_SYS_TIME_H
87 #include <sys/time.h>
88 #endif
89 #if HAVE_SYS_RESOURCE_H
90 #include <sys/resource.h> /* needs sys/time.h above it */
91 #endif
92 #if HAVE_SYS_SOCKET_H
93 #include <sys/socket.h>
94 #endif
95 #if HAVE_NETINET_IN_H
96 #include <netinet/in.h>
97 #endif
98 #if HAVE_ARPA_INET_H
99 #include <arpa/inet.h>
100 #endif
101 #if HAVE_SYS_STAT_H
102 #include <sys/stat.h>
103 #endif
104 #if HAVE_SYS_UN_H
105 #include <sys/un.h>
106 #endif
107 #if HAVE_SYS_WAIT_H
108 #include <sys/wait.h>
109 #endif
110 #if HAVE_LIBC_H
111 #include <libc.h>
112 #endif
113 #if HAVE_STRING_H
114 #include <string.h>
115 #endif
116 #if HAVE_STRINGS_H
117 #include <strings.h>
118 #endif
119 #if HAVE_BSTRING_H
120 #include <bstring.h>
121 #endif
122 #if HAVE_CRYPT_H
123 #include <crypt.h>
124 #endif
125 #if HAVE_SYS_SELECT_H
126 #include <sys/select.h>
127 #endif
128 #if HAVE_FNMATCH_H
129 extern "C"
130 {
131 #include <fnmatch.h>
132 }
133 #endif
134
135 #include "assert.h"
136 #include "util.h"
137 #include "IPAddress.h"
138
139 #ifndef DEFAULT_CACHEMGR_CONFIG
140 #define DEFAULT_CACHEMGR_CONFIG "/etc/squid/cachemgr.conf"
141 #endif
142
143 typedef struct
144 {
145 char *server;
146 char *hostname;
147 int port;
148 char *action;
149 char *user_name;
150 char *passwd;
151 char *pub_auth;
152 }
153
154 cachemgr_request;
155
156 /*
157 * Debugging macros (info goes to error_log on your web server)
158 * Note: do not run cache manager with non zero debugging level
159 * if you do not debug, it may write a lot of [sensitive]
160 * information to your error log.
161 */
162
163 /* debugging level 0 (disabled) - 3 (max) */
164 #define DEBUG_LEVEL 0
165 #define debug(level) if ((level) <= DEBUG_LEVEL && DEBUG_LEVEL > 0)
166
167 /*
168 * Static variables and constants
169 */
170 static const time_t passwd_ttl = 60 * 60 * 3; /* in sec */
171 static const char *script_name = "/cgi-bin/cachemgr.cgi";
172 static const char *progname = NULL;
173 static time_t now;
174
175 /*
176 * Function prototypes
177 */
178 #define safe_free(str) { if (str) { xfree(str); (str) = NULL; } }
179 static const char *safe_str(const char *str);
180 static const char *xstrtok(char **str, char del);
181 static void print_trailer(void);
182 static void auth_html(const char *host, int port, const char *user_name);
183 static void error_html(const char *msg);
184 static char *menu_url(cachemgr_request * req, const char *action);
185 static int parse_status_line(const char *sline, const char **statusStr);
186 static cachemgr_request *read_request(void);
187 static char *read_get_request(void);
188 static char *read_post_request(void);
189
190 static void make_pub_auth(cachemgr_request * req);
191 static void decode_pub_auth(cachemgr_request * req);
192 static void reset_auth(cachemgr_request * req);
193 static const char *make_auth_header(const cachemgr_request * req);
194
195 static int check_target_acl(const char *hostname, int port);
196
197 #ifdef _SQUID_MSWIN_
198 static int s_iInitCount = 0;
199
200 int Win32SockInit(void)
201 {
202 int iVersionRequested;
203 WSADATA wsaData;
204 int err;
205
206 if (s_iInitCount > 0) {
207 s_iInitCount++;
208 return (0);
209 } else if (s_iInitCount < 0)
210 return (s_iInitCount);
211
212 /* s_iInitCount == 0. Do the initailization */
213 iVersionRequested = MAKEWORD(2, 0);
214
215 err = WSAStartup((WORD) iVersionRequested, &wsaData);
216
217 if (err) {
218 s_iInitCount = -1;
219 return (s_iInitCount);
220 }
221
222 if (LOBYTE(wsaData.wVersion) != 2 ||
223 HIBYTE(wsaData.wVersion) != 0) {
224 s_iInitCount = -2;
225 WSACleanup();
226 return (s_iInitCount);
227 }
228
229 s_iInitCount++;
230 return (s_iInitCount);
231 }
232
233 void Win32SockCleanup(void)
234 {
235 if (--s_iInitCount == 0)
236 WSACleanup();
237
238 return;
239 }
240
241 #endif /* ifdef _SQUID_MSWIN_ */
242
243 static const char *
244 safe_str(const char *str)
245 {
246 return str ? str : "";
247 }
248
249 /* relaxed number format */
250 static int
251 is_number(const char *str)
252 {
253 return strspn(str, "\t -+01234567890./\n") == strlen(str);
254 }
255
256 static const char *
257 xstrtok(char **str, char del)
258 {
259 if (*str) {
260 char *p = strchr(*str, del);
261 char *tok = *str;
262 int len;
263
264 if (p) {
265 *str = p + 1;
266 *p = '\0';
267 } else
268 *str = NULL;
269
270 /* trim */
271 len = strlen(tok);
272
273 while (len && xisspace(tok[len - 1]))
274 tok[--len] = '\0';
275
276 while (xisspace(*tok))
277 tok++;
278
279 return tok;
280 } else
281 return "";
282 }
283
284 static void
285 print_trailer(void)
286 {
287 printf("<HR noshade size=\"1px\">\n");
288 printf("<ADDRESS>\n");
289 printf("Generated %s, by %s/%s@%s\n",
290 mkrfc1123(now), progname, VERSION, getfullhostname());
291 printf("</ADDRESS></BODY></HTML>\n");
292 }
293
294 static void
295 auth_html(const char *host, int port, const char *user_name)
296 {
297 FILE *fp;
298 int need_host = 1;
299
300 if (!user_name)
301 user_name = "";
302
303 if (!host || !strlen(host))
304 host = "";
305
306 printf("Content-Type: text/html\r\n\r\n");
307
308 printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
309
310 printf("<HTML><HEAD><TITLE>Cache Manager Interface</TITLE>\n");
311
312 printf("<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE></HEAD>\n");
313
314 printf("<BODY><H1>Cache Manager Interface</H1>\n");
315
316 printf("<P>This is a WWW interface to the instrumentation interface\n");
317
318 printf("for the Squid object cache.</P>\n");
319
320 printf("<HR noshade size=\"1px\">\n");
321
322 printf("<FORM METHOD=\"POST\" ACTION=\"%s\">\n", script_name);
323
324 printf("<TABLE BORDER=\"0\" CELLPADDING=\"10\" CELLSPACING=\"1\">\n");
325
326
327 fp = fopen("cachemgr.conf", "r");
328
329 if (fp == NULL)
330 fp = fopen(DEFAULT_CACHEMGR_CONFIG, "r");
331
332 if (fp != NULL) {
333 int servers = 0;
334 char config_line[BUFSIZ];
335
336 while (fgets(config_line, BUFSIZ, fp)) {
337 char *server, *comment;
338 strtok(config_line, "\r\n");
339
340 if (config_line[0] == '#')
341 continue;
342
343 if (config_line[0] == '\0')
344 continue;
345
346 if ((server = strtok(config_line, " \t")) == NULL)
347 continue;
348
349 if (strchr(server, '*') || strchr(server, '[') || strchr(server, '?')) {
350 need_host = -1;
351 continue;
352 }
353
354 comment = strtok(NULL, "");
355
356 if (comment)
357 while (*comment == ' ' || *comment == '\t')
358 comment++;
359
360 if (!comment || !*comment)
361 comment = server;
362
363 if (!servers) {
364 printf("<TR><TH ALIGN=\"left\">Cache Server:</TH><TD><SELECT NAME=\"server\">\n");
365 }
366
367 printf("<OPTION VALUE=\"%s\"%s>%s</OPTION>\n", server, (servers || *host) ? "" : " SELECTED", comment);
368 servers++;
369 }
370
371 if (servers) {
372 if (need_host == 1 && !*host)
373 need_host = 0;
374
375 if (need_host)
376 printf("<OPTION VALUE=\"\"%s>Other</OPTION>\n", (*host) ? " SELECTED" : "");
377
378 printf("</SELECT></TR>\n");
379 }
380
381 fclose(fp);
382 }
383
384 if (need_host) {
385 if (need_host == 1 && !*host)
386 host = "localhost";
387
388 printf("<TR><TH ALIGN=\"left\">Cache Host:</TH><TD><INPUT NAME=\"host\" ");
389
390 printf("size=\"30\" VALUE=\"%s\"></TD></TR>\n", host);
391
392 printf("<TR><TH ALIGN=\"left\">Cache Port:</TH><TD><INPUT NAME=\"port\" ");
393
394 printf("size=\"30\" VALUE=\"%d\"></TD></TR>\n", port);
395 }
396
397 printf("<TR><TH ALIGN=\"left\">Manager name:</TH><TD><INPUT NAME=\"user_name\" ");
398
399 printf("size=\"30\" VALUE=\"%s\"></TD></TR>\n", user_name);
400
401 printf("<TR><TH ALIGN=\"left\">Password:</TH><TD><INPUT TYPE=\"password\" NAME=\"passwd\" ");
402
403 printf("size=\"30\" VALUE=\"\"></TD></TR>\n");
404
405 printf("</TABLE><BR CLEAR=\"all\">\n");
406
407 printf("<INPUT TYPE=\"submit\" VALUE=\"Continue...\">\n");
408
409 printf("</FORM>\n");
410
411 print_trailer();
412 }
413
414 static void
415 error_html(const char *msg)
416 {
417 printf("Content-Type: text/html\r\n\r\n");
418 printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
419 printf("<HTML><HEAD><TITLE>Cache Manager Error</TITLE>\n");
420 printf("<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE></HEAD>\n");
421 printf("<BODY><H1>Cache Manager Error</H1>\n");
422 printf("<P>\n%s</P>\n", msg);
423 print_trailer();
424 }
425
426 /* returns http status extracted from status line or -1 on parsing failure */
427 static int
428 parse_status_line(const char *sline, const char **statusStr)
429 {
430 const char *sp = strchr(sline, ' ');
431
432 if (statusStr)
433 *statusStr = NULL;
434
435 if (strncasecmp(sline, "HTTP/", 5) || !sp)
436 return -1;
437
438 while (xisspace(*++sp))
439
440 ;
441 if (!xisdigit(*sp))
442 return -1;
443
444 if (statusStr)
445 *statusStr = sp;
446
447 return atoi(sp);
448 }
449
450 static char *
451 menu_url(cachemgr_request * req, const char *action)
452 {
453 static char url[1024];
454 snprintf(url, sizeof(url), "%s?host=%s&port=%d&user_name=%s&operation=%s&auth=%s",
455 script_name,
456 req->hostname,
457 req->port,
458 safe_str(req->user_name),
459 action,
460 safe_str(req->pub_auth));
461 return url;
462 }
463
464 static const char *
465 munge_menu_line(const char *buf, cachemgr_request * req)
466 {
467 char *x;
468 const char *a;
469 const char *d;
470 const char *p;
471 char *a_url;
472 char *buf_copy;
473 static char html[2 * 1024];
474
475 if (strlen(buf) < 1)
476 return buf;
477
478 if (*buf != ' ')
479 return buf;
480
481 buf_copy = x = xstrdup(buf);
482
483 a = xstrtok(&x, '\t');
484
485 d = xstrtok(&x, '\t');
486
487 p = xstrtok(&x, '\t');
488
489 a_url = xstrdup(menu_url(req, a));
490
491 /* no reason to give a url for a disabled action */
492 if (!strcmp(p, "disabled"))
493 snprintf(html, sizeof(html), "<LI type=\"circle\">%s (disabled)<A HREF=\"%s\">.</A>\n", d, a_url);
494 else
495 /* disable a hidden action (requires a password, but password is not in squid.conf) */
496 if (!strcmp(p, "hidden"))
497 snprintf(html, sizeof(html), "<LI type=\"circle\">%s (hidden)<A HREF=\"%s\">.</A>\n", d, a_url);
498 else
499 /* disable link if authentication is required and we have no password */
500 if (!strcmp(p, "protected") && !req->passwd)
501 snprintf(html, sizeof(html), "<LI type=\"circle\">%s (requires <a href=\"%s\">authentication</a>)<A HREF=\"%s\">.</A>\n",
502 d, menu_url(req, "authenticate"), a_url);
503 else
504 /* highlight protected but probably available entries */
505 if (!strcmp(p, "protected"))
506 snprintf(html, sizeof(html), "<LI type=\"square\"><A HREF=\"%s\"><font color=\"#FF0000\">%s</font></A>\n",
507 a_url, d);
508
509 /* public entry or unknown type of protection */
510 else
511 snprintf(html, sizeof(html), "<LI type=\"disk\"><A HREF=\"%s\">%s</A>\n", a_url, d);
512
513 xfree(a_url);
514
515 xfree(buf_copy);
516
517 return html;
518 }
519
520 static const char *
521 munge_other_line(const char *buf, cachemgr_request * req)
522 {
523 static const char *ttags[] = {"td", "th"};
524
525 static char html[4096];
526 static int table_line_num = 0;
527 static int next_is_header = 0;
528 int is_header = 0;
529 const char *ttag;
530 char *buf_copy;
531 char *x, *p;
532 int l = 0;
533 /* does it look like a table? */
534
535 if (!strchr(buf, '\t') || *buf == '\t') {
536 /* nope, just text */
537 snprintf(html, sizeof(html), "%s%s",
538 table_line_num ? "</table>\n<pre>" : "", buf);
539 table_line_num = 0;
540 return html;
541 }
542
543 /* start html table */
544 if (!table_line_num) {
545 l += snprintf(html + l, sizeof(html) - l, "</pre><table cellpadding=\"2\" cellspacing=\"1\">\n");
546 next_is_header = 0;
547 }
548
549 /* remove '\n' */
550 is_header = (!table_line_num || next_is_header) && !strchr(buf, ':') && !is_number(buf);
551
552 ttag = ttags[is_header];
553
554 /* record starts */
555 l += snprintf(html + l, sizeof(html) - l, "<tr>");
556
557 /* substitute '\t' */
558 buf_copy = x = xstrdup(buf);
559
560 if ((p = strchr(x, '\n')))
561 *p = '\0';
562
563 while (x && strlen(x)) {
564 int column_span = 1;
565 const char *cell = xstrtok(&x, '\t');
566
567 while (x && *x == '\t') {
568 column_span++;
569 x++;
570 }
571
572 l += snprintf(html + l, sizeof(html) - l, "<%s colspan=\"%d\" align=\"%s\">%s</%s>",
573 ttag, column_span,
574 is_header ? "center" : is_number(cell) ? "right" : "left",
575 cell, ttag);
576 }
577
578 xfree(buf_copy);
579 /* record ends */
580 l += snprintf(html + l, sizeof(html) - l, "</tr>\n");
581 next_is_header = is_header && strstr(buf, "\t\t");
582 table_line_num++;
583 return html;
584 }
585
586 static int
587 read_reply(int s, cachemgr_request * req)
588 {
589 char buf[4 * 1024];
590 #ifdef _SQUID_MSWIN_
591
592 int reply;
593 char *tmpfile = tempnam(NULL, "tmp0000");
594 FILE *fp = fopen(tmpfile, "w+");
595 #else
596
597 FILE *fp = fdopen(s, "r");
598 #endif
599 /* interpretation states */
600 enum {
601 isStatusLine, isHeaders, isBodyStart, isBody, isForward, isEof, isForwardEof, isSuccess, isError
602 } istate = isStatusLine;
603 int parse_menu = 0;
604 const char *action = req->action;
605 const char *statusStr = NULL;
606 int status = -1;
607
608 if (0 == strlen(req->action))
609 parse_menu = 1;
610 else if (0 == strcasecmp(req->action, "menu"))
611 parse_menu = 1;
612
613 if (fp == NULL) {
614 #ifdef _SQUID_MSWIN_
615 perror(tmpfile);
616 xfree(tmpfile);
617 #else
618
619 perror("fdopen");
620 #endif
621
622 close(s);
623 return 1;
624 }
625
626 #ifdef _SQUID_MSWIN_
627
628 while ((reply=recv(s, buf , sizeof(buf), 0)) > 0)
629 fwrite(buf, 1, reply, fp);
630
631 rewind(fp);
632
633 #endif
634
635 if (parse_menu)
636 action = "menu";
637
638 /* read reply interpreting one line at a time depending on state */
639 while (istate < isEof) {
640 if (!fgets(buf, sizeof(buf), fp))
641 istate = istate == isForward ? isForwardEof : isEof;
642
643 switch (istate) {
644
645 case isStatusLine:
646 /* get HTTP status */
647 /* uncomment the following if you want to debug headers */
648 /* fputs("\r\n\r\n", stdout); */
649 status = parse_status_line(buf, &statusStr);
650 istate = status == 200 ? isHeaders : isForward;
651 /* if cache asks for authentication, we have to reset our info */
652
653 if (status == 401 || status == 407) {
654 reset_auth(req);
655 status = 403; /* Forbiden, see comments in case isForward: */
656 }
657
658 /* this is a way to pass HTTP status to the Web server */
659 if (statusStr)
660 printf("Status: %d %s", status, statusStr); /* statusStr has '\n' */
661
662 break;
663
664 case isHeaders:
665 /* forward header field */
666 if (!strcmp(buf, "\r\n")) { /* end of headers */
667 fputs("Content-Type: text/html\r\n", stdout); /* add our type */
668 istate = isBodyStart;
669 }
670
671 if (strncasecmp(buf, "Content-Type:", 13)) /* filter out their type */
672 fputs(buf, stdout);
673
674 break;
675
676 case isBodyStart:
677 printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
678
679 printf("<HTML><HEAD><TITLE>CacheMgr@%s: %s</TITLE>\n",
680 req->hostname, action);
681
682 printf("<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}TABLE{background-color:#333333;border:0pt;padding:0pt}TH,TD{background-color:#ffffff;white-space:nowrap}--></STYLE>\n");
683
684 printf("</HEAD><BODY>\n");
685
686 if (parse_menu) {
687 printf("<H2><a href=\"%s\">Cache Manager</a> menu for %s:</H2>",
688 menu_url(req, "authenticate"), req->hostname);
689 printf("<UL>\n");
690 } else {
691 printf("<P><A HREF=\"%s\">%s</A>\n<HR noshade size=\"1px\">\n",
692 menu_url(req, "menu"), "Cache Manager menu");
693 printf("<PRE>\n");
694 }
695
696 istate = isBody;
697 /* yes, fall through, we do not want to loose the first line */
698
699 case isBody:
700 /* interpret [and reformat] cache response */
701
702 if (parse_menu)
703 fputs(munge_menu_line(buf, req), stdout);
704 else
705 fputs(munge_other_line(buf, req), stdout);
706
707 break;
708
709 case isForward:
710 /* forward: no modifications allowed */
711 /*
712 * Note: we currently do not know any way to get browser.reply to
713 * 401 to .cgi because web server filters out all auth info. Thus we
714 * disable authentication headers for now.
715 */
716 if (!strncasecmp(buf, "WWW-Authenticate:", 17) || !strncasecmp(buf, "Proxy-Authenticate:", 19))
717
718 ; /* skip */
719 else
720 fputs(buf, stdout);
721
722 break;
723
724 case isEof:
725 /* print trailers */
726 if (parse_menu)
727 printf("</UL>\n");
728 else
729 printf("</table></PRE>\n");
730
731 print_trailer();
732
733 istate = isSuccess;
734
735 break;
736
737 case isForwardEof:
738 /* indicate that we finished processing an "error" sequence */
739 istate = isError;
740
741 break;
742
743 default:
744 printf("%s: internal bug: invalid state reached: %d", script_name, istate);
745
746 istate = isError;
747 }
748 }
749
750 fclose(fp);
751 #ifdef _SQUID_MSWIN_
752
753 remove(tmpfile);
754 xfree(tmpfile);
755 close(s);
756
757 #endif
758
759 return 0;
760 }
761
762 static int
763 process_request(cachemgr_request * req)
764 {
765
766 char ipbuf[MAX_IPSTRLEN];
767 struct addrinfo *AI = NULL;
768 IPAddress S;
769 int s;
770 int l;
771
772 static char buf[2 * 1024];
773
774 if (req == NULL) {
775 auth_html(CACHEMGR_HOSTNAME, CACHE_HTTP_PORT, "");
776 return 1;
777 }
778
779 if (req->hostname == NULL) {
780 req->hostname = xstrdup(CACHEMGR_HOSTNAME);
781 }
782
783 if (req->port == 0) {
784 req->port = CACHE_HTTP_PORT;
785 }
786
787 if (req->action == NULL) {
788 req->action = xstrdup("");
789 }
790
791 if (strcmp(req->action, "authenticate") == 0) {
792 auth_html(req->hostname, req->port, req->user_name);
793 return 0;
794 }
795
796 if (!check_target_acl(req->hostname, req->port)) {
797 snprintf(buf, 1024, "target %s:%d not allowed in cachemgr.conf\n", req->hostname, req->port);
798 error_html(buf);
799 return 1;
800 }
801
802 #if USE_IPV6
803 if ((s = socket(PF_INET6, SOCK_STREAM, 0)) < 0) {
804 #else
805 if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
806 #endif
807 snprintf(buf, 1024, "socket: %s\n", xstrerror());
808 error_html(buf);
809 return 1;
810 }
811
812 S = *gethostbyname(req->hostname);
813
814 if ( !S.IsAnyAddr() ) {
815 (void) 0;
816 } else if ( S = req->hostname)
817 (void) 0;
818 else {
819 snprintf(buf, 1024, "Unknown host: %s\n", req->hostname);
820 error_html(buf);
821 return 1;
822 }
823
824 S.SetPort(req->port);
825
826 S.GetAddrInfo(AI);
827
828 if (connect(s, AI->ai_addr, AI->ai_addrlen) < 0) {
829 snprintf(buf, 1024, "connect %s: %s\n",
830 S.ToURL(ipbuf,MAX_IPSTRLEN),
831 xstrerror());
832 error_html(buf);
833 S.FreeAddrInfo(AI);
834 return 1;
835 }
836
837 S.FreeAddrInfo(AI);
838
839 l = snprintf(buf, sizeof(buf),
840 "GET cache_object://%s/%s HTTP/1.0\r\n"
841 "Accept: */*\r\n"
842 "%s" /* Authentication info or nothing */
843 "\r\n",
844 req->hostname,
845 req->action,
846 make_auth_header(req));
847 write(s, buf, l);
848 debug(1) fprintf(stderr, "wrote request: '%s'\n", buf);
849 return read_reply(s, req);
850 }
851
852 int
853 main(int argc, char *argv[])
854 {
855 char *s;
856 cachemgr_request *req;
857
858 now = time(NULL);
859 #ifdef _SQUID_MSWIN_
860
861 Win32SockInit();
862 atexit(Win32SockCleanup);
863 _setmode( _fileno( stdin ), _O_BINARY );
864 _setmode( _fileno( stdout ), _O_BINARY );
865 _fmode = _O_BINARY;
866
867 if ((s = strrchr(argv[0], '\\')))
868 #else
869
870 if ((s = strrchr(argv[0], '/')))
871 #endif
872
873 progname = xstrdup(s + 1);
874 else
875 progname = xstrdup(argv[0]);
876
877 if ((s = getenv("SCRIPT_NAME")) != NULL)
878 script_name = xstrdup(s);
879
880 req = read_request();
881
882 return process_request(req);
883 }
884
885 static char *
886 read_post_request(void)
887 {
888 char *s;
889 char *buf;
890 int len;
891
892 if ((s = getenv("REQUEST_METHOD")) == NULL)
893 return NULL;
894
895 if (0 != strcasecmp(s, "POST"))
896 return NULL;
897
898 if ((s = getenv("CONTENT_LENGTH")) == NULL)
899 return NULL;
900
901 if ((len = atoi(s)) <= 0)
902 return NULL;
903
904 buf = (char *)xmalloc(len + 1);
905
906 fread(buf, len, 1, stdin);
907
908 buf[len] = '\0';
909
910 return buf;
911 }
912
913 static char *
914 read_get_request(void)
915 {
916 char *s;
917
918 if ((s = getenv("QUERY_STRING")) == NULL)
919 return NULL;
920
921 return xstrdup(s);
922 }
923
924 static cachemgr_request *
925 read_request(void)
926 {
927 char *buf;
928
929 cachemgr_request *req;
930 char *s;
931 char *t;
932 char *q;
933
934 if ((buf = read_post_request()) != NULL)
935 (void) 0;
936 else if ((buf = read_get_request()) != NULL)
937 (void) 0;
938 else
939 return NULL;
940
941 #ifdef _SQUID_MSWIN_
942
943 if (strlen(buf) == 0 || strlen(buf) == 4000)
944 #else
945
946 if (strlen(buf) == 0)
947 #endif
948 {
949 free(buf);
950 return NULL;
951 }
952
953 req = (cachemgr_request *)xcalloc(1, sizeof(cachemgr_request));
954
955 for (s = strtok(buf, "&"); s != NULL; s = strtok(NULL, "&")) {
956 t = xstrdup(s);
957
958 if ((q = strchr(t, '=')) == NULL)
959 continue;
960
961 *q++ = '\0';
962
963 rfc1738_unescape(t);
964
965 rfc1738_unescape(q);
966
967 if (0 == strcasecmp(t, "server") && strlen(q))
968 req->server = xstrdup(q);
969 else if (0 == strcasecmp(t, "host") && strlen(q))
970 req->hostname = xstrdup(q);
971 else if (0 == strcasecmp(t, "port") && strlen(q))
972 req->port = atoi(q);
973 else if (0 == strcasecmp(t, "user_name") && strlen(q))
974 req->user_name = xstrdup(q);
975 else if (0 == strcasecmp(t, "passwd") && strlen(q))
976 req->passwd = xstrdup(q);
977 else if (0 == strcasecmp(t, "auth") && strlen(q))
978 req->pub_auth = xstrdup(q), decode_pub_auth(req);
979 else if (0 == strcasecmp(t, "operation"))
980 req->action = xstrdup(q);
981 }
982
983 if (req->server && !req->hostname) {
984 char *p;
985 req->hostname = strtok(req->server, ":");
986
987 if ((p = strtok(NULL, ":")))
988 req->port = atoi(p);
989 }
990
991 make_pub_auth(req);
992 debug(1) fprintf(stderr, "cmgr: got req: host: '%s' port: %d uname: '%s' passwd: '%s' auth: '%s' oper: '%s'\n",
993 safe_str(req->hostname), req->port, safe_str(req->user_name), safe_str(req->passwd), safe_str(req->pub_auth), safe_str(req->action));
994 return req;
995 }
996
997
998 /* Routines to support authentication */
999
1000 /*
1001 * Encodes auth info into a "public" form.
1002 * Currently no powerful encryption is used.
1003 */
1004 static void
1005 make_pub_auth(cachemgr_request * req)
1006 {
1007 static char buf[1024];
1008 safe_free(req->pub_auth);
1009 debug(3) fprintf(stderr, "cmgr: encoding for pub...\n");
1010
1011 if (!req->passwd || !strlen(req->passwd))
1012 return;
1013
1014 /* host | time | user | passwd */
1015 snprintf(buf, sizeof(buf), "%s|%d|%s|%s",
1016 req->hostname,
1017 (int) now,
1018 req->user_name ? req->user_name : "",
1019 req->passwd);
1020
1021 debug(3) fprintf(stderr, "cmgr: pre-encoded for pub: %s\n", buf);
1022
1023 debug(3) fprintf(stderr, "cmgr: encoded: '%s'\n", base64_encode(buf));
1024
1025 req->pub_auth = xstrdup(base64_encode(buf));
1026 }
1027
1028 static void
1029 decode_pub_auth(cachemgr_request * req)
1030 {
1031 char *buf;
1032 const char *host_name;
1033 const char *time_str;
1034 const char *user_name;
1035 const char *passwd;
1036
1037 debug(2) fprintf(stderr, "cmgr: decoding pub: '%s'\n", safe_str(req->pub_auth));
1038 safe_free(req->passwd);
1039
1040 if (!req->pub_auth || strlen(req->pub_auth) < 4 + strlen(safe_str(req->hostname)))
1041 return;
1042
1043 buf = xstrdup(base64_decode(req->pub_auth));
1044
1045 debug(3) fprintf(stderr, "cmgr: length ok\n");
1046
1047 /* parse ( a lot of memory leaks, but that is cachemgr style :) */
1048 if ((host_name = strtok(buf, "|")) == NULL)
1049 return;
1050
1051 debug(3) fprintf(stderr, "cmgr: decoded host: '%s'\n", host_name);
1052
1053 if ((time_str = strtok(NULL, "|")) == NULL)
1054 return;
1055
1056 debug(3) fprintf(stderr, "cmgr: decoded time: '%s' (now: %d)\n", time_str, (int) now);
1057
1058 if ((user_name = strtok(NULL, "|")) == NULL)
1059 return;
1060
1061 debug(3) fprintf(stderr, "cmgr: decoded uname: '%s'\n", user_name);
1062
1063 if ((passwd = strtok(NULL, "|")) == NULL)
1064 return;
1065
1066 debug(2) fprintf(stderr, "cmgr: decoded passwd: '%s'\n", passwd);
1067
1068 /* verify freshness and validity */
1069 if (atoi(time_str) + passwd_ttl < now)
1070 return;
1071
1072 if (strcasecmp(host_name, req->hostname))
1073 return;
1074
1075 debug(1) fprintf(stderr, "cmgr: verified auth. info.\n");
1076
1077 /* ok, accept */
1078 xfree(req->user_name);
1079
1080 req->user_name = xstrdup(user_name);
1081
1082 req->passwd = xstrdup(passwd);
1083
1084 xfree(buf);
1085 }
1086
1087 static void
1088 reset_auth(cachemgr_request * req)
1089 {
1090 safe_free(req->passwd);
1091 safe_free(req->pub_auth);
1092 }
1093
1094 static const char *
1095 make_auth_header(const cachemgr_request * req)
1096 {
1097 static char buf[1024];
1098 size_t stringLength = 0;
1099 const char *str64;
1100
1101 if (!req->passwd)
1102 return "";
1103
1104 snprintf(buf, sizeof(buf), "%s:%s",
1105 req->user_name ? req->user_name : "",
1106 req->passwd);
1107
1108 str64 = base64_encode(buf);
1109
1110 stringLength += snprintf(buf, sizeof(buf), "Authorization: Basic %s\r\n", str64);
1111
1112 assert(stringLength < sizeof(buf));
1113
1114 stringLength += snprintf(&buf[stringLength], sizeof(buf) - stringLength,
1115 "Proxy-Authorization: Basic %s\r\n", str64);
1116
1117 return buf;
1118 }
1119
1120 static int
1121 check_target_acl(const char *hostname, int port)
1122 {
1123 char config_line[BUFSIZ];
1124 FILE *fp = NULL;
1125 int ret = 0;
1126 fp = fopen("cachemgr.conf", "r");
1127
1128 if (fp == NULL)
1129 fp = fopen(DEFAULT_CACHEMGR_CONFIG, "r");
1130
1131 if (fp == NULL) {
1132 #ifdef CACHEMGR_HOSTNAME_DEFINED
1133
1134 if (strcmp(hostname, CACHEMGR_HOSTNAME) == 0 && port == CACHE_HTTP_PORT)
1135 return 1;
1136
1137 #else
1138
1139 if (strcmp(hostname, "localhost") == 0)
1140 return 1;
1141
1142 if (strcmp(hostname, getfullhostname()) == 0)
1143 return 1;
1144
1145 #endif
1146
1147 return 0;
1148 }
1149
1150 while (fgets(config_line, BUFSIZ, fp)) {
1151 char *token = NULL;
1152 strtok(config_line, " \r\n\t");
1153
1154 if (config_line[0] == '#')
1155 continue;
1156
1157 if (config_line[0] == '\0')
1158 continue;
1159
1160 if ((token = strtok(config_line, ":")) == NULL)
1161 continue;
1162
1163 #if HAVE_FNMATCH_H
1164
1165 if (fnmatch(token, hostname, 0) != 0)
1166 continue;
1167
1168 #else
1169
1170 if (strcmp(token, hostname) != 0)
1171 continue;
1172
1173 #endif
1174
1175 if ((token = strtok(NULL, ":")) != NULL) {
1176 int i;
1177
1178 if (strcmp(token, "*") == 0)
1179
1180 ; /* Wildcard port specification */
1181 else if (strcasecmp(token, "any") == 0)
1182
1183 ; /* Wildcard port specification */
1184 else if (sscanf(token, "%d", &i) != 1)
1185 continue;
1186
1187 else if (i != port)
1188 continue;
1189 } else if (port != CACHE_HTTP_PORT)
1190 continue;
1191
1192 ret = 1;
1193
1194 break;
1195 }
1196
1197 fclose(fp);
1198 return ret;
1199 }