possible fix for remotely loaded plugins
This commit is contained in:
parent
35f9eb5933
commit
d6d2717771
@ -13,10 +13,10 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
{
|
{
|
||||||
public class RemoteAssemblyHandler : IRemoteAssemblyHandler
|
public class RemoteAssemblyHandler : IRemoteAssemblyHandler
|
||||||
{
|
{
|
||||||
private const int keyLength = 32;
|
private const int KeyLength = 32;
|
||||||
private const int tagLength = 16;
|
private const int TagLength = 16;
|
||||||
private const int nonceLength = 12;
|
private const int NonceLength = 12;
|
||||||
private const int iterationCount = 10000;
|
private const int IterationCount = 10000;
|
||||||
|
|
||||||
private readonly ApplicationConfiguration _appconfig;
|
private readonly ApplicationConfiguration _appconfig;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
@ -30,7 +30,7 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
public IEnumerable<Assembly> DecryptAssemblies(string[] encryptedAssemblies)
|
public IEnumerable<Assembly> DecryptAssemblies(string[] encryptedAssemblies)
|
||||||
{
|
{
|
||||||
return DecryptContent(encryptedAssemblies)
|
return DecryptContent(encryptedAssemblies)
|
||||||
.Select(decryptedAssembly => Assembly.Load(decryptedAssembly));
|
.Select(Assembly.Load);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> DecryptScripts(string[] encryptedScripts)
|
public IEnumerable<string> DecryptScripts(string[] encryptedScripts)
|
||||||
@ -38,24 +38,24 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
return DecryptContent(encryptedScripts).Select(decryptedScript => Encoding.UTF8.GetString(decryptedScript));
|
return DecryptContent(encryptedScripts).Select(decryptedScript => Encoding.UTF8.GetString(decryptedScript));
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[][] DecryptContent(string[] content)
|
private IEnumerable<byte[]> DecryptContent(string[] content)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(_appconfig.Id) || string.IsNullOrWhiteSpace(_appconfig.SubscriptionId))
|
if (string.IsNullOrEmpty(_appconfig.Id) || string.IsNullOrWhiteSpace(_appconfig.SubscriptionId))
|
||||||
{
|
{
|
||||||
_logger.LogWarning($"{nameof(_appconfig.Id)} and {nameof(_appconfig.SubscriptionId)} must be provided to attempt loading remote assemblies/scripts");
|
_logger.LogWarning($"{nameof(_appconfig.Id)} and {nameof(_appconfig.SubscriptionId)} must be provided to attempt loading remote assemblies/scripts");
|
||||||
return new byte[0][];
|
return Array.Empty<byte[]>();
|
||||||
}
|
}
|
||||||
|
|
||||||
var assemblies = content.Select(piece =>
|
var assemblies = content.Select(piece =>
|
||||||
{
|
{
|
||||||
byte[] byteContent = Convert.FromBase64String(piece);
|
var byteContent = Convert.FromBase64String(piece);
|
||||||
byte[] encryptedContent = byteContent.Take(byteContent.Length - (tagLength + nonceLength)).ToArray();
|
var encryptedContent = byteContent.Take(byteContent.Length - (TagLength + NonceLength)).ToArray();
|
||||||
byte[] tag = byteContent.Skip(byteContent.Length - (tagLength + nonceLength)).Take(tagLength).ToArray();
|
var tag = byteContent.Skip(byteContent.Length - (TagLength + NonceLength)).Take(TagLength).ToArray();
|
||||||
byte[] nonce = byteContent.Skip(byteContent.Length - nonceLength).Take(nonceLength).ToArray();
|
var nonce = byteContent.Skip(byteContent.Length - NonceLength).Take(NonceLength).ToArray();
|
||||||
byte[] decryptedContent = new byte[encryptedContent.Length];
|
var decryptedContent = new byte[encryptedContent.Length];
|
||||||
|
|
||||||
var keyGen = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(_appconfig.SubscriptionId), Encoding.UTF8.GetBytes(_appconfig.Id.ToString()), iterationCount, HashAlgorithmName.SHA512);
|
var keyGen = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(_appconfig.SubscriptionId), Encoding.UTF8.GetBytes(_appconfig.Id), IterationCount, HashAlgorithmName.SHA512);
|
||||||
var encryption = new AesGcm(keyGen.GetBytes(keyLength));
|
var encryption = new AesGcm(keyGen.GetBytes(KeyLength));
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -20,13 +20,21 @@ namespace IW4MAdmin.Application.Plugin
|
|||||||
public class PluginImporter : IPluginImporter
|
public class PluginImporter : IPluginImporter
|
||||||
{
|
{
|
||||||
private IEnumerable<PluginSubscriptionContent> _pluginSubscription;
|
private IEnumerable<PluginSubscriptionContent> _pluginSubscription;
|
||||||
private static readonly string PluginDir = "Plugins";
|
private const string PluginDir = "Plugins";
|
||||||
private const string PluginV2Match = "^ *((?:var|const|let) +init)|function init";
|
private const string PluginV2Match = "^ *((?:var|const|let) +init)|function init";
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IRemoteAssemblyHandler _remoteAssemblyHandler;
|
private readonly IRemoteAssemblyHandler _remoteAssemblyHandler;
|
||||||
private readonly IMasterApi _masterApi;
|
private readonly IMasterApi _masterApi;
|
||||||
private readonly ApplicationConfiguration _appConfig;
|
private readonly ApplicationConfiguration _appConfig;
|
||||||
|
|
||||||
|
private static readonly Type[] FilterTypes =
|
||||||
|
{
|
||||||
|
typeof(IPlugin),
|
||||||
|
typeof(IPluginV2),
|
||||||
|
typeof(Command),
|
||||||
|
typeof(IBaseConfiguration)
|
||||||
|
};
|
||||||
|
|
||||||
public PluginImporter(ILogger<PluginImporter> logger, ApplicationConfiguration appConfig, IMasterApi masterApi,
|
public PluginImporter(ILogger<PluginImporter> logger, ApplicationConfiguration appConfig, IMasterApi masterApi,
|
||||||
IRemoteAssemblyHandler remoteAssemblyHandler)
|
IRemoteAssemblyHandler remoteAssemblyHandler)
|
||||||
{
|
{
|
||||||
@ -77,74 +85,80 @@ namespace IW4MAdmin.Application.Plugin
|
|||||||
public (IEnumerable<Type>, IEnumerable<Type>, IEnumerable<Type>) DiscoverAssemblyPluginImplementations()
|
public (IEnumerable<Type>, IEnumerable<Type>, IEnumerable<Type>) DiscoverAssemblyPluginImplementations()
|
||||||
{
|
{
|
||||||
var pluginDir = $"{Utilities.OperatingDirectory}{PluginDir}{Path.DirectorySeparatorChar}";
|
var pluginDir = $"{Utilities.OperatingDirectory}{PluginDir}{Path.DirectorySeparatorChar}";
|
||||||
var pluginTypes = Enumerable.Empty<Type>();
|
var pluginTypes = new List<Type>();
|
||||||
var commandTypes = Enumerable.Empty<Type>();
|
var commandTypes = new List<Type>();
|
||||||
var configurationTypes = Enumerable.Empty<Type>();
|
var configurationTypes = new List<Type>();
|
||||||
|
|
||||||
if (Directory.Exists(pluginDir))
|
if (!Directory.Exists(pluginDir))
|
||||||
{
|
{
|
||||||
var dllFileNames = Directory.GetFiles(pluginDir, "*.dll");
|
return (pluginTypes, commandTypes, configurationTypes);
|
||||||
_logger.LogDebug("Discovered {Count} potential plugin assemblies", dllFileNames.Length);
|
}
|
||||||
|
|
||||||
if (dllFileNames.Length > 0)
|
var dllFileNames = Directory.GetFiles(pluginDir, "*.dll");
|
||||||
|
_logger.LogDebug("Discovered {Count} potential plugin assemblies", dllFileNames.Length);
|
||||||
|
|
||||||
|
if (!dllFileNames.Any())
|
||||||
|
{
|
||||||
|
return (pluginTypes, commandTypes, configurationTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we only want to load the most recent assembly in case of duplicates
|
||||||
|
var assemblies = dllFileNames.Select(Assembly.LoadFrom)
|
||||||
|
.Union(GetRemoteAssemblies())
|
||||||
|
.GroupBy(assembly => assembly.FullName).Select(assembly =>
|
||||||
|
assembly.OrderByDescending(asm => asm.GetName().Version).First());
|
||||||
|
|
||||||
|
var eligibleAssemblyTypes = assemblies
|
||||||
|
.SelectMany(asm =>
|
||||||
{
|
{
|
||||||
// we only want to load the most recent assembly in case of duplicates
|
try
|
||||||
var assemblies = dllFileNames.Select(name => Assembly.LoadFrom(name))
|
{
|
||||||
.Union(GetRemoteAssemblies())
|
return asm.GetTypes();
|
||||||
.GroupBy(assembly => assembly.FullName).Select(assembly => assembly.OrderByDescending(asm => asm.GetName().Version).First());
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return Enumerable.Empty<Type>();
|
||||||
|
}
|
||||||
|
}).Where(type =>
|
||||||
|
FilterTypes.Any(filterType => type.GetInterface(filterType.Name, false) != null) ||
|
||||||
|
(type.IsClass && FilterTypes.Contains(type.BaseType)));
|
||||||
|
|
||||||
|
foreach (var assemblyType in eligibleAssemblyTypes)
|
||||||
|
{
|
||||||
|
var isPlugin =
|
||||||
|
(assemblyType.GetInterface(nameof(IPlugin), false) ??
|
||||||
|
assemblyType.GetInterface(nameof(IPluginV2), false)) != null &&
|
||||||
|
(!assemblyType.Namespace?.StartsWith(nameof(SharedLibraryCore)) ?? false);
|
||||||
|
|
||||||
pluginTypes = assemblies
|
if (isPlugin)
|
||||||
.SelectMany(asm =>
|
{
|
||||||
{
|
pluginTypes.Add(assemblyType);
|
||||||
try
|
continue;
|
||||||
{
|
}
|
||||||
return asm.GetTypes();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return Enumerable.Empty<Type>();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.Where(assemblyType => (assemblyType.GetInterface(nameof(IPlugin), false) ?? assemblyType.GetInterface(nameof(IPluginV2), false)) != null)
|
|
||||||
.Where(assemblyType => !assemblyType.Namespace?.StartsWith(nameof(SharedLibraryCore)) ?? false);
|
|
||||||
|
|
||||||
_logger.LogDebug("Discovered {count} plugin implementations", pluginTypes.Count());
|
var isCommand = assemblyType.IsClass && assemblyType.BaseType == typeof(Command) &&
|
||||||
|
(!assemblyType.Namespace?.StartsWith(nameof(SharedLibraryCore)) ?? false);
|
||||||
|
|
||||||
commandTypes = assemblies
|
if (isCommand)
|
||||||
.SelectMany(asm =>{
|
{
|
||||||
try
|
commandTypes.Add(assemblyType);
|
||||||
{
|
continue;
|
||||||
return asm.GetTypes();
|
}
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return Enumerable.Empty<Type>();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.Where(assemblyType => assemblyType.IsClass && assemblyType.BaseType == typeof(Command))
|
|
||||||
.Where(assemblyType => !assemblyType.Namespace?.StartsWith(nameof(SharedLibraryCore)) ?? false);
|
|
||||||
|
|
||||||
_logger.LogDebug("Discovered {Count} plugin commands", commandTypes.Count());
|
var isConfiguration = assemblyType.IsClass &&
|
||||||
|
assemblyType.GetInterface(nameof(IBaseConfiguration), false) != null &&
|
||||||
|
(!assemblyType.Namespace?.StartsWith(nameof(SharedLibraryCore)) ?? false);
|
||||||
|
|
||||||
configurationTypes = assemblies
|
if (isConfiguration)
|
||||||
.SelectMany(asm => {
|
{
|
||||||
try
|
configurationTypes.Add(assemblyType);
|
||||||
{
|
|
||||||
return asm.GetTypes();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return Enumerable.Empty<Type>();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.Where(asmType =>
|
|
||||||
asmType.IsClass && asmType.GetInterface(nameof(IBaseConfiguration), false) != null)
|
|
||||||
.Where(assemblyType => !assemblyType.Namespace?.StartsWith(nameof(SharedLibraryCore)) ?? false);
|
|
||||||
|
|
||||||
_logger.LogDebug("Discovered {Count} configuration implementations", configurationTypes.Count());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger.LogDebug("Discovered {Count} plugin implementations", pluginTypes.Count);
|
||||||
|
_logger.LogDebug("Discovered {Count} plugin commands", pluginTypes.Count);
|
||||||
|
_logger.LogDebug("Discovered {Count} configuration implementations", pluginTypes.Count);
|
||||||
|
|
||||||
return (pluginTypes, commandTypes, configurationTypes);
|
return (pluginTypes, commandTypes, configurationTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,10 +166,11 @@ namespace IW4MAdmin.Application.Plugin
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_pluginSubscription == null)
|
_pluginSubscription ??= _masterApi
|
||||||
_pluginSubscription = _masterApi.GetPluginSubscription(Guid.Parse(_appConfig.Id), _appConfig.SubscriptionId).Result;
|
.GetPluginSubscription(Guid.Parse(_appConfig.Id), _appConfig.SubscriptionId).Result;
|
||||||
|
|
||||||
return _remoteAssemblyHandler.DecryptAssemblies(_pluginSubscription.Where(sub => sub.Type == PluginType.Binary).Select(sub => sub.Content).ToArray());
|
return _remoteAssemblyHandler.DecryptAssemblies(_pluginSubscription
|
||||||
|
.Where(sub => sub.Type == PluginType.Binary).Select(sub => sub.Content).ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -169,9 +184,11 @@ namespace IW4MAdmin.Application.Plugin
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_pluginSubscription ??= _masterApi.GetPluginSubscription(Guid.Parse(_appConfig.Id), _appConfig.SubscriptionId).Result;
|
_pluginSubscription ??= _masterApi
|
||||||
|
.GetPluginSubscription(Guid.Parse(_appConfig.Id), _appConfig.SubscriptionId).Result;
|
||||||
|
|
||||||
return _remoteAssemblyHandler.DecryptScripts(_pluginSubscription.Where(sub => sub.Type == PluginType.Script).Select(sub => sub.Content).ToArray());
|
return _remoteAssemblyHandler.DecryptScripts(_pluginSubscription
|
||||||
|
.Where(sub => sub.Type == PluginType.Script).Select(sub => sub.Content).ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
Loading…
Reference in New Issue
Block a user