mirror of
https://github.com/Facepunch/sbox-public.git
synced 2025-12-23 22:48:07 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
150 lines
4.0 KiB
C#
150 lines
4.0 KiB
C#
using Microsoft.CodeAnalysis;
|
|
using Mono.Cecil;
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Reflection;
|
|
using System.Security.Cryptography;
|
|
|
|
namespace Sandbox;
|
|
|
|
/// <summary>
|
|
/// Test for access rules compliance. Can be shared to prevent unnecessary re-checking or resolving.
|
|
/// </summary>
|
|
[SkipHotload]
|
|
public partial class AccessControl : IAssemblyResolver
|
|
{
|
|
internal ConcurrentDictionary<string, string> SafeAssemblies = new();
|
|
internal ConcurrentDictionary<AssemblyNameReference, AssemblyDefinition> Assemblies = new( AssemblyNameComparer.Instance );
|
|
internal AccessRules Rules;
|
|
|
|
public AccessControl()
|
|
{
|
|
Rules = new AccessRules();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
foreach ( var assm in Assemblies.Values )
|
|
{
|
|
assm.Dispose();
|
|
}
|
|
|
|
Assemblies = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dangerous! Create a <see cref="TrustedBinaryStream"/> of the given stream
|
|
/// without actually passing it through access control.
|
|
/// </summary>
|
|
public TrustedBinaryStream TrustUnsafe( byte[] dll )
|
|
{
|
|
var instance = new AssemblyAccess( this, dll );
|
|
return TrustedBinaryStream.CreateInternal( dll );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if an assembly passes whitelist checks.
|
|
/// </summary>
|
|
/// <param name="dll"></param>
|
|
/// <param name="outStream"><see cref="TrustedBinaryStream"/> wrapper around <paramref name="dll"/>.
|
|
/// Will close if <paramref name="dll"/> gets closed.</param>
|
|
/// <param name="addToWhitelist"></param>
|
|
public AccessControlResult VerifyAssembly( Stream dll, out TrustedBinaryStream outStream, bool addToWhitelist = true )
|
|
{
|
|
var ms = new MemoryStream();
|
|
dll.CopyTo( ms );
|
|
var bytes = ms.ToArray();
|
|
ms.Dispose();
|
|
|
|
var instance = new AssemblyAccess( this, bytes );
|
|
instance.Verify( out outStream );
|
|
|
|
if ( addToWhitelist )
|
|
{
|
|
AddSafeAssembly( instance.Assembly.Name.Name, bytes );
|
|
}
|
|
|
|
return instance.Result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// If we're definitely never going to see this assembly again (because it's being unloaded for instance)
|
|
/// We can totally get rid of it and free all that lovely memory.
|
|
/// </summary>
|
|
public void ForgetAssembly( string name )
|
|
{
|
|
RemoveSafeAssembly( name );
|
|
|
|
var matches = Assemblies
|
|
.Where( x => x.Key.Name.Equals( name, StringComparison.OrdinalIgnoreCase ) )
|
|
.ToArray();
|
|
|
|
foreach ( var match in matches )
|
|
{
|
|
if ( Assemblies.Remove( match.Key, out var assembly ) )
|
|
{
|
|
assembly.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Forget all versions of the named assembly strictly older than this one.
|
|
/// </summary>
|
|
public void ForgetOlderAssemblyDefinitions( AssemblyName name )
|
|
{
|
|
ForgetOlderAssemblyDefinitions( new AssemblyNameReference( name.Name, name.Version ) );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Forget all versions of the named assembly strictly older than this one.
|
|
/// </summary>
|
|
public void ForgetOlderAssemblyDefinitions( AssemblyNameReference name )
|
|
{
|
|
var matches = Assemblies
|
|
.Where( x => x.Key.Name.Equals( name.Name, StringComparison.OrdinalIgnoreCase ) )
|
|
.Where( x => x.Key.Version.CompareTo( name.Version ) < 0 )
|
|
.ToArray();
|
|
|
|
foreach ( var match in matches )
|
|
{
|
|
if ( Assemblies.Remove( match.Key, out var assembly ) )
|
|
{
|
|
assembly.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void AddSafeAssembly( string name, byte[] data )
|
|
{
|
|
using var sha256 = SHA256.Create();
|
|
var hash = Convert.ToBase64String( sha256.ComputeHash( data ) );
|
|
SafeAssemblies[name] = hash;
|
|
}
|
|
internal bool CheckSafeAssembly( string name, byte[] data )
|
|
{
|
|
using var sha256 = SHA256.Create();
|
|
var hash = Convert.ToBase64String( sha256.ComputeHash( data ) );
|
|
return SafeAssemblies.TryGetValue( name, out var existing ) && existing == hash;
|
|
}
|
|
internal bool RemoveSafeAssembly( string name ) => SafeAssemblies.Remove( name, out _ );
|
|
|
|
public struct CodeLocation
|
|
{
|
|
public string Text;
|
|
public Location RoslynLocation = null;
|
|
|
|
public CodeLocation( string text )
|
|
{
|
|
// most definitions will not have accurate symbols/locations, this is the best we can get
|
|
Text = text;
|
|
}
|
|
|
|
public CodeLocation( Location location )
|
|
{
|
|
Text = location.ToString();
|
|
RoslynLocation = location;
|
|
}
|
|
}
|
|
}
|