]> git.ipfire.org Git - thirdparty/squid.git/blame - src/cache_manager.cc
Renamed http_hdr_type to Http::HdrType, fixed some HdrType-int implicit conversions
[thirdparty/squid.git] / src / cache_manager.cc
CommitLineData
22f3fd98 1/*
bde978a6 2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
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.
22f3fd98 7 */
8
bbc27441
AJ
9/* DEBUG: section 16 Cache Manager Objects */
10
f7f3304a 11#include "squid.h"
8822ebee 12#include "base/TextException.h"
62ee09ca 13#include "CacheManager.h"
5c336a3b 14#include "comm/Connection.h"
8822ebee 15#include "Debug.h"
aa839030 16#include "errorpage.h"
8822ebee 17#include "fde.h"
528b2c61 18#include "HttpReply.h"
19#include "HttpRequest.h"
8822ebee 20#include "mgr/Action.h"
602d9612
A
21#include "mgr/ActionCreator.h"
22#include "mgr/ActionPasswordList.h"
8822ebee
AR
23#include "mgr/ActionProfile.h"
24#include "mgr/BasicActions.h"
25#include "mgr/Command.h"
26#include "mgr/Forwarder.h"
27#include "mgr/FunAction.h"
b8151fa1 28#include "mgr/QueryParams.h"
592b8687 29#include "protos.h"
4d5904f7 30#include "SquidConfig.h"
985c86bc 31#include "SquidTime.h"
8822ebee 32#include "Store.h"
602d9612 33#include "tools.h"
d295d770 34#include "wordlist.h"
5bed43d6 35
8822ebee 36#include <algorithm>
22f3fd98 37
63be0a78 38/// \ingroup CacheManagerInternal
22f3fd98 39#define MGR_PASSWD_SZ 128
40
8822ebee
AR
41/// creates Action using supplied Action::Create method and command
42class ClassActionCreator: public Mgr::ActionCreator
43{
44public:
45 typedef Mgr::Action::Pointer Handler(const Mgr::Command::Pointer &cmd);
c83f0bd5 46
8822ebee
AR
47public:
48 ClassActionCreator(Handler *aHandler): handler(aHandler) {}
49
d9fc6862 50 virtual Mgr::Action::Pointer create(const Mgr::Command::Pointer &cmd) const {
8822ebee
AR
51 return handler(cmd);
52 }
53
54private:
55 Handler *handler;
56};
57
8822ebee
AR
58/// Registers new profiles, ignoring attempts to register a duplicate
59void
60CacheManager::registerProfile(const Mgr::ActionProfile::Pointer &profile)
62ee09ca 61{
8822ebee 62 Must(profile != NULL);
e3c2ea01 63 if (!CacheManager::findAction(profile->name)) {
8822ebee
AR
64 menu_.push_back(profile);
65 debugs(16, 3, HERE << "registered profile: " << *profile);
66 } else {
67 debugs(16, 2, HERE << "skipped duplicate profile: " << *profile);
68 }
62ee09ca 69}
22f3fd98 70
50847dca
AJ
71/**
72 \ingroup CacheManagerAPI
73 * Registers a C-style action, which is implemented as a pointer to a function
74 * taking as argument a pointer to a StoreEntry and returning void.
75 * Implemented via CacheManagerActionLegacy.
76 */
22f3fd98 77void
8822ebee 78CacheManager::registerProfile(char const * action, char const * desc, OBJH * handler, int pw_req_flag, int atomic)
22f3fd98 79{
8822ebee
AR
80 debugs(16, 3, HERE << "registering legacy " << action);
81 const Mgr::ActionProfile::Pointer profile = new Mgr::ActionProfile(action,
d9fc6862 82 desc, pw_req_flag, atomic, new Mgr::FunActionCreator(handler));
8822ebee 83 registerProfile(profile);
1d8395bd 84}
62e76326 85
d154d3ec 86/**
e0d28505 87 * \ingroup CacheManagerAPI
a750e510 88 * Registers a C++-style action, via a pointer to a subclass of
d154d3ec
FC
89 * a CacheManagerAction object, whose run() method will be invoked when
90 * CacheManager identifies that the user has requested the action.
91 */
1d8395bd 92void
8822ebee 93CacheManager::registerProfile(char const * action, char const * desc,
d9fc6862
A
94 ClassActionCreator::Handler *handler,
95 int pw_req_flag, int atomic)
1d8395bd 96{
8822ebee 97 const Mgr::ActionProfile::Pointer profile = new Mgr::ActionProfile(action,
d9fc6862 98 desc, pw_req_flag, atomic, new ClassActionCreator(handler));
8822ebee 99 registerProfile(profile);
62ee09ca 100}
101
d154d3ec
FC
102/**
103 \ingroup CacheManagerInternal
104 * Locates an action in the actions registry ActionsList.
105\retval NULL if Action not found
106\retval CacheManagerAction* if the action was found
107 */
8822ebee
AR
108Mgr::ActionProfile::Pointer
109CacheManager::findAction(char const * action) const
22f3fd98 110{
8822ebee
AR
111 Must(action != NULL);
112 Menu::const_iterator a;
03c4599f
K
113
114 debugs(16, 5, "CacheManager::findAction: looking for action " << action);
8822ebee
AR
115 for (a = menu_.begin(); a != menu_.end(); ++a) {
116 if (0 == strcmp((*a)->name, action)) {
03c4599f
K
117 debugs(16, 6, " found");
118 return *a;
119 }
22f3fd98 120 }
62e76326 121
03c4599f 122 debugs(16, 6, "Action not found.");
8822ebee
AR
123 return Mgr::ActionProfilePointer();
124}
125
126Mgr::Action::Pointer
127CacheManager::createNamedAction(const char *actionName)
128{
129 Must(actionName);
130
131 Mgr::Command::Pointer cmd = new Mgr::Command;
132 cmd->profile = findAction(actionName);
133 cmd->params.actionName = actionName;
134
135 Must(cmd->profile != NULL);
136 return cmd->profile->creator->create(cmd);
137}
138
139Mgr::Action::Pointer
140CacheManager::createRequestedAction(const Mgr::ActionParams &params)
141{
142 Mgr::Command::Pointer cmd = new Mgr::Command;
143 cmd->params = params;
144 cmd->profile = findAction(params.actionName.termedBuf());
145 Must(cmd->profile != NULL);
146 return cmd->profile->creator->create(cmd);
22f3fd98 147}
148
832c08ab
FC
149/**
150 \ingroup CacheManagerInternal
151 * define whether the URL is a cache-manager URL and parse the action
152 * requested by the user. Checks via CacheManager::ActionProtection() that the
153 * item is accessible by the user.
154 \retval CacheManager::cachemgrStateData state object for the following handling
155 \retval NULL if the action can't be found or can't be accessed by the user
156 */
8822ebee 157Mgr::Command::Pointer
c83f0bd5 158CacheManager::ParseUrl(const char *url)
22f3fd98 159{
160 int t;
161 LOCAL_ARRAY(char, host, MAX_URL);
162 LOCAL_ARRAY(char, request, MAX_URL);
163 LOCAL_ARRAY(char, password, MAX_URL);
b8151fa1
CT
164 LOCAL_ARRAY(char, params, MAX_URL);
165 host[0] = 0;
166 request[0] = 0;
167 password[0] = 0;
168 params[0] = 0;
169 int pos = -1;
170 int len = strlen(url);
171 Must(len > 0);
172 t = sscanf(url, "cache_object://%[^/]/%[^@?]%n@%[^?]?%s", host, request, &pos, password, params);
5366b99b
AJ
173 if (t < 3) {
174 t = sscanf(url, "cache_object://%[^/]/%[^?]%n?%s", host, request, &pos, params);
175 }
e37bd29b
AJ
176 if (t < 1) {
177 t = sscanf(url, "http://%[^/]/squid-internal-mgr/%[^?]%n?%s", host, request, &pos, params);
178 }
179 if (t < 1) {
180 t = sscanf(url, "https://%[^/]/squid-internal-mgr/%[^?]%n?%s", host, request, &pos, params);
181 }
f9c6f861
AJ
182 if (t < 2) {
183 if (strncmp("cache_object://",url,15)==0)
184 xstrncpy(request, "menu", MAX_URL);
185 else
186 xstrncpy(request, "index", MAX_URL);
187 }
8822ebee 188
1191b93b 189#if _SQUID_OS2_
8822ebee 190 if (t == 2 && request[0] == '\0') {
62e76326 191 /*
192 * emx's sscanf insists of returning 2 because it sets request
193 * to null
194 */
f9c6f861
AJ
195 if (strncmp("cache_object://",url,15)==0)
196 xstrncpy(request, "menu", MAX_URL);
197 else
198 xstrncpy(request, "index", MAX_URL);
8822ebee 199 }
cd377065 200#endif
62e76326 201
5366b99b
AJ
202 debugs(16, 3, HERE << "MGR request: t=" << t << ", host='" << host << "', request='" << request << "', pos=" << pos <<
203 ", password='" << password << "', params='" << params << "'");
204
8822ebee
AR
205 Mgr::ActionProfile::Pointer profile = findAction(request);
206 if (!profile) {
3e1da049 207 debugs(16, DBG_IMPORTANT, "CacheManager::ParseUrl: action '" << request << "' not found");
62e76326 208 return NULL;
22f3fd98 209 }
62e76326 210
8822ebee
AR
211 const char *prot = ActionProtection(profile);
212 if (!strcmp(prot, "disabled") || !strcmp(prot, "hidden")) {
213 debugs(16, DBG_IMPORTANT, "CacheManager::ParseUrl: action '" << request << "' is " << prot);
214 return NULL;
215 }
62e76326 216
8822ebee 217 Mgr::Command::Pointer cmd = new Mgr::Command;
b8151fa1
CT
218 if (!Mgr::QueryParams::Parse(params, cmd->params.queryParams))
219 return NULL;
8822ebee
AR
220 cmd->profile = profile;
221 cmd->params.httpUri = url;
222 cmd->params.userName = String();
b8151fa1 223 cmd->params.password = password;
8822ebee
AR
224 cmd->params.actionName = request;
225 return cmd;
22f3fd98 226}
227
63be0a78 228/// \ingroup CacheManagerInternal
832c08ab
FC
229/*
230 \ingroup CacheManagerInternal
231 * Decodes the headers needed to perform user authentication and fills
232 * the details into the cachemgrStateData argument
233 */
c83f0bd5 234void
8822ebee 235CacheManager::ParseHeaders(const HttpRequest * request, Mgr::ActionParams &params)
63259c34 236{
8822ebee
AR
237 assert(request);
238
239 params.httpMethod = request->method.id();
240 params.httpFlags = request->flags;
241
9da6b594
AJ
242#if HAVE_AUTH_MODULE_BASIC
243 // TODO: use the authentication system decode to retrieve these details properly.
244
245 /* base 64 _decoded_ user:passwd pair */
789217a2 246 const char *basic_cookie = request->header.getAuth(Http::HdrType::AUTHORIZATION, "Basic");
62e76326 247
99edd1c3 248 if (!basic_cookie)
62e76326 249 return;
250
9da6b594 251 const char *passwd_del;
63259c34 252 if (!(passwd_del = strchr(basic_cookie, ':'))) {
3e1da049 253 debugs(16, DBG_IMPORTANT, "CacheManager::ParseHeaders: unknown basic_cookie format '" << basic_cookie << "'");
62e76326 254 return;
63259c34 255 }
62e76326 256
63259c34 257 /* found user:password pair, reset old values */
8822ebee
AR
258 params.userName.limitInit(basic_cookie, passwd_del - basic_cookie);
259 params.password = passwd_del + 1;
62e76326 260
9da6b594 261 /* warning: this prints decoded password which maybe not be what you want to do @?@ @?@ */
8822ebee 262 debugs(16, 9, "CacheManager::ParseHeaders: got user: '" <<
d9fc6862 263 params.userName << "' passwd: '" << params.password << "'");
9da6b594 264#endif
63259c34 265}
266
63be0a78 267/**
268 \ingroup CacheManagerInternal
269 *
f53969cc
SM
270 \retval 0 if mgr->password is good or "none"
271 \retval 1 if mgr->password is "disable"
272 \retval !0 if mgr->password does not match configured password
22f3fd98 273 */
c83f0bd5 274int
8822ebee 275CacheManager::CheckPassword(const Mgr::Command &cmd)
22f3fd98 276{
8822ebee
AR
277 assert(cmd.profile != NULL);
278 const char *action = cmd.profile->name;
279 char *pwd = PasswdGet(Config.passwd_list, action);
03c4599f 280
8822ebee 281 debugs(16, 4, "CacheManager::CheckPassword for action " << action);
62e76326 282
22f3fd98 283 if (pwd == NULL)
8822ebee 284 return cmd.profile->isPwReq;
62e76326 285
22f3fd98 286 if (strcmp(pwd, "disable") == 0)
62e76326 287 return 1;
288
22f3fd98 289 if (strcmp(pwd, "none") == 0)
62e76326 290 return 0;
291
8822ebee 292 if (!cmd.params.password.size())
62e76326 293 return 1;
294
8822ebee 295 return cmd.params.password != pwd;
22f3fd98 296}
297
832c08ab
FC
298/**
299 \ingroup CacheManagerAPI
300 * Main entry point in the Cache Manager's activity. Gets called as part
301 * of the forward chain if the right URL is detected there. Initiates
302 * all needed internal work and renders the response.
303 */
22f3fd98 304void
5c336a3b 305CacheManager::Start(const Comm::ConnectionPointer &client, HttpRequest * request, StoreEntry * entry)
22f3fd98 306{
832c08ab 307 debugs(16, 3, "CacheManager::Start: '" << entry->url() << "'" );
62e76326 308
8822ebee
AR
309 Mgr::Command::Pointer cmd = ParseUrl(entry->url());
310 if (!cmd) {
955394ce 311 ErrorState *err = new ErrorState(ERR_INVALID_URL, Http::scNotFound, request);
3900307b 312 err->url = xstrdup(entry->url());
62e76326 313 errorAppendEntry(entry, err);
314 entry->expires = squid_curtime;
315 return;
22f3fd98 316 }
62e76326 317
8822ebee 318 const char *actionName = cmd->profile->name;
34266cde 319
22f3fd98 320 entry->expires = squid_curtime;
34266cde 321
a750e510 322 debugs(16, 5, "CacheManager: " << client << " requesting '" << actionName << "'");
34266cde 323
63259c34 324 /* get additional info from request headers */
8822ebee
AR
325 ParseHeaders(request, cmd->params);
326
327 const char *userName = cmd->params.userName.size() ?
d9fc6862 328 cmd->params.userName.termedBuf() : "unknown";
34266cde 329
22f3fd98 330 /* Check password */
62e76326 331
8822ebee 332 if (CheckPassword(*cmd) != 0) {
62e76326 333 /* build error message */
955394ce 334 ErrorState errState(ERR_CACHE_MGR_ACCESS_DENIED, Http::scUnauthorized, request);
62e76326 335 /* warn if user specified incorrect password */
336
8822ebee 337 if (cmd->params.password.size()) {
26ac0430 338 debugs(16, DBG_IMPORTANT, "CacheManager: " <<
8822ebee 339 userName << "@" <<
5c336a3b 340 client << ": incorrect password for '" <<
8822ebee
AR
341 actionName << "'" );
342 } else {
26ac0430 343 debugs(16, DBG_IMPORTANT, "CacheManager: " <<
8822ebee 344 userName << "@" <<
5c336a3b 345 client << ": password needed for '" <<
8822ebee
AR
346 actionName << "'" );
347 }
62e76326 348
913524f0 349 HttpReply *rep = errState.BuildHttpReply();
62e76326 350
9da6b594 351#if HAVE_AUTH_MODULE_BASIC
62e76326 352 /*
8822ebee
AR
353 * add Authenticate header using action name as a realm because
354 * password depends on the action
62e76326 355 */
8822ebee 356 rep->header.putAuth("Basic", actionName);
9da6b594 357#endif
3865965d 358 // Allow cachemgr and other XHR scripts access to our version string
789217a2
FC
359 if (request->header.has(Http::HdrType::ORIGIN)) {
360 rep->header.putExt("Access-Control-Allow-Origin",request->header.getStr(Http::HdrType::ORIGIN));
3865965d
AJ
361#if HAVE_AUTH_MODULE_BASIC
362 rep->header.putExt("Access-Control-Allow-Credentials","true");
363#endif
364 rep->header.putExt("Access-Control-Expose-Headers","Server");
365 }
62e76326 366
367 /* store the reply */
db237875 368 entry->replaceHttpReply(rep);
62e76326 369
370 entry->expires = squid_curtime;
371
372 entry->complete();
373
62e76326 374 return;
22f3fd98 375 }
62e76326 376
789217a2
FC
377 if (request->header.has(Http::HdrType::ORIGIN)) {
378 cmd->params.httpOrigin = request->header.getStr(Http::HdrType::ORIGIN);
3865965d
AJ
379 }
380
0be039f4 381 debugs(16, 2, "CacheManager: " <<
8822ebee 382 userName << "@" <<
5c336a3b 383 client << " requesting '" <<
8822ebee 384 actionName << "'" );
62e76326 385
b073fc4b
AJ
386 // special case: /squid-internal-mgr/ index page
387 if (!strcmp(cmd->profile->name, "index")) {
955394ce 388 ErrorState err(MGR_INDEX, Http::scOkay, request);
b073fc4b
AJ
389 err.url = xstrdup(entry->url());
390 HttpReply *rep = err.BuildHttpReply();
391 if (strncmp(rep->body.content(),"Internal Error:", 15) == 0)
9b769c67 392 rep->sline.set(Http::ProtocolVersion(1,1), Http::scNotFound);
b073fc4b 393 // Allow cachemgr and other XHR scripts access to our version string
789217a2
FC
394 if (request->header.has(Http::HdrType::ORIGIN)) {
395 rep->header.putExt("Access-Control-Allow-Origin",request->header.getStr(Http::HdrType::ORIGIN));
b073fc4b
AJ
396#if HAVE_AUTH_MODULE_BASIC
397 rep->header.putExt("Access-Control-Allow-Credentials","true");
398#endif
399 rep->header.putExt("Access-Control-Expose-Headers","Server");
400 }
401 entry->replaceHttpReply(rep);
402 entry->complete();
403 return;
404 }
405
8822ebee 406 if (UsingSmp() && IamWorkerProcess()) {
1b76e6c1 407 // is client the right connection to pass here?
25b481e6 408 AsyncJob::Start(new Mgr::Forwarder(client, cmd->params, request, entry));
8822ebee 409 return;
cb69b4c7 410 }
62e76326 411
8822ebee
AR
412 Mgr::Action::Pointer action = cmd->profile->creator->create(cmd);
413 Must(action != NULL);
414 action->run(entry, true);
22f3fd98 415}
416
832c08ab
FC
417/*
418 \ingroup CacheManagerInternal
419 * Renders the protection level text for an action.
420 * Also doubles as a check for the protection level.
832c08ab 421 */
c83f0bd5 422const char *
8822ebee 423CacheManager::ActionProtection(const Mgr::ActionProfile::Pointer &profile)
7395afb8 424{
8822ebee
AR
425 assert(profile != NULL);
426 const char *pwd = PasswdGet(Config.passwd_list, profile->name);
62e76326 427
7395afb8 428 if (!pwd)
8822ebee 429 return profile->isPwReq ? "hidden" : "public";
62e76326 430
7395afb8 431 if (!strcmp(pwd, "disable"))
62e76326 432 return "disabled";
433
7395afb8 434 if (strcmp(pwd, "none") == 0)
62e76326 435 return "public";
436
7395afb8 437 return "protected";
438}
439
832c08ab 440/*
ee82937c 441 * \ingroup CacheManagerInternal
832c08ab
FC
442 * gets from the global Config the password the user would need to supply
443 * for the action she queried
444 */
c83f0bd5 445char *
613924ee 446CacheManager::PasswdGet(Mgr::ActionPasswordList * a, const char *action)
22f3fd98 447{
448 wordlist *w;
62e76326 449
22f3fd98 450 while (a != NULL) {
62e76326 451 for (w = a->actions; w != NULL; w = w->next) {
452 if (0 == strcmp(w->key, action))
453 return a->passwd;
454
455 if (0 == strcmp(w->key, "all"))
456 return a->passwd;
457 }
458
459 a = a->next;
22f3fd98 460 }
62e76326 461
22f3fd98 462 return NULL;
463}
c83f0bd5
K
464
465CacheManager* CacheManager::instance=0;
466
832c08ab
FC
467/**
468 \ingroup CacheManagerAPI
469 * Singleton accessor method.
470 */
c83f0bd5 471CacheManager*
26ac0430
AJ
472CacheManager::GetInstance()
473{
474 if (instance == 0) {
475 debugs(16, 6, "CacheManager::GetInstance: starting cachemanager up");
476 instance = new CacheManager;
8822ebee 477 Mgr::RegisterBasics();
26ac0430
AJ
478 }
479 return instance;
c83f0bd5 480}
f53969cc 481