DEV-15182 VWR-5474 SEC-20: re-enabled support for clicking on the

following SLAPP URL types in an untrusted browser:

  secondlife:///app/agent/...
  secondlife:///app/group/...
  secondlife:///app/parcel/...

In order to find a compromise between supporting these commands and
security concerns over potential griefing vectors, we use a throttling
solution when these commands are issued by untrusted web browsers.
That is, we only process one command per 15 seconds.

This applies to external browsers, like Firefox, as well as the
internal SL browser.

Notably, we continue to block secondlife:///app/teleport URLs.

Reviewed by james.
master
Martin Reddy 2009-09-14 17:09:45 +00:00
parent 5f4764c785
commit cb926640b7
15 changed files with 60 additions and 41 deletions

View File

@ -674,7 +674,7 @@ class LLChatHandler : public LLCommandHandler
{
public:
// not allowed from outside the app
LLChatHandler() : LLCommandHandler("chat", true) { }
LLChatHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { }
// Your code here
bool handle(const LLSD& tokens, const LLSD& query_map,

View File

@ -38,12 +38,14 @@
// system includes
#include <boost/tokenizer.hpp>
#define THROTTLE_PERIOD 15 // required secs between throttled commands
//---------------------------------------------------------------------------
// Underlying registry for command handlers, not directly accessible.
//---------------------------------------------------------------------------
struct LLCommandHandlerInfo
{
bool mRequireTrustedBrowser;
LLCommandHandler::EUntrustedAccess mUntrustedBrowserAccess;
LLCommandHandler* mHandler; // safe, all of these are static objects
};
@ -51,7 +53,9 @@ class LLCommandHandlerRegistry
{
public:
static LLCommandHandlerRegistry& instance();
void add(const char* cmd, bool require_trusted_browser, LLCommandHandler* handler);
void add(const char* cmd,
LLCommandHandler::EUntrustedAccess untrusted_access,
LLCommandHandler* handler);
bool dispatch(const std::string& cmd,
const LLSD& params,
const LLSD& query_map,
@ -72,10 +76,12 @@ LLCommandHandlerRegistry& LLCommandHandlerRegistry::instance()
return instance;
}
void LLCommandHandlerRegistry::add(const char* cmd, bool require_trusted_browser, LLCommandHandler* handler)
void LLCommandHandlerRegistry::add(const char* cmd,
LLCommandHandler::EUntrustedAccess untrusted_access,
LLCommandHandler* handler)
{
LLCommandHandlerInfo info;
info.mRequireTrustedBrowser = require_trusted_browser;
info.mUntrustedBrowserAccess = untrusted_access;
info.mHandler = handler;
mMap[cmd] = info;
@ -87,15 +93,37 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd,
LLMediaCtrl* web,
bool trusted_browser)
{
static F64 last_throttle_time = 0.0;
F64 cur_time = 0.0;
std::map<std::string, LLCommandHandlerInfo>::iterator it = mMap.find(cmd);
if (it == mMap.end()) return false;
const LLCommandHandlerInfo& info = it->second;
if (!trusted_browser && info.mRequireTrustedBrowser)
if (!trusted_browser)
{
// block request from external browser, but report as
// "handled" because it was well formatted.
LL_WARNS_ONCE("SLURL") << "Blocked SLURL command from untrusted browser" << LL_ENDL;
return true;
switch (info.mUntrustedBrowserAccess)
{
case LLCommandHandler::UNTRUSTED_ALLOW:
// fall through and let the command be handled
break;
case LLCommandHandler::UNTRUSTED_BLOCK:
// block request from external browser, but report as
// "handled" because it was well formatted.
LL_WARNS_ONCE("SLURL") << "Blocked SLURL command from untrusted browser" << LL_ENDL;
return true;
case LLCommandHandler::UNTRUSTED_THROTTLE:
cur_time = LLTimer::getElapsedSeconds();
if (cur_time < last_throttle_time + THROTTLE_PERIOD)
{
// block request from external browser if it happened
// within THROTTLE_PERIOD secs of the last command
LL_WARNS_ONCE("SLURL") << "Throttled SLURL command from untrusted browser" << LL_ENDL;
return true;
}
last_throttle_time = cur_time;
break;
}
}
if (!info.mHandler) return false;
return info.mHandler->handle(params, query_map, web);
@ -106,10 +134,9 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd,
//---------------------------------------------------------------------------
LLCommandHandler::LLCommandHandler(const char* cmd,
bool require_trusted_browser)
EUntrustedAccess untrusted_access)
{
LLCommandHandlerRegistry::instance().add(
cmd, require_trusted_browser, this);
LLCommandHandlerRegistry::instance().add(cmd, untrusted_access, this);
}
LLCommandHandler::~LLCommandHandler()

View File

@ -43,7 +43,7 @@ public:
// Inform the system you handle commands starting
// with "foo" and they are only allowed from
// "trusted" (pointed at Linden content) browsers
LLFooHandler() : LLCommandHandler("foo", true) { }
LLFooHandler() : LLCommandHandler("foo", UNTRUSTED_BLOCK) { }
// Your code here
bool handle(const LLSD& tokens, const LLSD& query_map,
@ -65,7 +65,14 @@ class LLMediaCtrl;
class LLCommandHandler
{
public:
LLCommandHandler(const char* command, bool allow_from_untrusted_browser);
enum EUntrustedAccess
{
UNTRUSTED_ALLOW, // allow commands from untrusted browsers
UNTRUSTED_BLOCK, // ignore commands from untrusted browsers
UNTRUSTED_THROTTLE // allow untrusted, but only a few per min.
};
LLCommandHandler(const char* command, EUntrustedAccess untrusted_access);
// Automatically registers object to get called when
// command is executed. All commands can be processed
// in links from LLMediaCtrl, but some (like teleport)

View File

@ -38,7 +38,7 @@ class LLFloaterHandler
: public LLCommandHandler
{
public:
LLFloaterHandler() : LLCommandHandler("floater", true) { }
LLFloaterHandler() : LLCommandHandler("floater", UNTRUSTED_BLOCK) { }
bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web);
};

View File

@ -53,7 +53,7 @@ class LLParcelHandler : public LLCommandHandler
{
public:
// requires trusted browser to trigger
LLParcelHandler() : LLCommandHandler("parcel", true) { }
LLParcelHandler() : LLCommandHandler("parcel", UNTRUSTED_THROTTLE) { }
bool handle(const LLSD& params, const LLSD& query_map,
LLMediaCtrl* web)
{

View File

@ -51,7 +51,7 @@ class LLGroupHandler : public LLCommandHandler
{
public:
// requires trusted browser to trigger
LLGroupHandler() : LLCommandHandler("group", true) { }
LLGroupHandler() : LLCommandHandler("group", UNTRUSTED_THROTTLE) { }
bool handle(const LLSD& tokens, const LLSD& query_map,
LLMediaCtrl* web)
{

View File

@ -39,7 +39,7 @@ class LLLoginHandler : public LLCommandHandler
{
public:
// allow from external browsers
LLLoginHandler() : LLCommandHandler("login", false) { }
LLLoginHandler() : LLCommandHandler("login", UNTRUSTED_ALLOW) { }
/*virtual*/ bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web);
// Fill in our internal fields from a SLURL like

View File

@ -916,15 +916,8 @@ void LLMediaCtrl::onClickLinkHref( LLPluginClassMedia* self )
//
void LLMediaCtrl::onClickLinkNoFollow( LLPluginClassMedia* self )
{
// let the dispatcher handle blocking/throttling of SLURLs
std::string url = self->getClickURL();
if (LLSLURL::isSLURLCommand(url)
&& !mTrusted)
{
// block handling of this secondlife:///app/ URL
LLNotifications::instance().add("UnableToOpenCommandURL");
return;
}
LLURLDispatcher::dispatch(url, this, mTrusted);
}

View File

@ -616,7 +616,7 @@ class LLChatHandler : public LLCommandHandler
{
public:
// not allowed from outside the app
LLChatHandler() : LLCommandHandler("chat", true) { }
LLChatHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { }
// Your code here
bool handle(const LLSD& tokens, const LLSD& query_map,

View File

@ -119,7 +119,7 @@ class LLClassifiedTeleportHandler : public LLCommandHandler
{
public:
// don't allow from external browsers because it moves you immediately
LLClassifiedTeleportHandler() : LLCommandHandler("classifiedteleport", true) { }
LLClassifiedTeleportHandler() : LLCommandHandler("classifiedteleport", UNTRUSTED_BLOCK) { }
bool handle(const LLSD& tokens, const LLSD& queryMap)
{

View File

@ -87,7 +87,7 @@ class LLLoginRefreshHandler : public LLCommandHandler
{
public:
// don't allow from external browsers
LLLoginRefreshHandler() : LLCommandHandler("login_refresh", true) { }
LLLoginRefreshHandler() : LLCommandHandler("login_refresh", UNTRUSTED_BLOCK) { }
bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web)
{
if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP)

View File

@ -46,7 +46,7 @@ class LLAgentHandler : public LLCommandHandler
{
public:
// requires trusted browser to trigger
LLAgentHandler() : LLCommandHandler("agent", true) { }
LLAgentHandler() : LLCommandHandler("agent", UNTRUSTED_THROTTLE) { }
bool handle(const LLSD& params, const LLSD& query_map,
LLMediaCtrl* web)

View File

@ -623,7 +623,7 @@ class LLBalanceHandler : public LLCommandHandler
{
public:
// Requires "trusted" browser/URL source
LLBalanceHandler() : LLCommandHandler("balance", true) { }
LLBalanceHandler() : LLCommandHandler("balance", UNTRUSTED_BLOCK) { }
bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web)
{
if (tokens.size() == 1

View File

@ -331,7 +331,7 @@ public:
// Teleport requests *must* come from a trusted browser
// inside the app, otherwise a malicious web page could
// cause a constant teleport loop. JC
LLTeleportHandler() : LLCommandHandler("teleport", true) { }
LLTeleportHandler() : LLCommandHandler("teleport", UNTRUSTED_BLOCK) { }
bool handle(const LLSD& tokens, const LLSD& query_map,
LLMediaCtrl* web)

View File

@ -6493,14 +6493,6 @@ Click Accept to join the chat or Decline to decline the invitation. Click Block
You just entered a region using a different server version, which may affect performance. Click to see the release notes.
</notification>
<notification
icon="notifytip.tga"
name="UnableToOpenCommandURL"
priority="high"
type="notifytip">
The link you clicked cannot be opened from this web browser.
</notification>
<notification
icon="notifytip.tga"
name="UnsupportedCommandSLURL"