From 96446adfa4dd91194c6ce6383927cdece8bdda1f Mon Sep 17 00:00:00 2001 From: Melanie Date: Sat, 4 Dec 2010 07:06:30 +0100 Subject: [PATCH] Monitor the UUIDs used to create and update wearable assets. Reject any changed texture that is not present in the user's inventory full perm. This will prevent "UUID snatching", a copybot-type of attack that can cause clothing makers to be forced to destroy and replace legit items in order to invalidate the copies. --- .../AgentAssetsTransactions.cs | 10 ++ .../AssetTransaction/AssetXferUploader.cs | 115 ++++++++++++++++++ 2 files changed, 125 insertions(+) diff --git a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs index c66a4ea26b..85e1c9949d 100644 --- a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs +++ b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs @@ -167,6 +167,16 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction { if (XferUploaders.ContainsKey(transactionID)) { + // Here we need to get the old asset to extract the + // texture UUIDs if it's a wearable. + if (item.AssetType == (int)AssetType.Bodypart || + item.AssetType == (int)AssetType.Clothing) + { + AssetBase oldAsset = m_Scene.AssetService.Get(item.AssetID.ToString()); + if (oldAsset != null) + XferUploaders[transactionID].SetOldData(oldAsset.Data); + } + AssetBase asset = GetTransactionAsset(transactionID); if (asset != null) diff --git a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetXferUploader.cs b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetXferUploader.cs index a7929ba75a..b8c8c85478 100644 --- a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetXferUploader.cs +++ b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetXferUploader.cs @@ -28,6 +28,7 @@ using System; using System.IO; using System.Reflection; +using System.Collections.Generic; using log4net; using OpenMetaverse; using OpenSim.Framework; @@ -38,6 +39,8 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction { public class AssetXferUploader { + // Viewer's notion of the default texture + private UUID defaultID = new UUID("5748decc-f629-461c-9a36-a35a221fe21f"); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private AssetBase m_asset; @@ -55,6 +58,7 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction private UUID TransactionID = UUID.Zero; private sbyte type = 0; private byte wearableType = 0; + private byte[] m_oldData = null; public ulong XferID; private Scene m_Scene; @@ -219,6 +223,7 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction private void DoCreateItem(uint callbackID) { + ValidateAssets(); m_Scene.AssetService.Store(m_asset); InventoryItemBase item = new InventoryItemBase(); @@ -245,6 +250,71 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction ourClient.SendAlertMessage("Unable to create inventory item"); } + private void ValidateAssets() + { + if (m_asset.Type == (sbyte)AssetType.Clothing || + m_asset.Type == (sbyte)AssetType.Bodypart) + { + string content = System.Text.Encoding.ASCII.GetString(m_asset.Data); + string[] lines = content.Split(new char[] {'\n'}); + + List validated = new List(); + + Dictionary allowed = ExtractTexturesFromOldData(); + + int textures = 0; + + foreach (string line in lines) + { + try + { + if (line.StartsWith("textures ")) + { + textures = Convert.ToInt32(line.Substring(9)); + validated.Add(line); + } + else if (textures > 0) + { + string[] parts = line.Split(new char[] {' '}); + + UUID tx = new UUID(parts[1]); + int id = Convert.ToInt32(parts[0]); + + if (tx == defaultID || tx == UUID.Zero || + (allowed.ContainsKey(id) && allowed[id] == tx)) + { + validated.Add(parts[0] + " " + tx.ToString()); + } + else + { + int perms = m_Scene.InventoryService.GetAssetPermissions(ourClient.AgentId, tx); + int full = (int)(PermissionMask.Modify | PermissionMask.Transfer | PermissionMask.Copy); + + if ((perms & full) != full) + { + m_log.ErrorFormat("[ASSET UPLOADER]: REJECTED update with texture {0} from {1} because they do not own the texture", tx, ourClient.AgentId); + validated.Add(parts[0] + " " + defaultID.ToString()); + } + } + textures--; + } + else + { + validated.Add(line); + } + } + catch + { + // If it's malformed, skip it + } + } + + string final = String.Join("\n", validated.ToArray()); + + m_asset.Data = System.Text.Encoding.ASCII.GetBytes(final); + } + } + /// /// Get the asset data uploaded in this transfer. /// @@ -253,10 +323,55 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction { if (m_finished) { + ValidateAssets(); return m_asset; } return null; } + + public void SetOldData(byte[] d) + { + m_oldData = d; + } + + private Dictionary ExtractTexturesFromOldData() + { + Dictionary result = new Dictionary(); + if (m_oldData == null) + return result; + + string content = System.Text.Encoding.ASCII.GetString(m_oldData); + string[] lines = content.Split(new char[] {'\n'}); + + int textures = 0; + + foreach (string line in lines) + { + try + { + if (line.StartsWith("textures ")) + { + textures = Convert.ToInt32(line.Substring(9)); + } + else if (textures > 0) + { + string[] parts = line.Split(new char[] {' '}); + + UUID tx = new UUID(parts[1]); + int id = Convert.ToInt32(parts[0]); + result[id] = tx; + textures--; + } + } + catch + { + // If it's malformed, skip it + } + } + + return result; + } } } +