| | 1 | | using System; |
| | 2 | | using System.Collections.Concurrent; |
| | 3 | | using System.ComponentModel; |
| | 4 | | using System.Runtime.Serialization; |
| | 5 | | using SharpHoundCommonLib.Enums; |
| | 6 | |
|
| | 7 | | namespace SharpHoundCommonLib |
| | 8 | | { |
| | 9 | | [DataContract] |
| | 10 | | public class Cache |
| | 11 | | { |
| | 12 | | //Leave these here until we switch back to Newtonsoft which doesn't suck |
| | 13 | | // [DataMember]private ConcurrentDictionary<string, string[]> _globalCatalogCache; |
| | 14 | | // |
| | 15 | | // [DataMember]private ConcurrentDictionary<string, Label> _idToTypeCache; |
| | 16 | | // |
| | 17 | | // [DataMember]private ConcurrentDictionary<string, string> _machineSidCache; |
| | 18 | | // |
| | 19 | | // [DataMember]private ConcurrentDictionary<string, string> _sidToDomainCache; |
| | 20 | | // |
| | 21 | | // [DataMember]private ConcurrentDictionary<string, string> _valueToIDCache; |
| | 22 | |
|
| 1 | 23 | | private static Version defaultVersion = new(1, 0, 0); |
| | 24 | |
|
| 2 | 25 | | private Cache() |
| 2 | 26 | | { |
| 2 | 27 | | ValueToIdCache = new ConcurrentDictionary<string, string>(); |
| 2 | 28 | | IdToTypeCache = new ConcurrentDictionary<string, Label>(); |
| 2 | 29 | | GlobalCatalogCache = new ConcurrentDictionary<string, string[]>(); |
| 2 | 30 | | MachineSidCache = new ConcurrentDictionary<string, string>(); |
| 2 | 31 | | SIDToDomainCache = new ConcurrentDictionary<string, string>(); |
| 2 | 32 | | } |
| | 33 | |
|
| 2 | 34 | | [DataMember] public ConcurrentDictionary<string, string[]> GlobalCatalogCache { get; private set; } |
| | 35 | |
|
| 2 | 36 | | [DataMember] public ConcurrentDictionary<string, Label> IdToTypeCache { get; private set; } |
| | 37 | |
|
| 2 | 38 | | [DataMember] public ConcurrentDictionary<string, string> MachineSidCache { get; private set; } |
| | 39 | |
|
| 2 | 40 | | [DataMember] public ConcurrentDictionary<string, string> SIDToDomainCache { get; private set; } |
| | 41 | |
|
| 2 | 42 | | [DataMember] public ConcurrentDictionary<string, string> ValueToIdCache { get; private set; } |
| 3 | 43 | | [DataMember] public DateTime CacheCreationDate { get; set; } |
| 4 | 44 | | [DataMember] public Version CacheCreationVersion { get; set; } |
| | 45 | |
|
| 11 | 46 | | [IgnoreDataMember] private static Cache CacheInstance { get; set; } |
| | 47 | |
|
| | 48 | | /// <summary> |
| | 49 | | /// Add a SID to/from Domain mapping to the cache |
| | 50 | | /// </summary> |
| | 51 | | /// <param name="key"></param> |
| | 52 | | /// <param name="value"></param> |
| | 53 | | internal static void AddDomainSidMapping(string key, string value) |
| 0 | 54 | | { |
| 0 | 55 | | CacheInstance?.SIDToDomainCache.TryAdd(key, value); |
| 0 | 56 | | } |
| | 57 | |
|
| | 58 | | /// <summary> |
| | 59 | | /// Get a SID to Domain or Domain to SID mapping |
| | 60 | | /// </summary> |
| | 61 | | /// <param name="key"></param> |
| | 62 | | /// <param name="value"></param> |
| | 63 | | /// <returns></returns> |
| | 64 | | internal static bool GetDomainSidMapping(string key, out string value) |
| 1 | 65 | | { |
| 1 | 66 | | if (CacheInstance != null) return CacheInstance.SIDToDomainCache.TryGetValue(key, out value); |
| 1 | 67 | | value = null; |
| 1 | 68 | | return false; |
| 1 | 69 | | } |
| | 70 | |
|
| | 71 | | /// <summary> |
| | 72 | | /// Add a Domain SID -> Computer SID mapping to the cache |
| | 73 | | /// </summary> |
| | 74 | | /// <param name="key"></param> |
| | 75 | | /// <param name="value"></param> |
| | 76 | | internal static void AddMachineSid(string key, string value) |
| 4 | 77 | | { |
| 4 | 78 | | CacheInstance?.MachineSidCache.TryAdd(key, value); |
| 4 | 79 | | } |
| | 80 | |
|
| | 81 | | internal static bool GetMachineSid(string key, out string value) |
| 4 | 82 | | { |
| 4 | 83 | | if (CacheInstance != null) return CacheInstance.MachineSidCache.TryGetValue(key, out value); |
| 4 | 84 | | value = null; |
| 4 | 85 | | return false; |
| 4 | 86 | | } |
| | 87 | |
|
| | 88 | | internal static void AddPrefixedValue(string key, string domain, string value) |
| 0 | 89 | | { |
| 0 | 90 | | CacheInstance?.ValueToIdCache.TryAdd(GetPrefixKey(key, domain), value); |
| 0 | 91 | | } |
| | 92 | |
|
| | 93 | | internal static void AddType(string key, Label value) |
| 0 | 94 | | { |
| 0 | 95 | | CacheInstance?.IdToTypeCache.TryAdd(key, value); |
| 0 | 96 | | } |
| | 97 | |
|
| | 98 | | internal static void AddGCCache(string key, string[] value) |
| 1 | 99 | | { |
| 1 | 100 | | CacheInstance?.GlobalCatalogCache?.TryAdd(key, value); |
| 1 | 101 | | } |
| | 102 | |
|
| | 103 | | internal static bool GetGCCache(string key, out string[] value) |
| 1 | 104 | | { |
| 1 | 105 | | if (CacheInstance != null) return CacheInstance.GlobalCatalogCache.TryGetValue(key.ToUpper(), out value); |
| 1 | 106 | | value = null; |
| 1 | 107 | | return false; |
| 1 | 108 | | } |
| | 109 | |
|
| | 110 | | internal static bool GetPrefixedValue(string key, string domain, out string value) |
| 0 | 111 | | { |
| 0 | 112 | | if (CacheInstance != null) |
| 0 | 113 | | return CacheInstance.ValueToIdCache.TryGetValue(GetPrefixKey(key, domain), out value); |
| 0 | 114 | | value = null; |
| 0 | 115 | | return false; |
| 0 | 116 | | } |
| | 117 | |
|
| | 118 | | internal static bool GetIDType(string key, out Label value) |
| 0 | 119 | | { |
| 0 | 120 | | if (CacheInstance != null) return CacheInstance.IdToTypeCache.TryGetValue(key, out value); |
| 0 | 121 | | value = Label.Base; |
| 0 | 122 | | return false; |
| 0 | 123 | | } |
| | 124 | |
|
| | 125 | | private static string GetPrefixKey(string key, string domain) |
| 0 | 126 | | { |
| 0 | 127 | | return $"{key}|{domain}"; |
| 0 | 128 | | } |
| | 129 | |
|
| | 130 | | /// <summary> |
| | 131 | | /// Creates a new empty cache instance |
| | 132 | | /// </summary> |
| | 133 | | /// <returns></returns> |
| | 134 | | public static Cache CreateNewCache(Version version = null) |
| 2 | 135 | | { |
| 2 | 136 | | if (version == null) |
| 1 | 137 | | { |
| 1 | 138 | | version = defaultVersion; |
| 1 | 139 | | } |
| 2 | 140 | | return new Cache |
| 2 | 141 | | { |
| 2 | 142 | | CacheCreationVersion = version, |
| 2 | 143 | | CacheCreationDate = DateTime.Now.Date |
| 2 | 144 | | }; |
| 2 | 145 | | } |
| | 146 | |
|
| | 147 | | /// <summary> |
| | 148 | | /// Sets the cache instance being used by the common library |
| | 149 | | /// </summary> |
| | 150 | | /// <param name="cache"></param> |
| | 151 | | public static void SetCacheInstance(Cache cache) |
| 0 | 152 | | { |
| 0 | 153 | | CacheInstance = cache; |
| 0 | 154 | | CreateMissingDictionaries(); |
| 0 | 155 | | } |
| | 156 | |
|
| | 157 | | /// <summary> |
| | 158 | | /// Gets stats from the currently loaded cache |
| | 159 | | /// </summary> |
| | 160 | | /// <returns></returns> |
| | 161 | | public string GetCacheStats() |
| 0 | 162 | | { |
| | 163 | | try |
| 0 | 164 | | { |
| 0 | 165 | | return |
| 0 | 166 | | $"{IdToTypeCache.Count} ID to type mappings.\n {ValueToIdCache.Count} name to SID mappings.\n {Machi |
| | 167 | | } |
| 0 | 168 | | catch |
| 0 | 169 | | { |
| 0 | 170 | | return ""; |
| | 171 | | } |
| 0 | 172 | | } |
| | 173 | |
|
| | 174 | | /// <summary> |
| | 175 | | /// Returns the currently loaded cache instance |
| | 176 | | /// </summary> |
| | 177 | | /// <returns></returns> |
| | 178 | | public static Cache GetCacheInstance() |
| 0 | 179 | | { |
| 0 | 180 | | return CacheInstance; |
| 0 | 181 | | } |
| | 182 | |
|
| | 183 | | private static void CreateMissingDictionaries() |
| 0 | 184 | | { |
| 0 | 185 | | CacheInstance ??= new Cache(); |
| 0 | 186 | | CacheInstance.IdToTypeCache ??= new ConcurrentDictionary<string, Label>(); |
| 0 | 187 | | CacheInstance.GlobalCatalogCache ??= new ConcurrentDictionary<string, string[]>(); |
| 0 | 188 | | CacheInstance.MachineSidCache ??= new ConcurrentDictionary<string, string>(); |
| 0 | 189 | | CacheInstance.SIDToDomainCache ??= new ConcurrentDictionary<string, string>(); |
| 0 | 190 | | CacheInstance.ValueToIdCache ??= new ConcurrentDictionary<string, string>(); |
| 0 | 191 | | } |
| | 192 | | } |
| | 193 | | } |