// 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; } }