]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - tools/cachemgr.cc
SourceFormat Enforcement
[thirdparty/squid.git] / tools / cachemgr.cc
index 020103f3ec727de8487417388205897d408fcd4d..2c3e25cf60894db3e1b317a0a71cb4b5ba03274b 100644 (file)
@@ -1,33 +1,9 @@
 /*
- * DEBUG: section --    CGI Cache Manager
- * AUTHOR: Duane Wessels
- *
- * SQUID Web Proxy Cache          http://www.squid-cache.org/
- * ----------------------------------------------------------
- *
- *  Squid is the result of efforts by numerous individuals from
- *  the Internet community; see the CONTRIBUTORS file for full
- *  details.   Many organizations have provided support for Squid's
- *  development; see the SPONSORS file for full details.  Squid is
- *  Copyrighted (C) 2001 by the Regents of the University of
- *  California; see the COPYRIGHT file for full details.  Squid
- *  incorporates software developed and/or copyrighted by other
- *  sources; see the CREDITS file for full details.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
  *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #include "squid.h"
 #include "rfc1738.h"
 #include "util.h"
 
+#include <cctype>
+#include <cerrno>
+#include <csignal>
+#include <cstring>
+#include <ctime>
 #if HAVE_UNISTD_H
 #include <unistd.h>
 #endif
-#if HAVE_STDIO_H
-#include <stdio.h>
-#endif
-#if HAVE_CTYPE_H
-#include <ctype.h>
-#endif
-#if HAVE_ERRNO_H
-#include <errno.h>
-#endif
 #if HAVE_FCNTL_H
 #include <fcntl.h>
 #endif
 #if HAVE_PWD_H
 #include <pwd.h>
 #endif
-#if HAVE_SIGNAL_H
-#include <signal.h>
-#endif
-#if HAVE_TIME_H
-#include <time.h>
-#endif
 #if HAVE_SYS_PARAM_H
 #include <sys/param.h>
 #endif
 #if HAVE_LIBC_H
 #include <libc.h>
 #endif
-#if HAVE_STRING_H
-#include <string.h>
-#endif
 #if HAVE_STRINGS_H
 #include <strings.h>
 #endif
@@ -119,7 +82,6 @@ extern "C" {
 }
 #endif
 
-
 #ifndef DEFAULT_CACHEMGR_CONFIG
 #define DEFAULT_CACHEMGR_CONFIG "/etc/squid/cachemgr.conf"
 #endif
@@ -139,7 +101,7 @@ typedef struct {
 /*
  * Static variables and constants
  */
-static const time_t passwd_ttl = 60 * 60 * 3;  /* in sec */
+static const time_t passwd_ttl = 60 * 60 * 3;   /* in sec */
 static const char *script_name = "/cgi-bin/cachemgr.cgi";
 static const char *progname = NULL;
 static time_t now;
@@ -165,7 +127,7 @@ static const char *make_auth_header(const cachemgr_request * req);
 
 static int check_target_acl(const char *hostname, int port);
 
-#if _SQUID_MSWIN_
+#if _SQUID_WINDOWS_
 static int s_iInitCount = 0;
 
 int Win32SockInit(void)
@@ -325,7 +287,6 @@ auth_html(const char *host, int port, const char *user_name)
 
     printf("<TABLE BORDER=\"0\" CELLPADDING=\"10\" CELLSPACING=\"1\">\n");
 
-
     if (fp != NULL) {
         int servers = 0;
         char config_line[BUFSIZ];
@@ -519,7 +480,7 @@ munge_menu_line(const char *buf, cachemgr_request * req)
 }
 
 static const char *
-munge_other_line(const char *buf, cachemgr_request * req)
+munge_other_line(const char *buf, cachemgr_request *)
 {
     static const char *ttags[] = {"td", "th"};
 
@@ -596,12 +557,15 @@ munge_action_line(const char *_buf, cachemgr_request * req)
     if ((p = strchr(x, '\n')))
         *p = '\0';
     action = xstrtok(&x, '\t');
+    if (!action) {
+        xfree(buf);
+        return "";
+    }
     description = xstrtok(&x, '\t');
     if (!description)
         description = action;
-    if (!action)
-        return "";
     snprintf(html, sizeof(html), " <a href=\"%s\">%s</a>", menu_url(req, action), description);
+    xfree(buf);
     return html;
 }
 
@@ -609,7 +573,7 @@ static int
 read_reply(int s, cachemgr_request * req)
 {
     char buf[4 * 1024];
-#if _SQUID_MSWIN_
+#if _SQUID_WINDOWS_
 
     int reply;
     char *tmpfile = tempnam(NULL, "tmp0000");
@@ -633,7 +597,7 @@ read_reply(int s, cachemgr_request * req)
         parse_menu = 1;
 
     if (fp == NULL) {
-#if _SQUID_MSWIN_
+#if _SQUID_WINDOWS_
         perror(tmpfile);
         xfree(tmpfile);
 #else
@@ -645,7 +609,7 @@ read_reply(int s, cachemgr_request * req)
         return 1;
     }
 
-#if _SQUID_MSWIN_
+#if _SQUID_WINDOWS_
 
     while ((reply=recv(s, buf , sizeof(buf), 0)) > 0)
         fwrite(buf, 1, reply, fp);
@@ -674,23 +638,23 @@ read_reply(int s, cachemgr_request * req)
 
             if (status == 401 || status == 407) {
                 reset_auth(req);
-                status = 403;  /* Forbiden, see comments in case isForward: */
+                status = 403;   /* Forbiden, see comments in case isForward: */
             }
 
             /* this is a way to pass HTTP status to the Web server */
             if (statusStr)
-                printf("Status: %d %s", status, statusStr);    /* statusStr has '\n' */
+                printf("Status: %d %s", status, statusStr); /* statusStr has '\n' */
 
             break;
 
         case isHeaders:
             /* forward header field */
-            if (!strcmp(buf, "\r\n")) {                /* end of headers */
-                fputs("Content-Type: text/html\r\n", stdout);  /* add our type */
+            if (!strcmp(buf, "\r\n")) {     /* end of headers */
+                fputs("Content-Type: text/html\r\n", stdout);   /* add our type */
                 istate = isBodyStart;
             }
 
-            if (strncasecmp(buf, "Content-Type:", 13)) /* filter out their type */
+            if (strncasecmp(buf, "Content-Type:", 13))  /* filter out their type */
                 fputs(buf, stdout);
 
             break;
@@ -716,7 +680,7 @@ read_reply(int s, cachemgr_request * req)
             }
 
             istate = isActions;
-            /* yes, fall through, we do not want to loose the first line */
+        /* yes, fall through, we do not want to loose the first line */
 
         case isActions:
             if (strncmp(buf, "action:", 7) == 0) {
@@ -732,7 +696,7 @@ read_reply(int s, cachemgr_request * req)
             }
 
             istate = isBody;
-            /* yes, fall through, we do not want to loose the first line */
+        /* yes, fall through, we do not want to loose the first line */
 
         case isBody:
             /* interpret [and reformat] cache response */
@@ -751,7 +715,7 @@ read_reply(int s, cachemgr_request * req)
              * 401 to .cgi because web server filters out all auth info. Thus we
              * disable authentication headers for now.
              */
-            if (!strncasecmp(buf, "WWW-Authenticate:", 17) || !strncasecmp(buf, "Proxy-Authenticate:", 19));   /* skip */
+            if (!strncasecmp(buf, "WWW-Authenticate:", 17) || !strncasecmp(buf, "Proxy-Authenticate:", 19));    /* skip */
             else
                 fputs(buf, stdout);
 
@@ -784,7 +748,7 @@ read_reply(int s, cachemgr_request * req)
     }
 
     fclose(fp);
-#if _SQUID_MSWIN_
+#if _SQUID_WINDOWS_
 
     remove(tmpfile);
     xfree(tmpfile);
@@ -830,53 +794,55 @@ process_request(cachemgr_request * req)
     }
 
     if (!check_target_acl(req->hostname, req->port)) {
-        snprintf(buf, 1024, "target %s:%d not allowed in cachemgr.conf\n", req->hostname, req->port);
+        snprintf(buf, sizeof(buf), "target %s:%d not allowed in cachemgr.conf\n", req->hostname, req->port);
         error_html(buf);
         return 1;
     }
 
     S = *gethostbyname(req->hostname);
 
-    if ( !S.IsAnyAddr() ) {
+    if ( !S.isAnyAddr() ) {
         (void) 0;
     } else if ((S = req->hostname))
         (void) 0;
     else {
-        snprintf(buf, 1024, "Unknown host: %s\n", req->hostname);
+        snprintf(buf, sizeof(buf), "Unknown host: %s\n", req->hostname);
         error_html(buf);
         return 1;
     }
 
-    S.SetPort(req->port);
+    S.port(req->port);
 
-    S.GetAddrInfo(AI);
+    S.getAddrInfo(AI);
 
 #if USE_IPV6
     if ((s = socket( AI->ai_family, SOCK_STREAM, 0)) < 0) {
 #else
     if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
 #endif
-        snprintf(buf, 1024, "socket: %s\n", xstrerror());
+        snprintf(buf, sizeof(buf), "socket: %s\n", xstrerror());
         error_html(buf);
+        Ip::Address::FreeAddr(AI);
         return 1;
     }
 
     if (connect(s, AI->ai_addr, AI->ai_addrlen) < 0) {
-        snprintf(buf, 1024, "connect %s: %s\n",
-                 S.ToURL(ipbuf,MAX_IPSTRLEN),
+        snprintf(buf, sizeof(buf), "connect %s: %s\n",
+                 S.toUrl(ipbuf,MAX_IPSTRLEN),
                  xstrerror());
         error_html(buf);
-        S.FreeAddrInfo(AI);
+        Ip::Address::FreeAddr(AI);
+        close(s);
         return 1;
     }
 
-    S.FreeAddrInfo(AI);
+    Ip::Address::FreeAddr(AI);
 
     l = snprintf(buf, sizeof(buf),
                  "GET cache_object://%s/%s%s%s HTTP/1.0\r\n"
                  "User-Agent: cachemgr.cgi/%s\r\n"
                  "Accept: */*\r\n"
-                 "%s"                  /* Authentication info or nothing */
+                 "%s"           /* Authentication info or nothing */
                  "\r\n",
                  req->hostname,
                  req->action,
@@ -899,7 +865,7 @@ main(int argc, char *argv[])
     cachemgr_request *req;
 
     now = time(NULL);
-#if _SQUID_MSWIN_
+#if _SQUID_WINDOWS_
 
     Win32SockInit();
     atexit(Win32SockCleanup);
@@ -954,8 +920,6 @@ static char *
 read_post_request(void)
 {
     char *s;
-    char *buf;
-    int len;
 
     if ((s = getenv("REQUEST_METHOD")) == NULL)
         return NULL;
@@ -966,15 +930,34 @@ read_post_request(void)
     if ((s = getenv("CONTENT_LENGTH")) == NULL)
         return NULL;
 
-    if ((len = atoi(s)) <= 0)
+    if (*s == '-') // negative length content huh?
         return NULL;
 
-    buf = (char *)xmalloc(len + 1);
+    uint64_t len;
 
-    if (fread(buf, len, 1, stdin) == 0)
+    char *endptr = s+ strlen(s);
+    if ((len = strtoll(s, &endptr, 10)) <= 0)
         return NULL;
 
-    buf[len] = '\0';
+    // limit the input to something reasonable.
+    // 4KB should be enough for the GET/POST data length, but may be extended.
+    size_t bufLen = (len < 4096 ? len : 4095);
+    char *buf = (char *)xmalloc(bufLen + 1);
+
+    size_t readLen = fread(buf, 1, bufLen, stdin);
+    if (readLen == 0) {
+        xfree(buf);
+        return NULL;
+    }
+    buf[readLen] = '\0';
+    len -= readLen;
+
+    // purge the remainder of the request entity
+    while (len > 0 && readLen) {
+        char temp[65535];
+        readLen = fread(temp, 1, 65535, stdin);
+        len -= readLen;
+    }
 
     return buf;
 }
@@ -1007,7 +990,7 @@ read_request(void)
     else
         return NULL;
 
-#if _SQUID_MSWIN_
+#if _SQUID_WINDOWS_
 
     if (strlen(buf) == 0 || strlen(buf) == 4000)
 #else
@@ -1034,24 +1017,25 @@ read_request(void)
 
         rfc1738_unescape(q);
 
-        if (0 == strcasecmp(t, "server") && strlen(q))
+        if (0 == strcmp(t, "server") && strlen(q))
             req->server = xstrdup(q);
-        else if (0 == strcasecmp(t, "host") && strlen(q))
+        else if (0 == strcmp(t, "host") && strlen(q))
             req->hostname = xstrdup(q);
-        else if (0 == strcasecmp(t, "port") && strlen(q))
+        else if (0 == strcmp(t, "port") && strlen(q))
             req->port = atoi(q);
-        else if (0 == strcasecmp(t, "user_name") && strlen(q))
+        else if (0 == strcmp(t, "user_name") && strlen(q))
             req->user_name = xstrdup(q);
-        else if (0 == strcasecmp(t, "passwd") && strlen(q))
+        else if (0 == strcmp(t, "passwd") && strlen(q))
             req->passwd = xstrdup(q);
-        else if (0 == strcasecmp(t, "auth") && strlen(q))
+        else if (0 == strcmp(t, "auth") && strlen(q))
             req->pub_auth = xstrdup(q), decode_pub_auth(req);
-        else if (0 == strcasecmp(t, "operation"))
+        else if (0 == strcmp(t, "operation"))
             req->action = xstrdup(q);
-        else if (0 == strcasecmp(t, "workers") && strlen(q))
+        else if (0 == strcmp(t, "workers") && strlen(q))
             req->workers = xstrdup(q);
-        else if (0 == strcasecmp(t, "processes") && strlen(q))
+        else if (0 == strcmp(t, "processes") && strlen(q))
             req->processes = xstrdup(q);
+        safe_free(t);
     }
 
     if (req->server && !req->hostname) {
@@ -1068,7 +1052,6 @@ read_request(void)
     return req;
 }
 
-
 /* Routines to support authentication */
 
 /*
@@ -1095,7 +1078,11 @@ make_pub_auth(cachemgr_request * req)
 
     const int encodedLen = base64_encode_len(bufLen);
     req->pub_auth = (char *) xmalloc(encodedLen);
-    base64_encode_str(req->pub_auth, encodedLen, buf, bufLen);
+    struct base64_encode_ctx ctx;
+    base64_encode_init(&ctx);
+    size_t blen = base64_encode_update(&ctx, reinterpret_cast<uint8_t*>(req->pub_auth), bufLen, reinterpret_cast<uint8_t*>(buf));
+    blen += base64_encode_final(&ctx, reinterpret_cast<uint8_t*>(req->pub_auth)+blen);
+    req->pub_auth[blen] = '\0';
     debug("cmgr: encoded: '%s'\n", req->pub_auth);
 }
 
@@ -1114,44 +1101,63 @@ decode_pub_auth(cachemgr_request * req)
     if (!req->pub_auth || strlen(req->pub_auth) < 4 + strlen(safe_str(req->hostname)))
         return;
 
-    const int decodedLen = base64_decode_len(req->pub_auth);
+    size_t decodedLen = BASE64_DECODE_LENGTH(strlen(req->pub_auth));
     buf = (char*)xmalloc(decodedLen);
-    base64_decode(buf, decodedLen, req->pub_auth);
+    struct base64_decode_ctx ctx;
+    base64_decode_init(&ctx);
+    if (!base64_decode_update(&ctx, &decodedLen, reinterpret_cast<uint8_t*>(buf), strlen(req->pub_auth), reinterpret_cast<const uint8_t*>(req->pub_auth)) ||
+            !base64_decode_final(&ctx)) {
+        debug("cmgr: base64 decode failure. Incomplete auth token string.\n");
+        xfree(buf);
+        return;
+    }
 
     debug("cmgr: length ok\n");
 
     /* parse ( a lot of memory leaks, but that is cachemgr style :) */
-    if ((host_name = strtok(buf, "|")) == NULL)
+    if ((host_name = strtok(buf, "|")) == NULL) {
+        xfree(buf);
         return;
+    }
 
     debug("cmgr: decoded host: '%s'\n", host_name);
 
-    if ((time_str = strtok(NULL, "|")) == NULL)
+    if ((time_str = strtok(NULL, "|")) == NULL) {
+        xfree(buf);
         return;
+    }
 
     debug("cmgr: decoded time: '%s' (now: %d)\n", time_str, (int) now);
 
-    if ((user_name = strtok(NULL, "|")) == NULL)
+    if ((user_name = strtok(NULL, "|")) == NULL) {
+        xfree(buf);
         return;
+    }
 
     debug("cmgr: decoded uname: '%s'\n", user_name);
 
-    if ((passwd = strtok(NULL, "|")) == NULL)
+    if ((passwd = strtok(NULL, "|")) == NULL) {
+        xfree(buf);
         return;
+    }
 
     debug("cmgr: decoded passwd: '%s'\n", passwd);
 
     /* verify freshness and validity */
-    if (atoi(time_str) + passwd_ttl < now)
+    if (atoi(time_str) + passwd_ttl < now) {
+        xfree(buf);
         return;
+    }
 
-    if (strcasecmp(host_name, req->hostname))
+    if (strcasecmp(host_name, req->hostname)) {
+        xfree(buf);
         return;
+    }
 
     debug("cmgr: verified auth. info.\n");
 
     /* ok, accept */
-    xfree(req->user_name);
+    safe_free(req->user_name);
 
     req->user_name = xstrdup(user_name);
 
@@ -1184,15 +1190,20 @@ make_auth_header(const cachemgr_request * req)
     if (encodedLen <= 0)
         return "";
 
-    char *str64 = static_cast<char*>(xmalloc(encodedLen));
-    base64_encode_str(str64, encodedLen, buf, bufLen);
+    uint8_t *str64 = static_cast<uint8_t*>(xmalloc(encodedLen));
+    struct base64_encode_ctx ctx;
+    base64_encode_init(&ctx);
+    size_t blen = base64_encode_update(&ctx, str64, bufLen, reinterpret_cast<uint8_t*>(buf));
+    blen += base64_encode_final(&ctx, str64+blen);
+    str64[blen] = '\0';
 
-    stringLength += snprintf(buf, sizeof(buf), "Authorization: Basic %s\r\n", str64);
+    stringLength += snprintf(buf, sizeof(buf), "Authorization: Basic %.*s\r\n", (int)blen, str64);
 
     assert(stringLength < sizeof(buf));
 
-    snprintf(&buf[stringLength], sizeof(buf) - stringLength, "Proxy-Authorization: Basic %s\r\n", str64);
+    snprintf(&buf[stringLength], sizeof(buf) - stringLength, "Proxy-Authorization: Basic %.*s\r\n", (int)blen, str64);
 
+    xfree(str64);
     return buf;
 }
 
@@ -1257,7 +1268,7 @@ check_target_acl(const char *hostname, int port)
             if (strcmp(token, "*") == 0)
 
                 ;   /* Wildcard port specification */
-            else if (strcasecmp(token, "any") == 0)
+            else if (strcmp(token, "any") == 0)
 
                 ;   /* Wildcard port specification */
             else if (sscanf(token, "%d", &i) != 1)
@@ -1276,3 +1287,4 @@ check_target_acl(const char *hostname, int port)
     fclose(fp);
     return ret;
 }
+