make server entity scripts respect script whitelist

This commit is contained in:
Stephen Birarda 2017-05-22 16:17:01 -07:00
parent bfb44693a3
commit cecbf43129
2 changed files with 71 additions and 24 deletions

View file

@ -957,6 +957,24 @@ void EntityTree::bumpTimestamp(EntityItemProperties& properties) { //fixme put c
properties.setLastEdited(properties.getLastEdited() + LAST_EDITED_SERVERSIDE_BUMP);
}
bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) {
// grab a URL representation of the entity script so we can check the host for this script
auto entityScriptURL = QUrl::fromUserInput(scriptProperty);
for (const auto& whiteListedPrefix : _entityScriptSourceWhitelist) {
auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix);
// check if this script URL matches the whitelist domain and, optionally, is beneath the path
if (entityScriptURL.host().compare(whiteListURL.host(), Qt::CaseInsensitive) == 0 &&
entityScriptURL.path().startsWith(whiteListURL.path(), Qt::CaseInsensitive)) {
return true;
}
}
return false;
}
int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength,
const SharedNodePointer& senderNode) {
@ -986,7 +1004,8 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
quint64 startFilter = 0, endFilter = 0;
quint64 startLogging = 0, endLogging = 0;
bool suppressDisallowedScript = false;
bool suppressDisallowedClientScript = false;
bool suppressDisallowedServerScript = false;
bool isPhysics = message.getType() == PacketType::EntityPhysics;
_totalEditMessages++;
@ -1011,36 +1030,57 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
}
}
if (validEditPacket && !_entityScriptSourceWhitelist.isEmpty() && !properties.getScript().isEmpty()) {
bool passedWhiteList = false;
if (validEditPacket && !_entityScriptSourceWhitelist.isEmpty()) {
// grab a URL representation of the entity script so we can check the host for this script
auto entityScriptURL = QUrl::fromUserInput(properties.getScript());
bool wasDeletedBecauseOfClientScript = false;
for (const auto& whiteListedPrefix : _entityScriptSourceWhitelist) {
auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix);
// check the client entity script to make sure its URL is in the whitelist
if (!properties.getScript().isEmpty()) {
bool clientScriptPassedWhitelist = isScriptInWhitelist(properties.getScript());
// check if this script URL matches the whitelist domain and, optionally, is beneath the path
if (entityScriptURL.host().compare(whiteListURL.host(), Qt::CaseInsensitive) == 0 &&
entityScriptURL.path().startsWith(whiteListURL.path(), Qt::CaseInsensitive)) {
passedWhiteList = true;
break;
if (!clientScriptPassedWhitelist) {
if (wantEditLogging()) {
qCDebug(entities) << "User [" << senderNode->getUUID()
<< "] attempting to set entity script not on whitelist, edit rejected";
}
// If this was an add, we also want to tell the client that sent this edit that the entity was not added.
if (isAdd) {
QWriteLocker locker(&_recentlyDeletedEntitiesLock);
_recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID);
validEditPacket = false;
wasDeletedBecauseOfClientScript = true;
} else {
suppressDisallowedClientScript = true;
}
}
}
if (!passedWhiteList) {
if (wantEditLogging()) {
qCDebug(entities) << "User [" << senderNode->getUUID() << "] attempting to set entity script not on whitelist, edit rejected";
}
// If this was an add, we also want to tell the client that sent this edit that the entity was not added.
if (isAdd) {
QWriteLocker locker(&_recentlyDeletedEntitiesLock);
_recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID);
validEditPacket = passedWhiteList;
} else {
suppressDisallowedScript = true;
// check all server entity scripts to make sure their URLs are in the whitelist
if (!properties.getServerScripts().isEmpty()) {
bool serverScriptPassedWhitelist = isScriptInWhitelist(properties.getServerScripts());
if (!serverScriptPassedWhitelist) {
if (wantEditLogging()) {
qCDebug(entities) << "User [" << senderNode->getUUID()
<< "] attempting to set server entity script not on whitelist, edit rejected";
}
// If this was an add, we also want to tell the client that sent this edit that the entity was not added.
if (isAdd) {
// Make sure we didn't already need to send back a delete because the client script failed
// the whitelist check
if (!wasDeletedBecauseOfClientScript) {
QWriteLocker locker(&_recentlyDeletedEntitiesLock);
_recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID);
validEditPacket = false;
}
} else {
suppressDisallowedServerScript = true;
}
}
}
}
if ((isAdd || properties.lifetimeChanged()) &&
@ -1075,11 +1115,16 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
if (existingEntity && !isAdd) {
if (suppressDisallowedScript) {
if (suppressDisallowedClientScript) {
bumpTimestamp(properties);
properties.setScript(existingEntity->getScript());
}
if (suppressDisallowedServerScript) {
bumpTimestamp(properties);
properties.setServerScripts(existingEntity->getServerScripts());
}
// if the EntityItem exists, then update it
startLogging = usecTimestampNow();
if (wantEditLogging()) {

View file

@ -303,6 +303,8 @@ protected:
void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode);
bool isScriptInWhitelist(const QString& scriptURL);
QReadWriteLock _newlyCreatedHooksLock;
QVector<NewlyCreatedEntityHook*> _newlyCreatedHooks;