// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
namespace EpicGames.UBA
{
///
/// Utils
///
public static partial class Utils
{
///
/// Is UBA available?
///
public static bool IsAvailable() => s_available.Value;
static readonly Lazy s_available = new(() => File.Exists(GetLibraryPath()));
///
/// Paths that are not allowed to be transferred over the network for UBA remote agents.
///
/// enumerable of disallowed paths
public static IEnumerable DisallowedPaths => s_disallowedPaths.Distinct().Order();
static readonly ConcurrentBag s_disallowedPaths = [];
///
/// Mapping of binary paths for cross architecture host binaries, to allow for using helpers of a different architecture.
///
/// dictionary of binary mappings, where the key is the binary for the current host architecture
public static IReadOnlyDictionary CrossArchitecturePaths => s_crossArchitecturePaths;
static readonly ConcurrentDictionary s_crossArchitecturePaths = [];
///
/// Mapping of a folder path to a single hash, to allow for hashing an entire folder so each individual file does not need to be processed.
///
public static IReadOnlyDictionary PathHashes => s_pathHashes;
static readonly ConcurrentDictionary s_pathHashes = [];
///
/// Registers a path that is not allowed to be transferred over the network for UBA remote agents.
///
/// The paths to add to the disallowed list
public static void RegisterDisallowedPaths(params string[] paths)
{
foreach (string path in paths)
{
if (!s_disallowedPaths.Contains(path))
{
s_disallowedPaths.Add(path);
}
}
DisallowedPathRegistered?.Invoke(s_disallowedPaths, new(paths));
}
///
/// Registers a path mapping for cross architecture binaries
///
/// host architecture path
/// cross architecture path
public static void RegisterCrossArchitecturePath(string path, string crossPath)
{
if (s_crossArchitecturePaths.TryAdd(path, crossPath))
{
CrossArchitecturePathRegistered?.Invoke(s_crossArchitecturePaths, new(path, crossPath));
}
}
///
/// Registers a hash for a path
///
/// The path string
/// The hash string
public static void RegisterPathHash(string path, string hash)
{
if (s_pathHashes.TryAdd(path, hash))
{
PathHashRegistered?.Invoke(s_pathHashes, new(path, hash));
}
}
///
/// Delegate for registering a remote disallowed path
///
/// collection that is being changed
/// event args containing which paths were added
public delegate void DisallowedPathRegisteredEventHandler(IReadOnlyCollection sender, DisallowedPathRegisteredEventArgs e);
///
/// Delegate for registering a cross architecture path
///
/// collection that is being changed
/// event args containing which path was added
public delegate void CrossArchitecturePathRegisteredEventHandler(IReadOnlyDictionary sender, CrossArchitecturePathRegisteredEventArgs e);
///
/// Delegate for registering a path hash
///
/// collection that is being changed
/// event args containing which path was added
public delegate void PathHashRegisteredEventHandler(IReadOnlyDictionary sender, PathHashRegisteredEventArgs e);
///
/// Remote disallowed path registered event handler
///
public static event DisallowedPathRegisteredEventHandler? DisallowedPathRegistered;
///
/// Cross architecture path registered event handler
///
public static event CrossArchitecturePathRegisteredEventHandler? CrossArchitecturePathRegistered;
///
/// Remote disallowed path registered event handler
///
public static event PathHashRegisteredEventHandler? PathHashRegistered;
///
/// Get the path to the p/invoke library that would be loaded
///
/// The path to the library
/// If the operating system is not supported
[System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "folder path is lowercase")]
static string GetLibraryPath()
{
string arch = RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant();
string assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
if (OperatingSystem.IsWindows())
{
return Path.Combine(assemblyFolder, "runtimes", $"win-{arch}", "native", "UbaHost.dll");
}
else if (OperatingSystem.IsLinux())
{
return Path.Combine(assemblyFolder, "runtimes", $"linux-{arch}", "native", "libUbaHost.so");
}
else if (OperatingSystem.IsMacOS())
{
return Path.Combine(assemblyFolder, "runtimes", $"osx-{arch}", "native", "libUbaHost.dylib");
}
throw new PlatformNotSupportedException();
}
}
///
/// Event args for registering a remote disallowed path
///
public sealed class DisallowedPathRegisteredEventArgs(params string[] paths) : EventArgs
{
///
/// The paths being registered
///
public IEnumerable Paths { get; } = paths;
}
///
/// Event args for registering a cross architecture path
///
public sealed class CrossArchitecturePathRegisteredEventArgs(string path, string crossPath) : EventArgs
{
///
/// The host architecture path being registered
///
public string Path { get; } = path;
///
/// The cross architecture path being registered
///
public string CrossPath { get; } = crossPath;
}
///
/// Event args for registering a path hash
///
public sealed class PathHashRegisteredEventArgs(string path, string hash) : EventArgs
{
///
/// The path being registered
///
public string Path { get; } = path;
///
/// The hash for the path being registered
///
public string Hash { get; } = hash;
}
}