cachemgr.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2017 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",
224  mkrfc1123(now), progname, VERSION, getfullhostname());
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  strtok(config_line, "\r\n");
298 
299  if (config_line[0] == '#')
300  continue;
301 
302  if (config_line[0] == '\0')
303  continue;
304 
305  if ((server = strtok(config_line, " \t")) == NULL)
306  continue;
307 
308  if (strchr(server, '*') || strchr(server, '[') || strchr(server, '?')) {
309  need_host = -1;
310  continue;
311  }
312 
313  comment = strtok(NULL, "");
314 
315  if (comment)
316  while (*comment == ' ' || *comment == '\t')
317  ++comment;
318 
319  if (!comment || !*comment)
320  comment = server;
321 
322  if (!servers)
323  printf("<TR><TH ALIGN=\"left\">Cache Server:</TH><TD><SELECT id=\"server\" NAME=\"server\">\n");
324 
325  printf("<OPTION VALUE=\"%s\"%s>%s</OPTION>\n", server, (servers || *host) ? "" : " SELECTED", comment);
326  ++servers;
327  }
328 
329  if (servers) {
330  if (need_host == 1 && !*host)
331  need_host = 0;
332 
333  if (need_host)
334  printf("<OPTION VALUE=\"\"%s>Other</OPTION>\n", (*host) ? " SELECTED" : "");
335 
336  printf("</SELECT></TR>\n");
337  }
338 
339  fclose(fp);
340  }
341 
342  if (need_host) {
343  if (need_host == 1 && !*host)
344  host = "localhost";
345 
346  printf("<TR><TH ALIGN=\"left\">Cache Host:</TH><TD><INPUT NAME=\"host\" ");
347 
348  printf("size=\"30\" VALUE=\"%s\"></TD></TR>\n", host);
349 
350  printf("<TR><TH ALIGN=\"left\">Cache Port:</TH><TD><INPUT NAME=\"port\" ");
351 
352  printf("size=\"30\" VALUE=\"%d\"></TD></TR>\n", port);
353  }
354 
355  printf("<TR><TH ALIGN=\"left\">Manager name:</TH><TD><INPUT NAME=\"user_name\" ");
356 
357  printf("size=\"30\" VALUE=\"%s\"></TD></TR>\n", user_name);
358 
359  printf("<TR><TH ALIGN=\"left\">Password:</TH><TD><INPUT TYPE=\"password\" NAME=\"passwd\" ");
360 
361  printf("size=\"30\" VALUE=\"\"></TD></TR>\n");
362 
363  printf("</TABLE><BR CLEAR=\"all\">\n");
364 
365  printf("<INPUT TYPE=\"submit\" VALUE=\"Continue...\">\n");
366 
367  printf("</FORM></div>\n");
368 
369  printf("<script type=\"text/javascript\">\n");
370  printf("var s = document.getElementById(\"server\");\n");
371  printf("for (var i = 0; i < s.childElementCount; i++) {\n");
372  printf(" TS(s.children[i].value, '');\n");
373  printf(" TS(s.children[i].value, 's');\n");
374  printf("}</script>\n");
375 
376  print_trailer();
377 }
378 
379 static void
380 error_html(const char *msg)
381 {
382  printf("Content-Type: text/html\r\n\r\n");
383  printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
384  printf("<HTML><HEAD><TITLE>Cache Manager Error</TITLE>\n");
385  printf("<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE></HEAD>\n");
386  printf("<BODY><H1>Cache Manager Error</H1>\n");
387  printf("<P>\n%s</P>\n", html_quote(msg));
388  print_trailer();
389 }
390 
391 /* returns http status extracted from status line or -1 on parsing failure */
392 static int
393 parse_status_line(const char *sline, const char **statusStr)
394 {
395  const char *sp = strchr(sline, ' ');
396 
397  if (statusStr)
398  *statusStr = NULL;
399 
400  if (strncasecmp(sline, "HTTP/", 5) || !sp)
401  return -1;
402 
403  while (xisspace(*++sp));
404  if (!xisdigit(*sp))
405  return -1;
406 
407  if (statusStr)
408  *statusStr = sp;
409 
410  return atoi(sp);
411 }
412 
413 static char *
414 menu_url(cachemgr_request * req, const char *action)
415 {
416  static char url[1024];
417  snprintf(url, sizeof(url), "%s?host=%s&port=%d&user_name=%s&operation=%s&auth=%s",
418  script_name,
419  req->hostname,
420  req->port,
421  safe_str(req->user_name),
422  action,
423  safe_str(req->pub_auth));
424  return url;
425 }
426 
427 static void
428 munge_menu_line(MemBuf &out, const char *buf, cachemgr_request * req)
429 {
430  char *x;
431  const char *a;
432  const char *d;
433  const char *p;
434  char *a_url;
435  char *buf_copy;
436 
437  const char bufLen = strlen(buf);
438  if (bufLen < 1 || *buf != ' ') {
439  out.append(buf, bufLen);
440  return;
441  }
442 
443  buf_copy = x = xstrndup(buf, bufLen+1);
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  out.appendf("<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  out.appendf("<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  out.appendf("<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  out.appendf("<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  out.appendf("<LI type=\"disk\"><A HREF=\"%s\">%s</A>\n", a_url, d);
474 
475  xfree(a_url);
476 
477  xfree(buf_copy);
478 }
479 
480 static void
481 munge_other_line(MemBuf &out, const char *buf, cachemgr_request *)
482 {
483  static const char *ttags[] = {"td", "th"};
484 
485  static int table_line_num = 0;
486  static int next_is_header = 0;
487  int is_header = 0;
488  const char *ttag;
489  char *buf_copy;
490  char *x, *p;
491  /* does it look like a table? */
492 
493  if (!strchr(buf, '\t') || *buf == '\t') {
494  /* nope, just text */
495  if (table_line_num)
496  out.append("</table>\n<pre>", 14);
497  out.appendf("%s", html_quote(buf));
498  table_line_num = 0;
499  return;
500  }
501 
502  /* start html table */
503  if (!table_line_num) {
504  out.append("</pre><table cellpadding=\"2\" cellspacing=\"1\">\n", 46);
505  next_is_header = 0;
506  }
507 
508  /* remove '\n' */
509  is_header = (!table_line_num || next_is_header) && !strchr(buf, ':') && !is_number(buf);
510 
511  ttag = ttags[is_header];
512 
513  /* record starts */
514  out.append("<tr>", 4);
515 
516  /* substitute '\t' */
517  buf_copy = x = xstrdup(buf);
518 
519  if ((p = strchr(x, '\n')))
520  *p = '\0';
521 
522  while (x && strlen(x)) {
523  int column_span = 1;
524  const char *cell = xstrtok(&x, '\t');
525 
526  while (x && *x == '\t') {
527  ++column_span;
528  ++x;
529  }
530 
531  out.appendf("<%s colspan=\"%d\" align=\"%s\">%s</%s>",
532  ttag, column_span,
533  is_header ? "center" : is_number(cell) ? "right" : "left",
534  html_quote(cell), ttag);
535  }
536 
537  xfree(buf_copy);
538  /* record ends */
539  out.append("</tr>\n", 6);
540  next_is_header = is_header && strstr(buf, "\t\t");
541  ++table_line_num;
542 }
543 
544 static const char *
545 munge_action_line(const char *_buf, cachemgr_request * req)
546 {
547  static char html[2 * 1024];
548  char *buf = xstrdup(_buf);
549  char *x = buf;
550  const char *action, *description;
551  char *p;
552 
553  if ((p = strchr(x, '\n')))
554  *p = '\0';
555  action = xstrtok(&x, '\t');
556  if (!action) {
557  xfree(buf);
558  return "";
559  }
560  description = xstrtok(&x, '\t');
561  if (!description)
562  description = action;
563  snprintf(html, sizeof(html), " <a href=\"%s\">%s</a>", menu_url(req, action), description);
564  xfree(buf);
565  return html;
566 }
567 
568 static int
570 {
571  char buf[4 * 1024];
572 #if _SQUID_WINDOWS_
573 
574  int reply;
575  char *tmpfile = tempnam(NULL, "tmp0000");
576  FILE *fp = fopen(tmpfile, "w+");
577 #else
578 
579  FILE *fp = fdopen(s, "r");
580 #endif
581  /* interpretation states */
582  enum {
583  isStatusLine, isHeaders, isActions, isBodyStart, isBody, isForward, isEof, isForwardEof, isSuccess, isError
584  } istate = isStatusLine;
585  int parse_menu = 0;
586  const char *action = req->action;
587  const char *statusStr = NULL;
588  int status = -1;
589 
590  if (0 == strlen(req->action))
591  parse_menu = 1;
592  else if (0 == strcasecmp(req->action, "menu"))
593  parse_menu = 1;
594 
595  if (fp == NULL) {
596 #if _SQUID_WINDOWS_
597  perror(tmpfile);
598  xfree(tmpfile);
599 #else
600 
601  perror("fdopen");
602 #endif
603 
604  close(s);
605  return 1;
606  }
607 
608 #if _SQUID_WINDOWS_
609 
610  while ((reply=recv(s, buf, sizeof(buf), 0)) > 0)
611  fwrite(buf, 1, reply, fp);
612 
613  rewind(fp);
614 
615 #endif
616 
617  if (parse_menu)
618  action = "menu";
619 
620  /* read reply interpreting one line at a time depending on state */
621  while (istate < isEof) {
622  if (!fgets(buf, sizeof(buf), fp))
623  istate = istate == isForward ? isForwardEof : isEof;
624 
625  switch (istate) {
626 
627  case isStatusLine:
628  /* get HTTP status */
629  /* uncomment the following if you want to debug headers */
630  /* fputs("\r\n\r\n", stdout); */
631  status = parse_status_line(buf, &statusStr);
632  istate = status == 200 ? isHeaders : isForward;
633  /* if cache asks for authentication, we have to reset our info */
634 
635  if (status == 401 || status == 407) {
636  reset_auth(req);
637  status = 403; /* Forbiden, see comments in case isForward: */
638  }
639 
640  /* this is a way to pass HTTP status to the Web server */
641  if (statusStr)
642  printf("Status: %d %s", status, statusStr); /* statusStr has '\n' */
643 
644  break;
645 
646  case isHeaders:
647  /* forward header field */
648  if (!strcmp(buf, "\r\n")) { /* end of headers */
649  fputs("Content-Type: text/html\r\n", stdout); /* add our type */
650  istate = isBodyStart;
651  }
652 
653  if (strncasecmp(buf, "Content-Type:", 13)) /* filter out their type */
654  fputs(buf, stdout);
655 
656  break;
657 
658  case isBodyStart:
659  printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
660 
661  printf("<HTML><HEAD><TITLE>CacheMgr@%s: %s</TITLE>\n",
662  req->hostname, action);
663 
664  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");
665 
666  printf("</HEAD><BODY>\n");
667 
668  if (parse_menu) {
669  printf("<H2><a href=\"%s\">Cache Manager</a> menu for %s:</H2>",
670  menu_url(req, "authenticate"), req->hostname);
671  printf("<UL>\n");
672  } else {
673  printf("<P><A HREF=\"%s\">%s</A>\n<HR noshade size=\"1px\">\n",
674  menu_url(req, "menu"), "Cache Manager menu");
675  printf("<PRE>\n");
676  }
677 
678  istate = isActions;
679  /* yes, fall through, we do not want to loose the first line */
680 
681  case isActions:
682  if (strncmp(buf, "action:", 7) == 0) {
683  fputs(" ", stdout);
684  fputs(munge_action_line(buf + 7, req), stdout);
685  break;
686  }
687  if (parse_menu) {
688  printf("<UL>\n");
689  } else {
690  printf("<HR noshade size=\"1px\">\n");
691  printf("<PRE>\n");
692  }
693 
694  istate = isBody;
695  /* yes, fall through, we do not want to loose the first line */
696 
697  case isBody:
698  {
699  /* interpret [and reformat] cache response */
700  MemBuf out;
701  out.init();
702  if (parse_menu)
703  munge_menu_line(out, buf, req);
704  else
705  munge_other_line(out, buf, req);
706 
707  fputs(out.buf, 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 
762 static int
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) {
776  return 1;
777  }
778 
779  if (req->hostname == NULL) {
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  int xerrno = errno;
824  snprintf(buf, sizeof(buf), "socket: %s\n", xstrerr(xerrno));
825  error_html(buf);
827  return 1;
828  }
829 
830  if (connect(s, AI->ai_addr, AI->ai_addrlen) < 0) {
831  int xerrno = errno;
832  snprintf(buf, sizeof(buf), "connect %s: %s\n", S.toUrl(ipbuf,MAX_IPSTRLEN), xstrerr(xerrno));
833  error_html(buf);
835  close(s);
836  return 1;
837  }
838 
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 
861 int
862 main(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 
919 static char *
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 
965 static char *
967 {
968  char *s;
969 
970  if ((s = getenv("QUERY_STRING")) == NULL)
971  return NULL;
972 
973  return xstrdup(s);
974 }
975 
976 static cachemgr_request *
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  */
1062 static void
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  struct base64_encode_ctx ctx;
1083  base64_encode_init(&ctx);
1084  size_t blen = base64_encode_update(&ctx, reinterpret_cast<uint8_t*>(req->pub_auth), bufLen, reinterpret_cast<uint8_t*>(buf));
1085  blen += base64_encode_final(&ctx, reinterpret_cast<uint8_t*>(req->pub_auth)+blen);
1086  req->pub_auth[blen] = '\0';
1087  debug("cmgr: encoded: '%s'\n", req->pub_auth);
1088 }
1089 
1090 static void
1092 {
1093  char *buf;
1094  const char *host_name;
1095  const char *time_str;
1096  const char *user_name;
1097  const char *passwd;
1098 
1099  debug("cmgr: decoding pub: '%s'\n", safe_str(req->pub_auth));
1100  safe_free(req->passwd);
1101 
1102  if (!req->pub_auth || strlen(req->pub_auth) < 4 + strlen(safe_str(req->hostname)))
1103  return;
1104 
1105  size_t decodedLen = BASE64_DECODE_LENGTH(strlen(req->pub_auth));
1106  buf = (char*)xmalloc(decodedLen);
1107  struct base64_decode_ctx ctx;
1108  base64_decode_init(&ctx);
1109  if (!base64_decode_update(&ctx, &decodedLen, reinterpret_cast<uint8_t*>(buf), strlen(req->pub_auth), reinterpret_cast<const uint8_t*>(req->pub_auth)) ||
1110  !base64_decode_final(&ctx)) {
1111  debug("cmgr: base64 decode failure. Incomplete auth token string.\n");
1112  xfree(buf);
1113  return;
1114  }
1115 
1116  debug("cmgr: length ok\n");
1117 
1118  /* parse ( a lot of memory leaks, but that is cachemgr style :) */
1119  if ((host_name = strtok(buf, "|")) == NULL) {
1120  xfree(buf);
1121  return;
1122  }
1123 
1124  debug("cmgr: decoded host: '%s'\n", host_name);
1125 
1126  if ((time_str = strtok(NULL, "|")) == NULL) {
1127  xfree(buf);
1128  return;
1129  }
1130 
1131  debug("cmgr: decoded time: '%s' (now: %d)\n", time_str, (int) now);
1132 
1133  if ((user_name = strtok(NULL, "|")) == NULL) {
1134  xfree(buf);
1135  return;
1136  }
1137 
1138  debug("cmgr: decoded uname: '%s'\n", user_name);
1139 
1140  if ((passwd = strtok(NULL, "|")) == NULL) {
1141  xfree(buf);
1142  return;
1143  }
1144 
1145  debug("cmgr: decoded passwd: '%s'\n", passwd);
1146 
1147  /* verify freshness and validity */
1148  if (atoi(time_str) + passwd_ttl < now) {
1149  xfree(buf);
1150  return;
1151  }
1152 
1153  if (strcasecmp(host_name, req->hostname)) {
1154  xfree(buf);
1155  return;
1156  }
1157 
1158  debug("cmgr: verified auth. info.\n");
1159 
1160  /* ok, accept */
1161  safe_free(req->user_name);
1162 
1163  req->user_name = xstrdup(user_name);
1164 
1165  req->passwd = xstrdup(passwd);
1166 
1167  xfree(buf);
1168 }
1169 
1170 static void
1172 {
1173  safe_free(req->passwd);
1174  safe_free(req->pub_auth);
1175 }
1176 
1177 static const char *
1179 {
1180  static char buf[1024];
1181  size_t stringLength = 0;
1182 
1183  if (!req->passwd)
1184  return "";
1185 
1186  int bufLen = snprintf(buf, sizeof(buf), "%s:%s",
1187  req->user_name ? req->user_name : "",
1188  req->passwd);
1189 
1190  int encodedLen = base64_encode_len(bufLen);
1191  if (encodedLen <= 0)
1192  return "";
1193 
1194  uint8_t *str64 = static_cast<uint8_t*>(xmalloc(encodedLen));
1195  struct base64_encode_ctx ctx;
1196  base64_encode_init(&ctx);
1197  size_t blen = base64_encode_update(&ctx, str64, bufLen, reinterpret_cast<uint8_t*>(buf));
1198  blen += base64_encode_final(&ctx, str64+blen);
1199  str64[blen] = '\0';
1200 
1201  stringLength += snprintf(buf, sizeof(buf), "Authorization: Basic %.*s\r\n", (int)blen, str64);
1202 
1203  assert(stringLength < sizeof(buf));
1204 
1205  snprintf(&buf[stringLength], sizeof(buf) - stringLength, "Proxy-Authorization: Basic %.*s\r\n", (int)blen, str64);
1206 
1207  xfree(str64);
1208  return buf;
1209 }
1210 
1211 static int
1212 check_target_acl(const char *hostname, int port)
1213 {
1214  char config_line[BUFSIZ];
1215  FILE *fp = NULL;
1216  int ret = 0;
1217  fp = fopen("cachemgr.conf", "r");
1218 
1219  if (fp == NULL)
1220  fp = fopen(DEFAULT_CACHEMGR_CONFIG, "r");
1221 
1222  if (fp == NULL) {
1223 #ifdef CACHEMGR_HOSTNAME_DEFINED
1224 
1225  if (strcmp(hostname, CACHEMGR_HOSTNAME) == 0 && port == CACHE_HTTP_PORT)
1226  return 1;
1227 
1228 #else
1229 
1230  if (strcmp(hostname, "localhost") == 0)
1231  return 1;
1232 
1233  if (strcmp(hostname, getfullhostname()) == 0)
1234  return 1;
1235 
1236 #endif
1237 
1238  return 0;
1239  }
1240 
1241  while (fgets(config_line, BUFSIZ, fp)) {
1242  char *token = NULL;
1243  strtok(config_line, " \r\n\t");
1244 
1245  if (config_line[0] == '#')
1246  continue;
1247 
1248  if (config_line[0] == '\0')
1249  continue;
1250 
1251  if ((token = strtok(config_line, ":")) == NULL)
1252  continue;
1253 
1254 #if HAVE_FNMATCH_H
1255 
1256  if (fnmatch(token, hostname, 0) != 0)
1257  continue;
1258 
1259 #else
1260 
1261  if (strcmp(token, hostname) != 0)
1262  continue;
1263 
1264 #endif
1265 
1266  if ((token = strtok(NULL, ":")) != NULL) {
1267  int i;
1268 
1269  if (strcmp(token, "*") == 0)
1270 
1271  ; /* Wildcard port specification */
1272  else if (strcmp(token, "any") == 0)
1273 
1274  ; /* Wildcard port specification */
1275  else if (sscanf(token, "%d", &i) != 1)
1276  continue;
1277 
1278  else if (i != port)
1279  continue;
1280  } else if (port != CACHE_HTTP_PORT)
1281  continue;
1282 
1283  ret = 1;
1284 
1285  break;
1286  }
1287 
1288  fclose(fp);
1289  return ret;
1290 }
1291 
static void decode_pub_auth(cachemgr_request *req)
Definition: cachemgr.cc:1091
#define assert(EX)
Definition: assert.h:17
static char * read_post_request(void)
Definition: cachemgr.cc:920
int main(int argc, char *argv[])
Definition: cachemgr.cc:862
const char * mkrfc1123(time_t)
Definition: rfc1123.c:202
virtual void append(const char *c, int sz)
Definition: MemBuf.cc:225
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:694
static int check_target_acl(const char *hostname, int port)
Definition: cachemgr.cc:1212
#define xcalloc
Definition: membanger.c:57
int debug_enabled
Definition: debug.cc:13
int i
Definition: membanger.c:49
#define xstrdup
static int read_reply(int s, cachemgr_request *req)
Definition: cachemgr.cc:569
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:58
#define xisspace(x)
Definition: xis.h:17
char * p
Definition: membanger.c:43
static void reset_auth(cachemgr_request *req)
Definition: cachemgr.cc:1171
static void munge_other_line(MemBuf &out, const char *buf, cachemgr_request *)
Definition: cachemgr.cc:481
static const char * munge_action_line(const char *_buf, cachemgr_request *req)
Definition: cachemgr.cc:545
#define BASE64_DECODE_LENGTH(length)
Definition: base64.h:25
bool isAnyAddr() const
Definition: Address.cc:163
static void error_html(const char *msg)
Definition: cachemgr.cc:380
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:105
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:428
size_t base64_encode_final(struct base64_encode_ctx *ctx, uint8_t *dst)
DST should point to an area of size at least BASE64_ENCODE_FINAL_LENGTH.
Definition: base64.c:253
char * hostname
Definition: cachemgr.cc:92
static time_t now
Definition: cachemgr.cc:108
static const char * progname
Definition: cachemgr.cc:107
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:892
static int parse_status_line(const char *sline, const char **statusStr)
Definition: cachemgr.cc:393
static void make_pub_auth(cachemgr_request *req)
Definition: cachemgr.cc:1063
void base64_encode_init(struct base64_encode_ctx *ctx)
Definition: base64.c:182
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:966
#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:786
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:977
char * buf
Definition: MemBuf.h:134
static char * menu_url(cachemgr_request *req, const char *action)
Definition: cachemgr.cc:414
unsigned short WORD
Definition: smblib-priv.h:145
struct _sp sp
#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
static const char * make_auth_header(const cachemgr_request *req)
Definition: cachemgr.cc:1178
#define VERSION
static int process_request(cachemgr_request *req)
Definition: cachemgr.cc:763
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 uint8_t *src)
Definition: base64.c:107
int64_t strtoll(const char *nptr, char **endptr, int base)
Definition: strtoll.c:81
size_t base64_encode_update(struct base64_encode_ctx *ctx, uint8_t *dst, size_t length, const uint8_t *src)
Definition: base64.c:213
#define CACHEMGR_HOSTNAME
Definition: squid.h:46
#define xfree
int base64_decode_final(struct base64_decode_ctx *ctx)
Definition: base64.c:137
#define NULL
Definition: types.h:166
char * tempnam(const char *dir, const char *pfx)
Definition: tempnam.c:119
char * processes
Definition: cachemgr.cc:99
void getAddrInfo(struct addrinfo *&ai, int force=AF_UNSPEC) const
Definition: Address.cc:607
#define base64_encode_len(length)
Definition: base64.h:93

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors