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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors