From 817a10d0aa670c779fbd7ae74e8af0c4f21dae50 Mon Sep 17 00:00:00 2001
From: Melanie Thielker
Date: Sun, 28 Dec 2008 23:55:34 +0000
Subject: Revamp the return logic to close a privilege escalation loophole.
 Estate owner / Master avatar returns would place the item in the returner's
 inventory rather than the owner's if the owner was not in sim.

---
 .../Region/Environment/Scenes/Scene.Inventory.cs   | 189 ++++++++++++++++-----
 1 file changed, 143 insertions(+), 46 deletions(-)

(limited to 'OpenSim/Region')

diff --git a/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs b/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs
index 2c42502..1f671ab 100644
--- a/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs
+++ b/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs
@@ -1765,6 +1765,26 @@ namespace OpenSim.Region.Environment.Scenes
             }
         }
 
+        private bool WaitForInventory(CachedUserInfo info)
+        {
+            // 200 Seconds wait. This is called in the context of the
+            // background delete thread, so we can afford to waste time
+            // here.
+            //
+            int count = 200;
+
+            while (count > 0)
+            {
+                System.Threading.Thread.Sleep(100);
+                count--;
+                if (info.HasReceivedInventory)
+                    return true;
+            }
+            m_log.DebugFormat("Timed out waiting for inventory of user {0}",
+                    info.UserProfile.ID.ToString());
+            return false;
+        }
+
         /// <summary>
         /// Delete a scene object from a scene and place in the given avatar's inventory.
         /// Returns the UUID of the newly created asset.
@@ -1780,63 +1800,151 @@ namespace OpenSim.Region.Environment.Scenes
 
             string sceneObjectXml = objectGroup.ToXmlString();
 
+            bool useOwner = false;
+
+            // Get the user info of the item destination
+            //
             CachedUserInfo userInfo;
 
-            if (remoteClient == null)
+            if (action == DeRezAction.Take || action == DeRezAction.TakeCopy || 
+                action == DeRezAction.SaveToExistingUserInventoryItem)
             {
-                userInfo = CommsManager.UserProfileCacheService.GetUserDetails(objectGroup.RootPart.OwnerID);
+                // Take or take copy require a taker
+                // Saving changes requires a local user
+                //
+                if (remoteClient == null)
+                    return UUID.Zero;
+
+                userInfo = CommsManager.UserProfileCacheService.GetUserDetails(
+                        remoteClient.AgentId);
             }
             else
             {
-                userInfo = CommsManager.UserProfileCacheService.GetUserDetails(remoteClient.AgentId);
+                // All returns / deletes go to the object owner
+                //
+                userInfo = CommsManager.UserProfileCacheService.GetUserDetails(
+                        objectGroup.RootPart.OwnerID);
+            }
+
+            if (userInfo == null) // Can't proceed
+            {
+                return UUID.Zero;
+            }
+
+            if (!userInfo.HasReceivedInventory)
+            {
+                // Async inventory requests will queue, but they will never
+                // execute unless inventory is actually fetched
+                //
+                CommsManager.UserProfileCacheService.RequestInventoryForUser(
+                        userInfo.UserProfile.ID);
             }
 
             if (userInfo != null)
             {
-                // If we're deleting someone else's item, it goes back to
-                // their deleted items folder
                 // If we're returning someone's item, it goes back to the
                 // owner's Lost And Found folder.
+                // Delete is treated like return in this case
+                // Deleting your own items makes them go to trash
+                //
 
-                if (folderID == UUID.Zero 
-                    || (action == DeRezAction.Delete && objectGroup.OwnerID != remoteClient.AgentId))
-                {
-                    InventoryFolderBase folder =
-                            userInfo.FindFolderForType(
-                            (int)AssetType.LostAndFoundFolder);
+                InventoryFolderBase folder = null;
+                InventoryItemBase item = null;
+                
 
-                    if (folder != null)
+                // No folder type needed
+                // We don't go here unless we have a user logged in
+                // so the skeleton is loaded
+                //
+                if (DeRezAction.SaveToExistingUserInventoryItem == action)
+                {
+                    item = userInfo.RootFolder.FindItem(
+                            objectGroup.RootPart.FromUserInventoryItemID);
+                    
+                    if (null == item)
                     {
-                        folderID = folder.ID;
+                        m_log.DebugFormat(
+                            "[AGENT INVENTORY]: Object {0} {1} scheduled for save to inventory has already been deleted.", 
+                            objectGroup.Name, objectGroup.UUID);                        
+                        return UUID.Zero;
                     }
-                    else
+                }
+                else
+                {
+                    // Folder magic
+                    // 
+                    if (action == DeRezAction.Delete)
                     {
-                        if (userInfo.RootFolder != null)
+                        // Deleting someone else's item
+                        //
+                        if (remoteClient == null ||
+                            objectGroup.OwnerID != remoteClient.AgentId)
                         {
-                            folderID = userInfo.RootFolder.ID;
+                            // Folder skeleton may not be loaded and we
+                            // have to wait for the inventory to find
+                            // the destination folder
+                            //
+                            if (!WaitForInventory(userInfo))
+                                return UUID.Zero;
+                            folder = userInfo.FindFolderForType(
+                                    (int)AssetType.LostAndFoundFolder);
                         }
                         else
                         {
-                            CommsManager.UserProfileCacheService.RequestInventoryForUser(objectGroup.RootPart.OwnerID);
-                            m_log.WarnFormat("[SCENE] Can't find root folder for user, requesting inventory");
-                            return assetID;
+                            // Assume inventory skeleton was loaded during login
+                            // and all folders can be found
+                            //
+                            folder = userInfo.FindFolderForType(
+                                    (int)AssetType.TrashFolder);
                         }
                     }
-                }
+                    else if (action == DeRezAction.Return)
+                    {
+                        // Wait if needed
+                        //
+                        if (!userInfo.HasReceivedInventory)
+                        {
+                            if (!WaitForInventory(userInfo))
+                                return UUID.Zero;
+                        }
 
-                InventoryItemBase item = null;
-                
-                if (DeRezAction.SaveToExistingUserInventoryItem == action)
-                {
-                    item = userInfo.RootFolder.FindItem(objectGroup.RootPart.FromUserInventoryItemID);
-                    
-                    if (null == item)
+                        // Dump to lost + found unconditionally
+                        //
+                        folder = userInfo.FindFolderForType(
+                                (int)AssetType.LostAndFoundFolder);
+                    }
+
+                    if (folderID == UUID.Zero && folder == null)
                     {
-                        m_log.DebugFormat(
-                            "[AGENT INVENTORY]: Object {0} {1} scheduled for save to inventory has already been deleted.", 
-                            objectGroup.Name, objectGroup.UUID);                        
-                        return UUID.Zero;
+                        // Catch all. Use lost & found
+                        //
+                        if (!userInfo.HasReceivedInventory)
+                        {
+                            if (!WaitForInventory(userInfo))
+                                return UUID.Zero;
+                        }
+
+                        folder = userInfo.FindFolderForType(
+                                (int)AssetType.LostAndFoundFolder);
+                    }
+
+                    if (folder == null) // None of the above
+                    {
+                        folder = userInfo.RootFolder.FindFolder(folderID);
+
+                        if (folder == null) // Nowhere to put it
+                        {
+                            return UUID.Zero;
+                        }
                     }
+
+                    item = new InventoryItemBase();
+                    item.Creator = objectGroup.RootPart.CreatorID;
+                    item.ID = UUID.Random();
+                    item.InvType = (int)InventoryType.Object;
+                    item.Folder = folder.ID;
+                    item.Owner = userInfo.UserProfile.ID;
+                    
                 }
 
                 AssetBase asset = CreateAsset(
@@ -1854,22 +1962,8 @@ namespace OpenSim.Region.Environment.Scenes
                 }
                 else
                 {
-                    item = new InventoryItemBase();
-                    item.Creator = objectGroup.RootPart.CreatorID;
-
-                    if (action == DeRezAction.TakeCopy || action == DeRezAction.Take)
-                        item.Owner = remoteClient.AgentId;
-                    else // Delete / Return
-                        item.Owner = objectGroup.OwnerID;
-
-                    item.ID = UUID.Random();
                     item.AssetID = asset.FullID;
-                    item.Description = asset.Description;
-                    item.Name = asset.Name;
-                    item.AssetType = asset.Type;
-                    item.InvType = (int)InventoryType.Object;
-                    item.Folder = folderID;
-                    
+
                     if (remoteClient != null && (remoteClient.AgentId != objectGroup.RootPart.OwnerID) && Permissions.PropagatePermissions())
                     {
                         uint perms=objectGroup.GetEffectivePermissions();
@@ -1899,6 +1993,9 @@ namespace OpenSim.Region.Environment.Scenes
 
                     // TODO: add the new fields (Flags, Sale info, etc)
                     item.CreationDate = Util.UnixTimeSinceEpoch();
+                    item.Description = asset.Description;
+                    item.Name = asset.Name;
+                    item.AssetType = asset.Type;
 
                     userInfo.AddItem(item);
                     
-- 
cgit v1.1