]>
git.ipfire.org Git - thirdparty/squid.git/blob - tools/cachemgr.cc
2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
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.
10 #include "base/CharacterSet.h"
12 #include "getfullhostname.h"
13 #include "html_quote.h"
14 #include "ip/Address.h"
35 #include <gnumalloc.h>
49 #include <sys/param.h>
52 #include <sys/socket.h>
55 #include <netinet/in.h>
58 #include <arpa/inet.h>
87 #ifndef DEFAULT_CACHEMGR_CONFIG
88 #define DEFAULT_CACHEMGR_CONFIG "/etc/squid/cachemgr.conf"
104 * Static variables and constants
106 static const time_t passwd_ttl
= 60 * 60 * 3; /* in sec */
107 static const char *script_name
= "/cgi-bin/cachemgr.cgi";
108 static const char *progname
= NULL
;
112 * Function prototypes
114 static const char *safe_str(const char *str
);
115 static const char *xstrtok(char **str
, char del
);
116 static void print_trailer(void);
117 static void auth_html(const char *host
, int port
, const char *user_name
);
118 static void error_html(const char *msg
);
119 static char *menu_url(cachemgr_request
* req
, const char *action
);
120 static int parse_status_line(const char *sline
, const char **statusStr
);
121 static cachemgr_request
*read_request(void);
122 static char *read_get_request(void);
123 static char *read_post_request(void);
125 static void make_pub_auth(cachemgr_request
* req
);
126 static void decode_pub_auth(cachemgr_request
* req
);
127 static void reset_auth(cachemgr_request
* req
);
128 static const char *make_auth_header(const cachemgr_request
* req
);
130 static int check_target_acl(const char *hostname
, int port
);
133 static int s_iInitCount
= 0;
135 int Win32SockInit(void)
137 int iVersionRequested
;
141 if (s_iInitCount
> 0) {
144 } else if (s_iInitCount
< 0)
145 return (s_iInitCount
);
147 /* s_iInitCount == 0. Do the initialization */
148 iVersionRequested
= MAKEWORD(2, 0);
150 err
= WSAStartup((WORD
) iVersionRequested
, &wsaData
);
154 return (s_iInitCount
);
157 if (LOBYTE(wsaData
.wVersion
) != 2 ||
158 HIBYTE(wsaData
.wVersion
) != 0) {
161 return (s_iInitCount
);
165 return (s_iInitCount
);
168 void Win32SockCleanup(void)
170 if (--s_iInitCount
== 0)
179 safe_str(const char *str
)
181 return str
? str
: "";
184 /* relaxed number format */
186 is_number(const char *str
)
188 return strspn(str
, "\t -+01234567890./\n") == strlen(str
);
192 xstrtok(char **str
, char del
)
195 char *p
= strchr(*str
, del
);
208 while (len
&& xisspace(tok
[len
- 1]))
211 while (xisspace(*tok
))
220 hostname_check(const char *uri
)
222 static CharacterSet hostChars
= CharacterSet("host",".:[]_") +
223 CharacterSet::ALPHA
+ CharacterSet::DIGIT
;
225 const auto limit
= strlen(uri
);
226 for (size_t i
= 0; i
< limit
; i
++) {
227 if (!hostChars
[uri
[i
]]) {
237 printf("<HR noshade size=\"1px\">\n");
238 printf("<ADDRESS>\n");
239 printf("Generated %s, by %s/%s@%s\n",
240 mkrfc1123(now
), progname
, VERSION
, getfullhostname());
241 printf("</ADDRESS></BODY></HTML>\n");
245 auth_html(const char *host
, int port
, const char *user_name
)
253 if (!host
|| !strlen(host
))
256 fp
= fopen("cachemgr.conf", "r");
259 fp
= fopen(DEFAULT_CACHEMGR_CONFIG
, "r");
262 printf("X-Error: message=\"Unable to open config %s\"", DEFAULT_CACHEMGR_CONFIG
);
264 printf("Content-Type: text/html\r\n\r\n");
266 printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
268 printf("<HTML><HEAD><TITLE>Cache Manager Interface</TITLE>\n");
270 printf("<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n");
272 printf("<script type=\"text/javascript\">\n");
273 printf("function TS(t, s) {\n");
274 printf(" var x = new XMLHttpRequest();\n");
275 printf(" x.open('GET', 'http' + s + '://' + t + '/squid-internal-mgr/', true);\n");
276 printf(" x.onreadystatechange=function() {\n");
277 printf(" if (x.readyState==4) {\n");
278 printf(" if ((x.status>=200 && x.status <= 299) || x.status==401) {\n");
279 printf(" var v = x.getResponseHeader('Server');\n");
280 printf(" if (v.substring(0,8) == 'squid/3.' && (v[8]=='H' || parseInt(v.substring(8)) >= 2)) {\n");
281 printf(" var d = document.getElementById('H' + s + 'mgr');\n");
282 printf(" if (d.innerHTML == '') d.innerHTML = '<h2>HTTP' + (s=='s'?'S':'') + ' Managed Proxies</h2>';\n");
283 printf(" d.innerHTML = d.innerHTML + '<p>Host: <a href=\"http' + s + '://' + t + '/squid-internal-mgr/\">' + t + '</a></p>';\n");
285 printf(" x.send(null);\n");
287 printf("</script>\n");
291 printf("<BODY><H1>Cache Manager Interface</H1>\n");
293 printf("<P>This is a WWW interface to the instrumentation interface\n");
295 printf("for the Squid object cache.</P>\n");
297 printf("<HR noshade size=\"1px\">\n");
299 printf("<div id=\"Hsmgr\"></div>\n");
300 printf("<div id=\"Hmgr\"></div>\n");
301 printf("<div id=\"Cmgr\">\n");
302 printf("<h2>CGI Managed Proxies</h2>\n");
303 printf("<FORM METHOD=\"POST\" ACTION=\"%s\">\n", script_name
);
305 printf("<TABLE BORDER=\"0\" CELLPADDING=\"10\" CELLSPACING=\"1\">\n");
309 char config_line
[BUFSIZ
];
311 while (fgets(config_line
, BUFSIZ
, fp
)) {
312 char *server
, *comment
;
313 if (strtok(config_line
, "\r\n") == nullptr)
316 if (config_line
[0] == '#')
319 if (config_line
[0] == '\0')
322 if ((server
= strtok(config_line
, " \t")) == NULL
)
325 if (strchr(server
, '*') || strchr(server
, '[') || strchr(server
, '?')) {
330 comment
= strtok(NULL
, "");
333 while (*comment
== ' ' || *comment
== '\t')
336 if (!comment
|| !*comment
)
340 printf("<TR><TH ALIGN=\"left\">Cache Server:</TH><TD><SELECT id=\"server\" NAME=\"server\">\n");
342 printf("<OPTION VALUE=\"%s\"%s>%s</OPTION>\n", server
, (servers
|| *host
) ? "" : " SELECTED", comment
);
347 if (need_host
== 1 && !*host
)
351 printf("<OPTION VALUE=\"\"%s>Other</OPTION>\n", (*host
) ? " SELECTED" : "");
353 printf("</SELECT></TR>\n");
360 if (need_host
== 1 && !*host
)
363 printf("<TR><TH ALIGN=\"left\">Cache Host:</TH><TD><INPUT NAME=\"host\" ");
365 printf("size=\"30\" VALUE=\"%s\"></TD></TR>\n", host
);
367 printf("<TR><TH ALIGN=\"left\">Cache Port:</TH><TD><INPUT NAME=\"port\" ");
369 printf("size=\"30\" VALUE=\"%d\"></TD></TR>\n", port
);
372 printf("<TR><TH ALIGN=\"left\">Manager name:</TH><TD><INPUT NAME=\"user_name\" ");
374 printf("size=\"30\" VALUE=\"%s\"></TD></TR>\n", rfc1738_escape(user_name
));
376 printf("<TR><TH ALIGN=\"left\">Password:</TH><TD><INPUT TYPE=\"password\" NAME=\"passwd\" ");
378 printf("size=\"30\" VALUE=\"\"></TD></TR>\n");
380 printf("</TABLE><BR CLEAR=\"all\">\n");
382 printf("<INPUT TYPE=\"submit\" VALUE=\"Continue...\">\n");
384 printf("</FORM></div>\n");
386 printf("<script type=\"text/javascript\">\n");
387 printf("var s = document.getElementById(\"server\");\n");
388 printf("for (var i = 0; i < s.childElementCount; i++) {\n");
389 printf(" TS(s.children[i].value, '');\n");
390 printf(" TS(s.children[i].value, 's');\n");
391 printf("}</script>\n");
397 error_html(const char *msg
)
399 printf("Content-Type: text/html\r\n\r\n");
400 printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
401 printf("<HTML><HEAD><TITLE>Cache Manager Error</TITLE>\n");
402 printf("<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE></HEAD>\n");
403 printf("<BODY><H1>Cache Manager Error</H1>\n");
404 printf("<P>\n%s</P>\n", html_quote(msg
));
408 /* returns http status extracted from status line or -1 on parsing failure */
410 parse_status_line(const char *sline
, const char **statusStr
)
412 const char *sp
= strchr(sline
, ' ');
417 if (strncasecmp(sline
, "HTTP/", 5) || !sp
)
420 while (xisspace(*++sp
));
431 menu_url(cachemgr_request
* req
, const char *action
)
433 static char url
[1024];
434 snprintf(url
, sizeof(url
), "%s?host=%s&port=%d&user_name=%s&operation=%s&auth=%s",
438 rfc1738_escape(safe_str(req
->user_name
)),
440 safe_str(req
->pub_auth
));
445 munge_menu_line(MemBuf
&out
, const char *buf
, cachemgr_request
* req
)
454 const char bufLen
= strlen(buf
);
455 if (bufLen
< 1 || *buf
!= ' ') {
456 out
.append(buf
, bufLen
);
460 buf_copy
= x
= xstrndup(buf
, bufLen
+1);
462 a
= xstrtok(&x
, '\t');
464 d
= xstrtok(&x
, '\t');
466 p
= xstrtok(&x
, '\t');
468 a_url
= xstrdup(menu_url(req
, a
));
470 /* no reason to give a url for a disabled action */
471 if (!strcmp(p
, "disabled"))
472 out
.appendf("<LI type=\"circle\">%s (disabled)<A HREF=\"%s\">.</A>\n", d
, a_url
);
474 /* disable a hidden action (requires a password, but password is not in squid.conf) */
475 if (!strcmp(p
, "hidden"))
476 out
.appendf("<LI type=\"circle\">%s (hidden)<A HREF=\"%s\">.</A>\n", d
, a_url
);
478 /* disable link if authentication is required and we have no password */
479 if (!strcmp(p
, "protected") && !req
->passwd
)
480 out
.appendf("<LI type=\"circle\">%s (requires <a href=\"%s\">authentication</a>)<A HREF=\"%s\">.</A>\n",
481 d
, menu_url(req
, "authenticate"), a_url
);
483 /* highlight protected but probably available entries */
484 if (!strcmp(p
, "protected"))
485 out
.appendf("<LI type=\"square\"><A HREF=\"%s\"><font color=\"#FF0000\">%s</font></A>\n",
488 /* public entry or unknown type of protection */
490 out
.appendf("<LI type=\"disk\"><A HREF=\"%s\">%s</A>\n", a_url
, d
);
498 munge_other_line(MemBuf
&out
, const char *buf
, cachemgr_request
*)
500 static const char *ttags
[] = {"td", "th"};
502 static int table_line_num
= 0;
503 static int next_is_header
= 0;
508 /* does it look like a table? */
510 if (!strchr(buf
, '\t') || *buf
== '\t') {
511 /* nope, just text */
513 out
.append("</table>\n<pre>", 14);
514 out
.appendf("%s", html_quote(buf
));
519 /* start html table */
520 if (!table_line_num
) {
521 out
.append("</pre><table cellpadding=\"2\" cellspacing=\"1\">\n", 46);
526 is_header
= (!table_line_num
|| next_is_header
) && !strchr(buf
, ':') && !is_number(buf
);
528 ttag
= ttags
[is_header
];
531 out
.append("<tr>", 4);
533 /* substitute '\t' */
534 buf_copy
= x
= xstrdup(buf
);
536 if ((p
= strchr(x
, '\n')))
539 while (x
&& strlen(x
)) {
541 const char *cell
= xstrtok(&x
, '\t');
543 while (x
&& *x
== '\t') {
548 out
.appendf("<%s colspan=\"%d\" align=\"%s\">%s</%s>",
550 is_header
? "center" : is_number(cell
) ? "right" : "left",
551 html_quote(cell
), ttag
);
556 out
.append("</tr>\n", 6);
557 next_is_header
= is_header
&& strstr(buf
, "\t\t");
562 munge_action_line(const char *_buf
, cachemgr_request
* req
)
564 static char html
[2 * 1024];
565 char *buf
= xstrdup(_buf
);
567 const char *action
, *description
;
570 if ((p
= strchr(x
, '\n')))
572 action
= xstrtok(&x
, '\t');
577 description
= xstrtok(&x
, '\t');
579 description
= action
;
580 snprintf(html
, sizeof(html
), " <a href=\"%s\">%s</a>", menu_url(req
, action
), description
);
586 read_reply(int s
, cachemgr_request
* req
)
592 char *tmpfile
= tempnam(NULL
, "tmp0000");
593 FILE *fp
= fopen(tmpfile
, "w+");
596 FILE *fp
= fdopen(s
, "r");
598 /* interpretation states */
600 isStatusLine
, isHeaders
, isActions
, isBodyStart
, isBody
, isForward
, isEof
, isForwardEof
, isSuccess
, isError
601 } istate
= isStatusLine
;
603 const char *action
= req
->action
;
604 const char *statusStr
= NULL
;
607 if (0 == strlen(req
->action
))
609 else if (0 == strcasecmp(req
->action
, "menu"))
627 while ((reply
=recv(s
, buf
, sizeof(buf
), 0)) > 0)
628 fwrite(buf
, 1, reply
, fp
);
637 /* read reply interpreting one line at a time depending on state */
638 while (istate
< isEof
) {
639 if (!fgets(buf
, sizeof(buf
), fp
))
640 istate
= istate
== isForward
? isForwardEof
: isEof
;
645 /* get HTTP status */
646 /* uncomment the following if you want to debug headers */
647 /* fputs("\r\n\r\n", stdout); */
648 status
= parse_status_line(buf
, &statusStr
);
649 istate
= status
== 200 ? isHeaders
: isForward
;
650 /* if cache asks for authentication, we have to reset our info */
652 if (status
== 401 || status
== 407) {
654 status
= 403; /* Forbidden, see comments in case isForward: */
657 /* this is a way to pass HTTP status to the Web server */
659 printf("Status: %d %s", status
, statusStr
); /* statusStr has '\n' */
664 /* forward header field */
665 if (!strcmp(buf
, "\r\n")) { /* end of headers */
666 fputs("Content-Type: text/html\r\n", stdout
); /* add our type */
667 istate
= isBodyStart
;
670 if (strncasecmp(buf
, "Content-Type:", 13)) /* filter out their type */
676 printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
678 printf("<HTML><HEAD><TITLE>CacheMgr@%s: %s</TITLE>\n",
679 req
->hostname
, action
);
681 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");
683 printf("</HEAD><BODY>\n");
686 printf("<H2><a href=\"%s\">Cache Manager</a> menu for %s:</H2>",
687 menu_url(req
, "authenticate"), req
->hostname
);
690 printf("<P><A HREF=\"%s\">%s</A>\n<HR noshade size=\"1px\">\n",
691 menu_url(req
, "menu"), "Cache Manager menu");
696 /* [[fallthrough]] we do not want to lose the first line */
699 if (strncmp(buf
, "action:", 7) == 0) {
701 fputs(munge_action_line(buf
+ 7, req
), stdout
);
707 printf("<HR noshade size=\"1px\">\n");
712 /* [[fallthrough]] we do not want to lose the first line */
716 /* interpret [and reformat] cache response */
720 munge_menu_line(out
, buf
, req
);
722 munge_other_line(out
, buf
, req
);
724 fputs(out
.buf
, stdout
);
729 /* forward: no modifications allowed */
731 * Note: we currently do not know any way to get browser.reply to
732 * 401 to .cgi because web server filters out all auth info. Thus we
733 * disable authentication headers for now.
735 if (!strncasecmp(buf
, "WWW-Authenticate:", 17) || !strncasecmp(buf
, "Proxy-Authenticate:", 19)); /* skip */
746 printf("</table></PRE>\n");
755 /* indicate that we finished processing an "error" sequence */
761 printf("%s: internal bug: invalid state reached: %d", script_name
, istate
);
780 process_request(cachemgr_request
* req
)
783 char ipbuf
[MAX_IPSTRLEN
];
784 struct addrinfo
*AI
= NULL
;
789 static char buf
[2 * 1024];
792 auth_html(CACHEMGR_HOSTNAME
, CACHE_HTTP_PORT
, "");
796 if (req
->hostname
== NULL
) {
797 req
->hostname
= xstrdup(CACHEMGR_HOSTNAME
);
800 if (req
->port
== 0) {
801 req
->port
= CACHE_HTTP_PORT
;
804 if (req
->action
== NULL
) {
805 req
->action
= xstrdup("");
808 if (strcmp(req
->action
, "authenticate") == 0) {
809 auth_html(req
->hostname
, req
->port
, req
->user_name
);
813 if (!check_target_acl(req
->hostname
, req
->port
)) {
814 snprintf(buf
, sizeof(buf
), "target %s:%d not allowed in cachemgr.conf\n", req
->hostname
, req
->port
);
819 S
= *gethostbyname(req
->hostname
);
821 if ( !S
.isAnyAddr() ) {
823 } else if ((S
= req
->hostname
))
826 if (hostname_check(req
->hostname
)) {
827 snprintf(buf
, sizeof(buf
), "Unknown Host: %s\n", req
->hostname
);
831 snprintf(buf
, sizeof(buf
), "%s\n", "Invalid Hostname");
842 if ((s
= socket( AI
->ai_family
, SOCK_STREAM
, 0)) < 0) {
844 if ((s
= socket(PF_INET
, SOCK_STREAM
, 0)) < 0) {
847 snprintf(buf
, sizeof(buf
), "socket: %s\n", xstrerr(xerrno
));
849 Ip::Address::FreeAddr(AI
);
853 if (connect(s
, AI
->ai_addr
, AI
->ai_addrlen
) < 0) {
855 snprintf(buf
, sizeof(buf
), "connect %s: %s\n", S
.toUrl(ipbuf
,MAX_IPSTRLEN
), xstrerr(xerrno
));
857 Ip::Address::FreeAddr(AI
);
862 Ip::Address::FreeAddr(AI
);
864 l
= snprintf(buf
, sizeof(buf
),
865 "GET cache_object://%s/%s%s%s HTTP/1.0\r\n"
866 "User-Agent: cachemgr.cgi/%s\r\n"
868 "%s" /* Authentication info or nothing */
872 req
->workers
? "?workers=" : (req
->processes
? "?processes=" : ""),
873 req
->workers
? req
->workers
: (req
->processes
? req
->processes
: ""),
875 make_auth_header(req
));
876 if (write(s
, buf
, l
) < 0) {
877 fprintf(stderr
,"ERROR: (%d) writing request: '%s'\n", errno
, buf
);
879 debug("wrote request: '%s'\n", buf
);
881 return read_reply(s
, req
);
885 main(int argc
, char *argv
[])
888 cachemgr_request
*req
;
894 atexit(Win32SockCleanup
);
895 _setmode( _fileno( stdin
), _O_BINARY
);
896 _setmode( _fileno( stdout
), _O_BINARY
);
899 if ((s
= strrchr(argv
[0], '\\')))
902 if ((s
= strrchr(argv
[0], '/')))
905 progname
= xstrdup(s
+ 1);
907 progname
= xstrdup(argv
[0]);
909 if ((s
= getenv("SCRIPT_NAME")) != NULL
)
910 script_name
= xstrdup(s
);
913 while (argc
> 1 && args
[1][0] == '-') {
914 char option
= args
[1][1];
926 req
= read_request();
928 return process_request(req
);
932 read_post_request(void)
936 if ((s
= getenv("REQUEST_METHOD")) == NULL
)
939 if (0 != strcasecmp(s
, "POST"))
942 if ((s
= getenv("CONTENT_LENGTH")) == NULL
)
945 if (*s
== '-') // negative length content huh?
950 char *endptr
= s
+ strlen(s
);
951 if ((len
= strtoll(s
, &endptr
, 10)) <= 0)
954 // limit the input to something reasonable.
955 // 4KB should be enough for the GET/POST data length, but may be extended.
956 size_t bufLen
= (len
< 4096 ? len
: 4095);
957 char *buf
= (char *)xmalloc(bufLen
+ 1);
959 size_t readLen
= fread(buf
, 1, bufLen
, stdin
);
967 // purge the remainder of the request entity
968 while (len
> 0 && readLen
) {
970 readLen
= fread(temp
, 1, 65535, stdin
);
978 read_get_request(void)
982 if ((s
= getenv("QUERY_STRING")) == NULL
)
988 static cachemgr_request
*
993 cachemgr_request
*req
;
998 if ((buf
= read_post_request()) != NULL
)
1000 else if ((buf
= read_get_request()) != NULL
)
1007 if (strlen(buf
) == 0 || strlen(buf
) == 4000)
1010 if (strlen(buf
) == 0)
1017 req
= (cachemgr_request
*)xcalloc(1, sizeof(cachemgr_request
));
1019 for (s
= strtok(buf
, "&"); s
!= NULL
; s
= strtok(NULL
, "&")) {
1023 if ((q
= strchr(t
, '=')) == NULL
)
1029 rfc1738_unescape(t
);
1031 rfc1738_unescape(q
);
1033 if (0 == strcmp(t
, "server") && strlen(q
))
1034 req
->server
= xstrdup(q
);
1035 else if (0 == strcmp(t
, "host") && strlen(q
))
1036 req
->hostname
= xstrdup(q
);
1037 else if (0 == strcmp(t
, "port") && strlen(q
))
1038 req
->port
= atoi(q
);
1039 else if (0 == strcmp(t
, "user_name") && strlen(q
))
1040 req
->user_name
= xstrdup(q
);
1041 else if (0 == strcmp(t
, "passwd") && strlen(q
))
1042 req
->passwd
= xstrdup(q
);
1043 else if (0 == strcmp(t
, "auth") && strlen(q
))
1044 req
->pub_auth
= xstrdup(q
), decode_pub_auth(req
);
1045 else if (0 == strcmp(t
, "operation"))
1046 req
->action
= xstrdup(q
);
1047 else if (0 == strcmp(t
, "workers") && strlen(q
))
1048 req
->workers
= xstrdup(q
);
1049 else if (0 == strcmp(t
, "processes") && strlen(q
))
1050 req
->processes
= xstrdup(q
);
1054 if (req
->server
&& !req
->hostname
) {
1056 req
->hostname
= strtok(req
->server
, ":");
1058 if ((p
= strtok(NULL
, ":")))
1059 req
->port
= atoi(p
);
1063 debug("cmgr: got req: host: '%s' port: %d uname: '%s' passwd: '%s' auth: '%s' oper: '%s' workers: '%s' processes: '%s'\n",
1064 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
));
1068 /* Routines to support authentication */
1071 * Encodes auth info into a "public" form.
1072 * Currently no powerful encryption is used.
1075 make_pub_auth(cachemgr_request
* req
)
1077 static char buf
[1024];
1078 safe_free(req
->pub_auth
);
1079 debug("cmgr: encoding for pub...\n");
1081 if (!req
->passwd
|| !strlen(req
->passwd
))
1084 auto *rfc1738_username
= xstrdup(rfc1738_escape(safe_str(req
->user_name
)));
1085 auto *rfc1738_passwd
= xstrdup(rfc1738_escape(req
->passwd
));
1087 /* host | time | user | passwd */
1088 const int bufLen
= snprintf(buf
, sizeof(buf
), "%s|%d|%s|%s",
1093 debug("cmgr: pre-encoded for pub: %s\n", buf
);
1095 safe_free(rfc1738_username
);
1096 safe_free(rfc1738_passwd
);
1098 const int encodedLen
= base64_encode_len(bufLen
);
1099 req
->pub_auth
= (char *) xmalloc(encodedLen
);
1100 struct base64_encode_ctx ctx
;
1101 base64_encode_init(&ctx
);
1102 size_t blen
= base64_encode_update(&ctx
, req
->pub_auth
, bufLen
, reinterpret_cast<uint8_t*>(buf
));
1103 blen
+= base64_encode_final(&ctx
, req
->pub_auth
+ blen
);
1104 req
->pub_auth
[blen
] = '\0';
1105 debug("cmgr: encoded: '%s'\n", req
->pub_auth
);
1109 decode_pub_auth(cachemgr_request
* req
)
1111 const char *host_name
;
1112 const char *time_str
;
1114 debug("cmgr: decoding pub: '%s'\n", safe_str(req
->pub_auth
));
1115 safe_free(req
->passwd
);
1117 if (!req
->pub_auth
|| strlen(req
->pub_auth
) < 4 + strlen(safe_str(req
->hostname
)))
1120 char *buf
= static_cast<char*>(xmalloc(BASE64_DECODE_LENGTH(strlen(req
->pub_auth
))+1));
1121 struct base64_decode_ctx ctx
;
1122 base64_decode_init(&ctx
);
1123 size_t decodedLen
= 0;
1124 if (!base64_decode_update(&ctx
, &decodedLen
, reinterpret_cast<uint8_t*>(buf
), strlen(req
->pub_auth
), req
->pub_auth
) ||
1125 !base64_decode_final(&ctx
)) {
1126 debug("cmgr: base64 decode failure. Incomplete auth token string.\n");
1130 buf
[decodedLen
] = '\0';
1132 debug("cmgr: length ok\n");
1134 /* parse ( a lot of memory leaks, but that is cachemgr style :) */
1135 if ((host_name
= strtok(buf
, "|")) == NULL
) {
1140 debug("cmgr: decoded host: '%s'\n", host_name
);
1142 if ((time_str
= strtok(NULL
, "|")) == NULL
) {
1147 debug("cmgr: decoded time: '%s' (now: %d)\n", time_str
, (int) now
);
1150 if ((user_name
= strtok(NULL
, "|")) == NULL
) {
1154 rfc1738_unescape(user_name
);
1156 debug("cmgr: decoded uname: '%s'\n", user_name
);
1159 if ((passwd
= strtok(NULL
, "|")) == NULL
) {
1163 rfc1738_unescape(passwd
);
1165 debug("cmgr: decoded passwd: '%s'\n", passwd
);
1167 /* verify freshness and validity */
1168 if (atoi(time_str
) + passwd_ttl
< now
) {
1173 if (strcasecmp(host_name
, req
->hostname
)) {
1178 debug("cmgr: verified auth. info.\n");
1181 safe_free(req
->user_name
);
1183 req
->user_name
= xstrdup(user_name
);
1185 req
->passwd
= xstrdup(passwd
);
1191 reset_auth(cachemgr_request
* req
)
1193 safe_free(req
->passwd
);
1194 safe_free(req
->pub_auth
);
1198 make_auth_header(const cachemgr_request
* req
)
1200 static char buf
[1024];
1201 size_t stringLength
= 0;
1206 int bufLen
= snprintf(buf
, sizeof(buf
), "%s:%s",
1207 req
->user_name
? req
->user_name
: "",
1210 int encodedLen
= base64_encode_len(bufLen
);
1211 if (encodedLen
<= 0)
1214 char *str64
= static_cast<char *>(xmalloc(encodedLen
));
1215 struct base64_encode_ctx ctx
;
1216 base64_encode_init(&ctx
);
1217 size_t blen
= base64_encode_update(&ctx
, str64
, bufLen
, reinterpret_cast<uint8_t*>(buf
));
1218 blen
+= base64_encode_final(&ctx
, str64
+blen
);
1221 stringLength
+= snprintf(buf
, sizeof(buf
), "Authorization: Basic %.*s\r\n", (int)blen
, str64
);
1223 assert(stringLength
< sizeof(buf
));
1225 snprintf(&buf
[stringLength
], sizeof(buf
) - stringLength
, "Proxy-Authorization: Basic %.*s\r\n", (int)blen
, str64
);
1232 check_target_acl(const char *hostname
, int port
)
1234 char config_line
[BUFSIZ
];
1237 fp
= fopen("cachemgr.conf", "r");
1240 fp
= fopen(DEFAULT_CACHEMGR_CONFIG
, "r");
1243 #ifdef CACHEMGR_HOSTNAME_DEFINED
1244 // TODO: simplify and maybe get rid of CACHEMGR_HOSTNAME altogether
1245 if (strcmp(hostname
, CACHEMGR_HOSTNAME
) == 0 && port
== CACHE_HTTP_PORT
)
1250 if (strcmp(hostname
, "localhost") == 0)
1253 if (strcmp(hostname
, getfullhostname()) == 0)
1261 while (fgets(config_line
, BUFSIZ
, fp
)) {
1263 strtok(config_line
, " \r\n\t");
1265 if (config_line
[0] == '#')
1268 if (config_line
[0] == '\0')
1271 if ((token
= strtok(config_line
, ":")) == NULL
)
1276 if (fnmatch(token
, hostname
, 0) != 0)
1281 if (strcmp(token
, hostname
) != 0)
1286 if ((token
= strtok(NULL
, ":")) != NULL
) {
1289 if (strcmp(token
, "*") == 0)
1291 ; /* Wildcard port specification */
1292 else if (strcmp(token
, "any") == 0)
1294 ; /* Wildcard port specification */
1295 else if (sscanf(token
, "%d", &i
) != 1)
1300 } else if (port
!= CACHE_HTTP_PORT
)