source: squid-ssl/trunk/fuentes/tools/cachemgr.cc @ 5496

Last change on this file since 5496 was 5496, checked in by Juanma, 22 months ago

Initial release

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