]> git.ipfire.org Git - thirdparty/squid.git/blame - src/cachemgr.cc
show if cbdata valid in event queue for cachemgr
[thirdparty/squid.git] / src / cachemgr.cc
CommitLineData
2ac76861 1
30a4f2a8 2/*
b6a2f15e 3 * $Id: cachemgr.cc,v 1.85 1999/04/15 06:15:46 wessels Exp $
30a4f2a8 4 *
f43e2ec2 5 * DEBUG: section 0 CGI Cache Manager
d23f4e1b 6 * AUTHOR: Duane Wessels
30a4f2a8 7 *
42c04c16 8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
e25c139f 9 * ----------------------------------------------------------
30a4f2a8 10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
e25c139f 13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * 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>
64#elif HAVE_MALLOC_H && !defined(_SQUID_FREEBSD_) && !defined(_SQUID_NEXT_)
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
090089c4 128
473471f2 129#include "util.h"
042461c3 130#include "snprintf.h"
090089c4 131
d23f4e1b 132typedef struct {
133 char *hostname;
134 int port;
135 char *action;
7395afb8 136 char *user_name;
d23f4e1b 137 char *passwd;
7395afb8 138 char *pub_auth;
d23f4e1b 139} cachemgr_request;
090089c4 140
d23f4e1b 141/*
7395afb8 142 * Debugging macros (info goes to error_log on your web server)
143 * Note: do not run cache manager with non zero debugging level
144 * if you do not debug, it may write a lot of [sensitive]
145 * information to your error log.
d23f4e1b 146 */
7395afb8 147
148/* debugging level 0 (disabled) - 3 (max) */
149#define DEBUG_LEVEL 0
b9bc2838 150#define debug(level) if ((level) <= DEBUG_LEVEL && DEBUG_LEVEL > 0)
7395afb8 151
152/*
153 * Static variables and constants
154 */
2ac76861 155static const time_t passwd_ttl = 60 * 60 * 3; /* in sec */
0ee4272b 156static const char *script_name = "/cgi-bin/cachemgr.cgi";
0ee4272b 157static const char *progname = NULL;
d1a43e28 158static time_t now;
429fdbec 159static struct in_addr no_addr;
090089c4 160
d23f4e1b 161/*
162 * Function prototypes
163 */
4055bb7e 164#define safe_free(str) { if (str) { xfree(str); (str) = NULL; } }
7395afb8 165static const char *safe_str(const char *str);
166static char *xstrtok(char **str, char del);
f5b8bbc4 167static void print_trailer(void);
7395afb8 168static void auth_html(char *host, int port, const char *user_name);
d23f4e1b 169static void error_html(const char *msg);
7395afb8 170static char *menu_url(cachemgr_request * req, const char *action);
171static int parse_status_line(const char *sline, const char **statusStr);
d23f4e1b 172static cachemgr_request *read_request(void);
173static char *read_get_request(void);
174static char *read_post_request(void);
175
2ac76861 176static void make_pub_auth(cachemgr_request * req);
177static void decode_pub_auth(cachemgr_request * req);
178static void reset_auth(cachemgr_request * req);
179static const char *make_auth_header(const cachemgr_request * req);
7395afb8 180
181
2ac76861 182static const char *
183safe_str(const char *str)
7395afb8 184{
185 return str ? str : "";
186}
187
7021844c 188/* relaxed number format */
189static int
190is_number(const char *str)
191{
192 return strspn(str, "\t -+01234567890./\n") == strlen(str);
193}
194
2ac76861 195static char *
196xstrtok(char **str, char del)
7395afb8 197{
198 if (*str) {
199 char *p = strchr(*str, del);
200 char *tok = *str;
201 int len;
202 if (p) {
2ac76861 203 *str = p + 1;
7395afb8 204 *p = '\0';
205 } else
206 *str = NULL;
207 /* trim */
208 len = strlen(tok);
b6a2f15e 209 while (len && xisspace(tok[len - 1]))
2ac76861 210 tok[--len] = '\0';
b6a2f15e 211 while (xisspace(*tok))
2ac76861 212 tok++;
213 return tok;
7395afb8 214 } else
215 return "";
216}
090089c4 217
24382924 218static void
8203a132 219print_trailer(void)
090089c4 220{
090089c4 221 printf("<HR>\n");
222 printf("<ADDRESS>\n");
223 printf("Generated %s, by %s/%s@%s\n",
72367f90 224 mkrfc1123(now), progname, SQUID_VERSION, getfullhostname());
a8f7d3ee 225 printf("</ADDRESS></BODY></HTML>\n");
090089c4 226}
227
24382924 228static void
7395afb8 229auth_html(char *host, int port, const char *user_name)
090089c4 230{
2ac76861 231 if (!user_name)
232 user_name = "";
233 if (!host || !strlen(host))
234 host = "localhost";
d23f4e1b 235 printf("Content-type: text/html\r\n\r\n");
a8f7d3ee 236 printf("<HTML><HEAD><TITLE>Cache Manager Interface</TITLE></HEAD>\n");
237 printf("<BODY><H1>Cache Manager Interface</H1>\n");
f7d9a2ad 238 printf("<P>This is a WWW interface to the instrumentation interface\n");
239 printf("for the Squid object cache.</P>\n");
090089c4 240 printf("<HR>\n");
bce0cf8c 241 printf("<FORM METHOD=\"GET\" ACTION=\"%s\">\n", script_name);
7395afb8 242 printf("<TABLE BORDER=0>\n");
243 printf("<TR><TH ALIGN=\"left\">Cache Host:</TH><TD><INPUT NAME=\"host\" ");
12cf1be2 244 printf("SIZE=30 VALUE=\"%s\"></TD></TR>\n", host);
7395afb8 245 printf("<TR><TH ALIGN=\"left\">Cache Port:</TH><TD><INPUT NAME=\"port\" ");
246 printf("SIZE=30 VALUE=\"%d\"></TD></TR>\n", port);
247 printf("<TR><TH ALIGN=\"left\">Manager name:</TH><TD><INPUT NAME=\"user_name\" ");
248 printf("SIZE=30 VALUE=\"%s\"></TD></TR>\n", user_name);
249 printf("<TR><TH ALIGN=\"left\">Password:</TH><TD><INPUT TYPE=\"password\" NAME=\"passwd\" ");
250 printf("SIZE=30 VALUE=\"\"></TD></TR>\n");
251 printf("</TABLE><BR CLEAR=\"all\">\n");
d23f4e1b 252 printf("<INPUT TYPE=\"submit\" VALUE=\"Continue...\">\n");
7395afb8 253 printf("</FORM>\n");
090089c4 254 print_trailer();
255}
256
d23f4e1b 257static void
258error_html(const char *msg)
090089c4 259{
d23f4e1b 260 printf("Content-type: text/html\r\n\r\n");
261 printf("<HTML><HEAD><TITLE>Cache Manager Error</TITLE></HEAD>\n");
262 printf("<BODY><H1>Cache Manager Error</H1>\n");
263 printf("<P>\n%s</P>\n", msg);
264 print_trailer();
090089c4 265}
266
7395afb8 267/* returns http status extracted from status line or -1 on parsing failure */
268static int
269parse_status_line(const char *sline, const char **statusStr)
270{
271 const char *sp = strchr(sline, ' ');
272 if (statusStr)
273 *statusStr = NULL;
274 if (strncasecmp(sline, "HTTP/", 5) || !sp)
275 return -1;
b6a2f15e 276 while (xisspace(*++sp));
277 if (!xisdigit(*sp))
7395afb8 278 return -1;
279 if (statusStr)
280 *statusStr = sp;
281 return atoi(sp);
282}
283
d23f4e1b 284static char *
7395afb8 285menu_url(cachemgr_request * req, const char *action)
090089c4 286{
d23f4e1b 287 static char url[1024];
137ee196 288 snprintf(url, sizeof(url), "%s?host=%s&port=%d&user_name=%s&operation=%s&auth=%s",
d23f4e1b 289 script_name,
290 req->hostname,
291 req->port,
7395afb8 292 safe_str(req->user_name),
293 action,
294 safe_str(req->pub_auth));
d23f4e1b 295 return url;
090089c4 296}
297
d23f4e1b 298static const char *
299munge_menu_line(const char *buf, cachemgr_request * req)
090089c4 300{
d23f4e1b 301 char *x;
7395afb8 302 const char *a;
303 const char *d;
304 const char *p;
305 char *a_url;
7021844c 306 char *buf_copy;
399e85ea 307 static char html[2 * 1024];
d23f4e1b 308 if (strlen(buf) < 1)
309 return buf;
310 if (*buf != ' ')
311 return buf;
7021844c 312 buf_copy = x = xstrdup(buf);
7395afb8 313 a = xstrtok(&x, '\t');
314 d = xstrtok(&x, '\t');
315 p = xstrtok(&x, '\t');
316 a_url = xstrdup(menu_url(req, a));
317 /* no reason to give a url for a disabled action */
318 if (!strcmp(p, "disabled"))
7021844c 319 snprintf(html, sizeof(html), "<LI type=\"circle\">%s (disabled)<A HREF=\"%s\">.</A>\n", d, a_url);
7395afb8 320 else
2ac76861 321 /* disable a hidden action (requires a password, but password is not in squid.conf) */
7395afb8 322 if (!strcmp(p, "hidden"))
7021844c 323 snprintf(html, sizeof(html), "<LI type=\"circle\">%s (hidden)<A HREF=\"%s\">.</A>\n", d, a_url);
7395afb8 324 else
2ac76861 325 /* disable link if authentication is required and we have no password */
326 if (!strcmp(p, "protected") && !req->passwd)
7021844c 327 snprintf(html, sizeof(html), "<LI type=\"circle\">%s (requires <a href=\"%s\">authentication</a>)<A HREF=\"%s\">.</A>\n",
7395afb8 328 d, menu_url(req, "authenticate"), a_url);
329 else
2ac76861 330 /* highlight protected but probably available entries */
7395afb8 331 if (!strcmp(p, "protected"))
7021844c 332 snprintf(html, sizeof(html), "<LI type=\"square\"><A HREF=\"%s\"><font color=\"#FF0000\">%s</font></A>\n",
7395afb8 333 a_url, d);
334 /* public entry or unknown type of protection */
335 else
7021844c 336 snprintf(html, sizeof(html), "<LI type=\"disk\"><A HREF=\"%s\">%s</A>\n", a_url, d);
7395afb8 337 xfree(a_url);
7021844c 338 xfree(buf_copy);
339 return html;
340}
341
342static const char *
343munge_other_line(const char *buf, cachemgr_request * req)
344{
399e85ea 345 static const char *ttags[] =
346 {"td", "th"};
7021844c 347 static char html[4096];
728da2ee 348 static int table_line_num = 0;
349 static int next_is_header = 0;
7021844c 350 int is_header = 0;
351 const char *ttag;
7021844c 352 char *buf_copy;
b9bc2838 353 char *x, *p;
7021844c 354 int l = 0;
355 /* does it look like a table? */
356 if (!strchr(buf, '\t') || *buf == '\t') {
357 /* nope, just text */
358 snprintf(html, sizeof(html), "%s%s",
359 table_line_num ? "</table>\n<pre>" : "", buf);
360 table_line_num = 0;
361 return html;
362 }
b9bc2838 363 /* start html table */
364 if (!table_line_num) {
399e85ea 365 l += snprintf(html + l, sizeof(html) - l, "</pre><table border=1 cellpadding=2 cellspacing=1>\n");
b9bc2838 366 next_is_header = 0;
367 }
368 /* remove '\n' */
369 is_header = (!table_line_num || next_is_header) && !strchr(buf, ':') && !is_number(buf);
7021844c 370 ttag = ttags[is_header];
371 /* record starts */
399e85ea 372 l += snprintf(html + l, sizeof(html) - l, "<tr>");
7021844c 373 /* substitute '\t' */
374 buf_copy = x = xstrdup(buf);
399e85ea 375 if ((p = strchr(x, '\n')))
376 *p = '\0';
b9bc2838 377 while (x && strlen(x)) {
378 int column_span = 1;
379 const char *cell = xstrtok(&x, '\t');
399e85ea 380 while (x && *x == '\t') {
381 column_span++;
382 x++;
383 }
384 l += snprintf(html + l, sizeof(html) - l, "<%s colspan=%d align=\"%s\">%s</%s>",
b9bc2838 385 ttag, column_span,
7021844c 386 is_header ? "center" : is_number(cell) ? "right" : "left",
387 cell, ttag);
388 }
389 xfree(buf_copy);
390 /* record ends */
399e85ea 391 l += snprintf(html + l, sizeof(html) - l, "</tr>\n");
b9bc2838 392 next_is_header = is_header && strstr(buf, "\t\t");
7021844c 393 table_line_num++;
d23f4e1b 394 return html;
090089c4 395}
396
d23f4e1b 397static int
398read_reply(int s, cachemgr_request * req)
090089c4 399{
2ac76861 400 char buf[4 * 1024];
d23f4e1b 401 FILE *fp = fdopen(s, "r");
7395afb8 402 /* interpretation states */
2ac76861 403 enum {
404 isStatusLine, isHeaders, isBodyStart, isBody, isForward, isEof, isForwardEof, isSuccess, isError
405 } istate = isStatusLine;
d23f4e1b 406 int parse_menu = 0;
7395afb8 407 const char *action = req->action;
408 const char *statusStr = NULL;
409 int status = -1;
d23f4e1b 410 if (0 == strlen(req->action))
411 parse_menu = 1;
412 else if (0 == strcasecmp(req->action, "menu"))
413 parse_menu = 1;
414 if (fp == NULL) {
415 perror("fdopen");
416 return 1;
090089c4 417 }
7395afb8 418 if (parse_menu)
419 action = "menu";
420 /* read reply interpreting one line at a time depending on state */
421 while (istate < isEof) {
422 if (!fgets(buf, sizeof(buf), fp))
423 istate = istate == isForward ? isForwardEof : isEof;
424 switch (istate) {
425 case isStatusLine:
426 /* get HTTP status */
427 /* uncomment the following if you want to debug headers */
2ac76861 428 /* fputs("\r\n\r\n", stdout); */
7395afb8 429 status = parse_status_line(buf, &statusStr);
430 istate = status == 200 ? isHeaders : isForward;
431 /* if cache asks for authentication, we have to reset our info */
432 if (status == 401 || status == 407) {
433 reset_auth(req);
2ac76861 434 status = 403; /* Forbiden, see comments in case isForward: */
7395afb8 435 }
436 /* this is a way to pass HTTP status to the Web server */
437 if (statusStr)
2ac76861 438 printf("Status: %d %s", status, statusStr); /* statusStr has '\n' */
7395afb8 439 break;
440 case isHeaders:
441 /* forward header field */
2ac76861 442 if (!strcmp(buf, "\r\n")) { /* end of headers */
443 fputs("Content-Type: text/html\r\n", stdout); /* add our type */
7395afb8 444 istate = isBodyStart;
445 }
2ac76861 446 if (strncasecmp(buf, "Content-Type:", 13)) /* filter out their type */
447 fputs(buf, stdout);
7395afb8 448 break;
449 case isBodyStart:
450 printf("<HTML><HEAD><TITLE>CacheMgr@%s: %s</TITLE></HEAD><BODY>\n",
2ac76861 451 req->hostname, action);
7395afb8 452 if (parse_menu) {
453 printf("<H2><a href=\"%s\">Cache Manager</a> menu for %s:</H2>",
454 menu_url(req, "authenticate"), req->hostname);
455 printf("<UL>\n");
456 } else {
457 printf("<P><A HREF=\"%s\">%s</A>\n<HR>\n",
458 menu_url(req, "menu"), "Cache Manager menu");
459 printf("<PRE>\n");
460 }
461 istate = isBody;
2edfbcb7 462 /* yes, fall through, we do not want to loose the first line */
7395afb8 463 case isBody:
464 /* interpret [and reformat] cache response */
d23f4e1b 465 if (parse_menu)
466 fputs(munge_menu_line(buf, req), stdout);
467 else
7021844c 468 fputs(munge_other_line(buf, req), stdout);
7395afb8 469 break;
470 case isForward:
471 /* forward: no modifications allowed */
472 /*
473 * Note: we currently do not know any way to get browser.reply to
474 * 401 to .cgi because web server filters out all auth info. Thus we
475 * disable authentication headers for now.
476 */
2ac76861 477 if (!strncasecmp(buf, "WWW-Authenticate:", 17) || !strncasecmp(buf, "Proxy-Authenticate:", 19)); /* skip */
7395afb8 478 else
2ac76861 479 fputs(buf, stdout);
7395afb8 480 break;
481 case isEof:
482 /* print trailers */
483 if (parse_menu)
484 printf("</UL>\n");
485 else
5e91f8bc 486 printf("</table></PRE>\n");
7395afb8 487 print_trailer();
488 istate = isSuccess;
489 break;
490 case isForwardEof:
491 /* indicate that we finished processing an "error" sequence */
492 istate = isError;
493 break;
494 default:
495 printf("%s: internal bug: invalid state reached: %d", script_name, istate);
496 istate = isError;
497 }
d23f4e1b 498 }
d23f4e1b 499 close(s);
500 return 0;
d1a43e28 501}
090089c4 502
d23f4e1b 503static int
504process_request(cachemgr_request * req)
090089c4 505{
d23f4e1b 506 const struct hostent *hp;
507 static struct sockaddr_in S;
508 int s;
509 int l;
2ac76861 510 static char buf[2 * 1024];
d23f4e1b 511 if (req == NULL) {
7395afb8 512 auth_html(CACHEMGR_HOSTNAME, CACHE_HTTP_PORT, "");
d23f4e1b 513 return 1;
514 }
515 if (req->hostname == NULL) {
516 req->hostname = xstrdup(CACHEMGR_HOSTNAME);
517 }
518 if (req->port == 0) {
519 req->port = CACHE_HTTP_PORT;
520 }
521 if (req->action == NULL) {
522 req->action = xstrdup("");
523 }
7395afb8 524 if (!strcmp(req->action, "authenticate")) {
525 auth_html(req->hostname, req->port, req->user_name);
526 return 0;
527 }
d23f4e1b 528 if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
529 snprintf(buf, 1024, "socket: %s\n", xstrerror());
530 error_html(buf);
531 return 1;
532 }
533 memset(&S, '\0', sizeof(struct sockaddr_in));
534 S.sin_family = AF_INET;
535 if ((hp = gethostbyname(req->hostname)) != NULL)
536 xmemcpy(&S.sin_addr.s_addr, hp->h_addr, hp->h_length);
537 else if (safe_inet_addr(req->hostname, &S.sin_addr))
538 (void) 0;
539 else {
540 snprintf(buf, 1024, "Unknown host: %s\n", req->hostname);
541 error_html(buf);
542 return 1;
543 }
544 S.sin_port = htons(req->port);
545 if (connect(s, (struct sockaddr *) &S, sizeof(struct sockaddr_in)) < 0) {
546 snprintf(buf, 1024, "connect: %s\n", xstrerror());
547 error_html(buf);
548 return 1;
549 }
7395afb8 550 l = snprintf(buf, sizeof(buf),
d23f4e1b 551 "GET cache_object://%s/%s HTTP/1.0\r\n"
552 "Accept: */*\r\n"
2ac76861 553 "%s" /* Authentication info or nothing */
d23f4e1b 554 "\r\n",
555 req->hostname,
7395afb8 556 req->action,
557 make_auth_header(req));
d23f4e1b 558 write(s, buf, l);
7395afb8 559 debug(1) fprintf(stderr, "wrote request: '%s'\n", buf);
d23f4e1b 560 return read_reply(s, req);
090089c4 561}
562
684c2720 563int
8203a132 564main(int argc, char *argv[])
090089c4 565{
d23f4e1b 566 char *s;
567 cachemgr_request *req;
429fdbec 568 safe_inet_addr("255.255.255.255", &no_addr);
d1a43e28 569 now = time(NULL);
090089c4 570 if ((s = strrchr(argv[0], '/')))
3c0117c9 571 progname = xstrdup(s + 1);
090089c4 572 else
3c0117c9 573 progname = xstrdup(argv[0]);
d23f4e1b 574 if ((s = getenv("SCRIPT_NAME")) != NULL)
3c0117c9 575 script_name = xstrdup(s);
d23f4e1b 576 req = read_request();
577 return process_request(req);
090089c4 578}
579
d23f4e1b 580static char *
581read_post_request(void)
090089c4 582{
d23f4e1b 583 char *s;
584 char *buf;
585 int len;
586 if ((s = getenv("REQUEST_METHOD")) == NULL)
587 return NULL;
588 if (0 != strcasecmp(s, "POST"))
589 return NULL;
590 if ((s = getenv("CONTENT_LENGTH")) == NULL)
591 return NULL;
592 if ((len = atoi(s)) <= 0)
593 return NULL;
594 buf = xmalloc(len + 1);
595 fread(buf, len, 1, stdin);
596 buf[len] = '\0';
597 return buf;
598}
090089c4 599
d23f4e1b 600static char *
601read_get_request(void)
602{
603 char *s;
604 if ((s = getenv("QUERY_STRING")) == NULL)
605 return NULL;
606 return xstrdup(s);
607}
090089c4 608
d23f4e1b 609static cachemgr_request *
610read_request(void)
611{
612 char *buf;
613 cachemgr_request *req;
614 char *s;
615 char *t;
616 char *q;
617 if ((buf = read_post_request()) != NULL)
618 (void) 0;
619 else if ((buf = read_get_request()) != NULL)
429fdbec 620 (void) 0;
233794cd 621 else
d23f4e1b 622 return NULL;
623 if (strlen(buf) == 0)
624 return NULL;
625 req = xcalloc(1, sizeof(cachemgr_request));
626 for (s = strtok(buf, "&"); s != NULL; s = strtok(NULL, "&")) {
627 t = xstrdup(s);
628 if ((q = strchr(t, '=')) == NULL)
629 continue;
630 *q++ = '\0';
7395afb8 631 if (0 == strcasecmp(t, "host") && strlen(q))
d23f4e1b 632 req->hostname = xstrdup(q);
2ac76861 633 else if (0 == strcasecmp(t, "port") && strlen(q))
d23f4e1b 634 req->port = atoi(q);
2ac76861 635 else if (0 == strcasecmp(t, "user_name") && strlen(q))
7395afb8 636 req->user_name = xstrdup(q);
2ac76861 637 else if (0 == strcasecmp(t, "passwd") && strlen(q))
d23f4e1b 638 req->passwd = xstrdup(q);
2ac76861 639 else if (0 == strcasecmp(t, "auth") && strlen(q))
7395afb8 640 req->pub_auth = xstrdup(q), decode_pub_auth(req);
2ac76861 641 else if (0 == strcasecmp(t, "operation"))
d23f4e1b 642 req->action = xstrdup(q);
643 }
7395afb8 644 make_pub_auth(req);
645 debug(1) fprintf(stderr, "cmgr: got req: host: '%s' port: %d uname: '%s' passwd: '%s' auth: '%s' oper: '%s'\n",
646 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 647 return req;
090089c4 648}
7395afb8 649
650
651/* Routines to support authentication */
652
653/*
654 * Encodes auth info into a "public" form.
655 * Currently no powerful encryption is used.
656 */
657static void
2ac76861 658make_pub_auth(cachemgr_request * req)
7395afb8 659{
660 static char buf[1024];
661 safe_free(req->pub_auth);
662 debug(3) fprintf(stderr, "cmgr: encoding for pub...\n");
7021844c 663 if (!req->passwd || !strlen(req->passwd))
7395afb8 664 return;
665 /* host | time | user | passwd */
666 snprintf(buf, sizeof(buf), "%s|%d|%s|%s",
667 req->hostname,
1afe05c5 668 (int) now,
7395afb8 669 req->user_name ? req->user_name : "",
670 req->passwd);
671 debug(3) fprintf(stderr, "cmgr: pre-encoded for pub: %s\n", buf);
672 debug(3) fprintf(stderr, "cmgr: encoded: '%s'\n", base64_encode(buf));
673 req->pub_auth = xstrdup(base64_encode(buf));
674}
675
676static void
2ac76861 677decode_pub_auth(cachemgr_request * req)
7395afb8 678{
679 char *buf;
680 const char *host_name;
681 const char *time_str;
682 const char *user_name;
683 const char *passwd;
684
685 debug(2) fprintf(stderr, "cmgr: decoding pub: '%s'\n", safe_str(req->pub_auth));
686 safe_free(req->passwd);
687 if (!req->pub_auth || strlen(req->pub_auth) < 4 + strlen(safe_str(req->hostname)))
688 return;
689 buf = xstrdup(base64_decode(req->pub_auth));
690 debug(3) fprintf(stderr, "cmgr: length ok\n");
691 /* parse ( a lot of memory leaks, but that is cachemgr style :) */
692 if ((host_name = strtok(buf, "|")) == NULL)
693 return;
694 debug(3) fprintf(stderr, "cmgr: decoded host: '%s'\n", host_name);
695 if ((time_str = strtok(NULL, "|")) == NULL)
696 return;
2ac76861 697 debug(3) fprintf(stderr, "cmgr: decoded time: '%s' (now: %d)\n", time_str, (int) now);
7395afb8 698 if ((user_name = strtok(NULL, "|")) == NULL)
699 return;
700 debug(3) fprintf(stderr, "cmgr: decoded uname: '%s'\n", user_name);
701 if ((passwd = strtok(NULL, "|")) == NULL)
702 return;
703 debug(2) fprintf(stderr, "cmgr: decoded passwd: '%s'\n", passwd);
704 /* verify freshness and validity */
705 if (atoi(time_str) + passwd_ttl < now)
706 return;
707 if (strcasecmp(host_name, req->hostname))
708 return;
709 debug(1) fprintf(stderr, "cmgr: verified auth. info.\n");
710 /* ok, accept */
711 xfree(req->user_name);
712 req->user_name = xstrdup(user_name);
713 req->passwd = xstrdup(passwd);
714 xfree(buf);
715}
716
717static void
2ac76861 718reset_auth(cachemgr_request * req)
7395afb8 719{
720 safe_free(req->passwd);
721 safe_free(req->pub_auth);
722}
723
724static const char *
2ac76861 725make_auth_header(const cachemgr_request * req)
7395afb8 726{
727 static char buf[1024];
b6a2f15e 728 off_t l = 0;
7395afb8 729 const char *str64;
2ac76861 730 if (!req->passwd)
7395afb8 731 return "";
732
2ac76861 733 snprintf(buf, sizeof(buf), "%s:%s",
7395afb8 734 req->user_name ? req->user_name : "",
2ac76861 735 req->passwd);
7395afb8 736
737 str64 = base64_encode(buf);
b6a2f15e 738 l += snprintf(buf, sizeof(buf), "Authorization: Basic %s\r\n", str64);
739 l += snprintf(&buf[l], sizeof(buf) - l,
740 "Proxy-Authorization: Basic %s\r\n", str64);
7395afb8 741 return buf;
742}