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