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