Inventory: Protect Craft and Drop actions (#10353)

Change dangerous pointer to unique_ptr for automated deletion.
This commit is contained in:
SmallJoker 2020-09-07 21:19:38 +02:00 committed by GitHub
parent 6dcc9e6331
commit 0d128ab344
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -600,7 +600,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
<< std::endl; << std::endl;
std::istringstream is(datastring, std::ios_base::binary); std::istringstream is(datastring, std::ios_base::binary);
// Create an action // Create an action
InventoryAction *a = InventoryAction::deSerialize(is); std::unique_ptr<InventoryAction> a(InventoryAction::deSerialize(is));
if (!a) { if (!a) {
infostream << "TOSERVER_INVENTORY_ACTION: " infostream << "TOSERVER_INVENTORY_ACTION: "
<< "InventoryAction::deSerialize() returned NULL" << "InventoryAction::deSerialize() returned NULL"
@ -617,11 +617,30 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
where the client made a bad prediction. where the client made a bad prediction.
*/ */
const bool player_has_interact = checkPriv(player->getName(), "interact");
auto check_inv_access = [player, player_has_interact] (
const InventoryLocation &loc) -> bool {
if (loc.type == InventoryLocation::CURRENT_PLAYER)
return false; // Only used internally on the client, never sent
if (loc.type == InventoryLocation::PLAYER) {
// Allow access to own inventory in all cases
return loc.name == player->getName();
}
if (!player_has_interact) {
infostream << "Cannot modify foreign inventory: "
<< "No interact privilege" << std::endl;
return false;
}
return true;
};
/* /*
Handle restrictions and special cases of the move action Handle restrictions and special cases of the move action
*/ */
if (a->getType() == IAction::Move) { if (a->getType() == IAction::Move) {
IMoveAction *ma = (IMoveAction*)a; IMoveAction *ma = (IMoveAction*)a.get();
ma->from_inv.applyCurrentPlayer(player->getName()); ma->from_inv.applyCurrentPlayer(player->getName());
ma->to_inv.applyCurrentPlayer(player->getName()); ma->to_inv.applyCurrentPlayer(player->getName());
@ -630,21 +649,11 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
if (ma->from_inv != ma->to_inv) if (ma->from_inv != ma->to_inv)
m_inventory_mgr->setInventoryModified(ma->to_inv); m_inventory_mgr->setInventoryModified(ma->to_inv);
bool from_inv_is_current_player = false; if (!check_inv_access(ma->from_inv) ||
if (ma->from_inv.type == InventoryLocation::PLAYER) { !check_inv_access(ma->to_inv))
if (ma->from_inv.name != player->getName()) return;
return;
from_inv_is_current_player = true;
}
bool to_inv_is_current_player = false; InventoryLocation *remote = ma->from_inv.type == InventoryLocation::PLAYER ?
if (ma->to_inv.type == InventoryLocation::PLAYER) {
if (ma->to_inv.name != player->getName())
return;
to_inv_is_current_player = true;
}
InventoryLocation *remote = from_inv_is_current_player ?
&ma->to_inv : &ma->from_inv; &ma->to_inv : &ma->from_inv;
// Check for out-of-range interaction // Check for out-of-range interaction
@ -664,7 +673,6 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
<< (ma->from_inv.dump()) << ":" << ma->from_list << (ma->from_inv.dump()) << ":" << ma->from_list
<< " to " << (ma->to_inv.dump()) << ":" << ma->to_list << " to " << (ma->to_inv.dump()) << ":" << ma->to_list
<< " because src is " << ma->from_list << std::endl; << " because src is " << ma->from_list << std::endl;
delete a;
return; return;
} }
@ -676,18 +684,6 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
<< (ma->from_inv.dump()) << ":" << ma->from_list << (ma->from_inv.dump()) << ":" << ma->from_list
<< " to " << (ma->to_inv.dump()) << ":" << ma->to_list << " to " << (ma->to_inv.dump()) << ":" << ma->to_list
<< " because dst is " << ma->to_list << std::endl; << " because dst is " << ma->to_list << std::endl;
delete a;
return;
}
// Disallow moving items in elsewhere than player's inventory
// if not allowed to interact
if (!checkPriv(player->getName(), "interact") &&
(!from_inv_is_current_player ||
!to_inv_is_current_player)) {
infostream << "Cannot move outside of player's inventory: "
<< "No interact privilege" << std::endl;
delete a;
return; return;
} }
} }
@ -695,7 +691,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
Handle restrictions and special cases of the drop action Handle restrictions and special cases of the drop action
*/ */
else if (a->getType() == IAction::Drop) { else if (a->getType() == IAction::Drop) {
IDropAction *da = (IDropAction*)a; IDropAction *da = (IDropAction*)a.get();
da->from_inv.applyCurrentPlayer(player->getName()); da->from_inv.applyCurrentPlayer(player->getName());
@ -708,22 +704,18 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
infostream << "Ignoring IDropAction from " infostream << "Ignoring IDropAction from "
<< (da->from_inv.dump()) << ":" << da->from_list << (da->from_inv.dump()) << ":" << da->from_list
<< " because src is " << da->from_list << std::endl; << " because src is " << da->from_list << std::endl;
delete a;
return; return;
} }
// Disallow dropping items if not allowed to interact // Disallow dropping items if not allowed to interact
if (!checkPriv(player->getName(), "interact")) { if (!player_has_interact || !check_inv_access(da->from_inv))
delete a;
return; return;
}
// Disallow dropping items if dead // Disallow dropping items if dead
if (playersao->isDead()) { if (playersao->isDead()) {
infostream << "Ignoring IDropAction from " infostream << "Ignoring IDropAction from "
<< (da->from_inv.dump()) << ":" << da->from_list << (da->from_inv.dump()) << ":" << da->from_list
<< " because player is dead." << std::endl; << " because player is dead." << std::endl;
delete a;
return; return;
} }
} }
@ -731,29 +723,28 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
Handle restrictions and special cases of the craft action Handle restrictions and special cases of the craft action
*/ */
else if (a->getType() == IAction::Craft) { else if (a->getType() == IAction::Craft) {
ICraftAction *ca = (ICraftAction*)a; ICraftAction *ca = (ICraftAction*)a.get();
ca->craft_inv.applyCurrentPlayer(player->getName()); ca->craft_inv.applyCurrentPlayer(player->getName());
m_inventory_mgr->setInventoryModified(ca->craft_inv); m_inventory_mgr->setInventoryModified(ca->craft_inv);
//bool craft_inv_is_current_player =
// (ca->craft_inv.type == InventoryLocation::PLAYER) &&
// (ca->craft_inv.name == player->getName());
// Disallow crafting if not allowed to interact // Disallow crafting if not allowed to interact
if (!checkPriv(player->getName(), "interact")) { if (!player_has_interact) {
infostream << "Cannot craft: " infostream << "Cannot craft: "
<< "No interact privilege" << std::endl; << "No interact privilege" << std::endl;
delete a;
return; return;
} }
if (!check_inv_access(ca->craft_inv))
return;
} else {
// Unknown action. Ignored.
return;
} }
// Do the action // Do the action
a->apply(m_inventory_mgr.get(), playersao, this); a->apply(m_inventory_mgr.get(), playersao, this);
// Eat the action
delete a;
} }
void Server::handleCommand_ChatMessage(NetworkPacket* pkt) void Server::handleCommand_ChatMessage(NetworkPacket* pkt)