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