]> git.ipfire.org Git - thirdparty/squid.git/blob - src/cache_manager.cc
Merged from trunk
[thirdparty/squid.git] / src / cache_manager.cc
1
2 /*
3 * $Id$
4 *
5 * DEBUG: section 16 Cache Manager Objects
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 #include "config.h"
37 #include "base/TextException.h"
38 #include "CacheManager.h"
39 #include "comm/Connection.h"
40 #include "Debug.h"
41 #include "errorpage.h"
42 #include "fde.h"
43 #include "HttpReply.h"
44 #include "HttpRequest.h"
45 #include "mgr/ActionCreator.h"
46 #include "mgr/Action.h"
47 #include "mgr/ActionProfile.h"
48 #include "mgr/BasicActions.h"
49 #include "mgr/Command.h"
50 #include "mgr/Forwarder.h"
51 #include "mgr/FunAction.h"
52 #include "mgr/QueryParams.h"
53 #include "protos.h" /* rotate_logs() */
54 #include "SquidTime.h"
55 #include "Store.h"
56 #include "wordlist.h"
57 #include <algorithm>
58
59
60 /// \ingroup CacheManagerInternal
61 #define MGR_PASSWD_SZ 128
62
63 /// creates Action using supplied Action::Create method and command
64 class ClassActionCreator: public Mgr::ActionCreator
65 {
66 public:
67 typedef Mgr::Action::Pointer Handler(const Mgr::Command::Pointer &cmd);
68
69 public:
70 ClassActionCreator(Handler *aHandler): handler(aHandler) {}
71
72 virtual Mgr::Action::Pointer create(const Mgr::Command::Pointer &cmd) const {
73 return handler(cmd);
74 }
75
76 private:
77 Handler *handler;
78 };
79
80
81 /// Registers new profiles, ignoring attempts to register a duplicate
82 void
83 CacheManager::registerProfile(const Mgr::ActionProfile::Pointer &profile)
84 {
85 Must(profile != NULL);
86 if (std::find(menu_.begin(), menu_.end(), profile) == menu_.end()) {
87 menu_.push_back(profile);
88 debugs(16, 3, HERE << "registered profile: " << *profile);
89 } else {
90 debugs(16, 2, HERE << "skipped duplicate profile: " << *profile);
91 }
92 }
93
94 /**
95 \ingroup CacheManagerAPI
96 * Registers a C-style action, which is implemented as a pointer to a function
97 * taking as argument a pointer to a StoreEntry and returning void.
98 * Implemented via CacheManagerActionLegacy.
99 */
100 void
101 CacheManager::registerProfile(char const * action, char const * desc, OBJH * handler, int pw_req_flag, int atomic)
102 {
103 debugs(16, 3, HERE << "registering legacy " << action);
104 const Mgr::ActionProfile::Pointer profile = new Mgr::ActionProfile(action,
105 desc, pw_req_flag, atomic, new Mgr::FunActionCreator(handler));
106 registerProfile(profile);
107 }
108
109 /**
110 * \ingroup CacheManagerAPI
111 * Registers a C++-style action, via a pointer to a subclass of
112 * a CacheManagerAction object, whose run() method will be invoked when
113 * CacheManager identifies that the user has requested the action.
114 */
115 void
116 CacheManager::registerProfile(char const * action, char const * desc,
117 ClassActionCreator::Handler *handler,
118 int pw_req_flag, int atomic)
119 {
120 const Mgr::ActionProfile::Pointer profile = new Mgr::ActionProfile(action,
121 desc, pw_req_flag, atomic, new ClassActionCreator(handler));
122 registerProfile(profile);
123 }
124
125 /**
126 \ingroup CacheManagerInternal
127 * Locates an action in the actions registry ActionsList.
128 \retval NULL if Action not found
129 \retval CacheManagerAction* if the action was found
130 */
131 Mgr::ActionProfile::Pointer
132 CacheManager::findAction(char const * action) const
133 {
134 Must(action != NULL);
135 Menu::const_iterator a;
136
137 debugs(16, 5, "CacheManager::findAction: looking for action " << action);
138 for (a = menu_.begin(); a != menu_.end(); ++a) {
139 if (0 == strcmp((*a)->name, action)) {
140 debugs(16, 6, " found");
141 return *a;
142 }
143 }
144
145 debugs(16, 6, "Action not found.");
146 return Mgr::ActionProfilePointer();
147 }
148
149 Mgr::Action::Pointer
150 CacheManager::createNamedAction(const char *actionName)
151 {
152 Must(actionName);
153
154 Mgr::Command::Pointer cmd = new Mgr::Command;
155 cmd->profile = findAction(actionName);
156 cmd->params.actionName = actionName;
157
158 Must(cmd->profile != NULL);
159 return cmd->profile->creator->create(cmd);
160 }
161
162 Mgr::Action::Pointer
163 CacheManager::createRequestedAction(const Mgr::ActionParams &params)
164 {
165 Mgr::Command::Pointer cmd = new Mgr::Command;
166 cmd->params = params;
167 cmd->profile = findAction(params.actionName.termedBuf());
168 Must(cmd->profile != NULL);
169 return cmd->profile->creator->create(cmd);
170 }
171
172 /**
173 \ingroup CacheManagerInternal
174 * define whether the URL is a cache-manager URL and parse the action
175 * requested by the user. Checks via CacheManager::ActionProtection() that the
176 * item is accessible by the user.
177 \retval CacheManager::cachemgrStateData state object for the following handling
178 \retval NULL if the action can't be found or can't be accessed by the user
179 */
180 Mgr::Command::Pointer
181 CacheManager::ParseUrl(const char *url)
182 {
183 int t;
184 LOCAL_ARRAY(char, host, MAX_URL);
185 LOCAL_ARRAY(char, request, MAX_URL);
186 LOCAL_ARRAY(char, password, MAX_URL);
187 LOCAL_ARRAY(char, params, MAX_URL);
188 host[0] = 0;
189 request[0] = 0;
190 password[0] = 0;
191 params[0] = 0;
192 int pos = -1;
193 int len = strlen(url);
194 Must(len > 0);
195 t = sscanf(url, "cache_object://%[^/]/%[^@?]%n@%[^?]?%s", host, request, &pos, password, params);
196
197 if (pos >0 && url[pos] == '?') {
198 ++pos;
199 if (pos < len)
200 xstrncpy(params, url + pos, sizeof(params));
201 }
202
203 if (t < 2)
204 xstrncpy(request, "menu", MAX_URL);
205
206 #ifdef _SQUID_OS2_
207 if (t == 2 && request[0] == '\0') {
208 /*
209 * emx's sscanf insists of returning 2 because it sets request
210 * to null
211 */
212 xstrncpy(request, "menu", MAX_URL);
213 }
214 #endif
215
216 Mgr::ActionProfile::Pointer profile = findAction(request);
217 if (!profile) {
218 debugs(16, DBG_IMPORTANT, "CacheManager::ParseUrl: action '" << request << "' not found");
219 return NULL;
220 }
221
222 const char *prot = ActionProtection(profile);
223 if (!strcmp(prot, "disabled") || !strcmp(prot, "hidden")) {
224 debugs(16, DBG_IMPORTANT, "CacheManager::ParseUrl: action '" << request << "' is " << prot);
225 return NULL;
226 }
227
228 Mgr::Command::Pointer cmd = new Mgr::Command;
229 if (!Mgr::QueryParams::Parse(params, cmd->params.queryParams))
230 return NULL;
231 cmd->profile = profile;
232 cmd->params.httpUri = url;
233 cmd->params.userName = String();
234 cmd->params.password = password;
235 cmd->params.actionName = request;
236 return cmd;
237 }
238
239 /// \ingroup CacheManagerInternal
240 /*
241 \ingroup CacheManagerInternal
242 * Decodes the headers needed to perform user authentication and fills
243 * the details into the cachemgrStateData argument
244 */
245 void
246 CacheManager::ParseHeaders(const HttpRequest * request, Mgr::ActionParams &params)
247 {
248 assert(request);
249
250 params.httpMethod = request->method.id();
251 params.httpFlags = request->flags;
252
253 #if HAVE_AUTH_MODULE_BASIC
254 // TODO: use the authentication system decode to retrieve these details properly.
255
256 /* base 64 _decoded_ user:passwd pair */
257 const char *basic_cookie = request->header.getAuth(HDR_AUTHORIZATION, "Basic");
258
259 if (!basic_cookie)
260 return;
261
262 const char *passwd_del;
263 if (!(passwd_del = strchr(basic_cookie, ':'))) {
264 debugs(16, DBG_IMPORTANT, "CacheManager::ParseHeaders: unknown basic_cookie format '" << basic_cookie << "'");
265 return;
266 }
267
268 /* found user:password pair, reset old values */
269 params.userName.limitInit(basic_cookie, passwd_del - basic_cookie);
270 params.password = passwd_del + 1;
271
272 /* warning: this prints decoded password which maybe not be what you want to do @?@ @?@ */
273 debugs(16, 9, "CacheManager::ParseHeaders: got user: '" <<
274 params.userName << "' passwd: '" << params.password << "'");
275 #endif
276 }
277
278 /**
279 \ingroup CacheManagerInternal
280 *
281 \retval 0 if mgr->password is good or "none"
282 \retval 1 if mgr->password is "disable"
283 \retval !0 if mgr->password does not match configured password
284 */
285 int
286 CacheManager::CheckPassword(const Mgr::Command &cmd)
287 {
288 assert(cmd.profile != NULL);
289 const char *action = cmd.profile->name;
290 char *pwd = PasswdGet(Config.passwd_list, action);
291
292 debugs(16, 4, "CacheManager::CheckPassword for action " << action);
293
294 if (pwd == NULL)
295 return cmd.profile->isPwReq;
296
297 if (strcmp(pwd, "disable") == 0)
298 return 1;
299
300 if (strcmp(pwd, "none") == 0)
301 return 0;
302
303 if (!cmd.params.password.size())
304 return 1;
305
306 return cmd.params.password != pwd;
307 }
308
309 /**
310 \ingroup CacheManagerAPI
311 * Main entry point in the Cache Manager's activity. Gets called as part
312 * of the forward chain if the right URL is detected there. Initiates
313 * all needed internal work and renders the response.
314 */
315 void
316 CacheManager::Start(const Comm::ConnectionPointer &client, HttpRequest * request, StoreEntry * entry)
317 {
318 ErrorState *err = NULL;
319 debugs(16, 3, "CacheManager::Start: '" << entry->url() << "'" );
320
321 Mgr::Command::Pointer cmd = ParseUrl(entry->url());
322 if (!cmd) {
323 err = errorCon(ERR_INVALID_URL, HTTP_NOT_FOUND, request);
324 err->url = xstrdup(entry->url());
325 errorAppendEntry(entry, err);
326 entry->expires = squid_curtime;
327 return;
328 }
329
330 const char *actionName = cmd->profile->name;
331
332 entry->expires = squid_curtime;
333
334 debugs(16, 5, "CacheManager: " << client << " requesting '" << actionName << "'");
335
336 /* get additional info from request headers */
337 ParseHeaders(request, cmd->params);
338
339 const char *userName = cmd->params.userName.size() ?
340 cmd->params.userName.termedBuf() : "unknown";
341
342 /* Check password */
343
344 if (CheckPassword(*cmd) != 0) {
345 /* build error message */
346 ErrorState *errState;
347 HttpReply *rep;
348 errState = errorCon(ERR_CACHE_MGR_ACCESS_DENIED, HTTP_UNAUTHORIZED, request);
349 /* warn if user specified incorrect password */
350
351 if (cmd->params.password.size()) {
352 debugs(16, DBG_IMPORTANT, "CacheManager: " <<
353 userName << "@" <<
354 client << ": incorrect password for '" <<
355 actionName << "'" );
356 } else {
357 debugs(16, DBG_IMPORTANT, "CacheManager: " <<
358 userName << "@" <<
359 client << ": password needed for '" <<
360 actionName << "'" );
361 }
362
363 rep = errState->BuildHttpReply();
364
365 errorStateFree(errState);
366
367 #if HAVE_AUTH_MODULE_BASIC
368 /*
369 * add Authenticate header using action name as a realm because
370 * password depends on the action
371 */
372 rep->header.putAuth("Basic", actionName);
373 #endif
374
375 /* store the reply */
376 entry->replaceHttpReply(rep);
377
378 entry->expires = squid_curtime;
379
380 entry->complete();
381
382 return;
383 }
384
385 debugs(16, 2, "CacheManager: " <<
386 userName << "@" <<
387 client << " requesting '" <<
388 actionName << "'" );
389
390 if (UsingSmp() && IamWorkerProcess()) {
391 // is client the right connection to pass here?
392 AsyncJob::Start(new Mgr::Forwarder(client, cmd->params, request, entry));
393 return;
394 }
395
396 Mgr::Action::Pointer action = cmd->profile->creator->create(cmd);
397 Must(action != NULL);
398 action->run(entry, true);
399 }
400
401 /*
402 \ingroup CacheManagerInternal
403 * Renders the protection level text for an action.
404 * Also doubles as a check for the protection level.
405 */
406 const char *
407 CacheManager::ActionProtection(const Mgr::ActionProfile::Pointer &profile)
408 {
409 assert(profile != NULL);
410 const char *pwd = PasswdGet(Config.passwd_list, profile->name);
411
412 if (!pwd)
413 return profile->isPwReq ? "hidden" : "public";
414
415 if (!strcmp(pwd, "disable"))
416 return "disabled";
417
418 if (strcmp(pwd, "none") == 0)
419 return "public";
420
421 return "protected";
422 }
423
424 /*
425 \ingroup CacheManagerInternal
426 * gets from the global Config the password the user would need to supply
427 * for the action she queried
428 */
429 char *
430 CacheManager::PasswdGet(cachemgr_passwd * a, const char *action)
431 {
432 wordlist *w;
433
434 while (a != NULL) {
435 for (w = a->actions; w != NULL; w = w->next) {
436 if (0 == strcmp(w->key, action))
437 return a->passwd;
438
439 if (0 == strcmp(w->key, "all"))
440 return a->passwd;
441 }
442
443 a = a->next;
444 }
445
446 return NULL;
447 }
448
449 CacheManager* CacheManager::instance=0;
450
451 /**
452 \ingroup CacheManagerAPI
453 * Singleton accessor method.
454 */
455 CacheManager*
456 CacheManager::GetInstance()
457 {
458 if (instance == 0) {
459 debugs(16, 6, "CacheManager::GetInstance: starting cachemanager up");
460 instance = new CacheManager;
461 Mgr::RegisterBasics();
462 }
463 return instance;
464 }