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