You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

322 line
11 KiB

  1. using Alphaleonis.Win32.Filesystem;
  2. using Microsoft.Win32.SafeHandles;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.ComponentModel;
  6. using System.Runtime.InteropServices;
  7. using System.Security.AccessControl;
  8. namespace Security2
  9. {
  10. internal partial class Win32
  11. {
  12. internal const string AUTHZ_OBJECTUUID_WITHCAP = "9a81c2bd-a525-471d-a4ed-49907c0b23da";
  13. internal const string RCP_OVER_TCP_PROTOCOL = "ncacn_ip_tcp";
  14. IntPtr userClientCtxt = IntPtr.Zero;
  15. SafeAuthzRMHandle authzRM;
  16. IntPtr pGrantedAccess = IntPtr.Zero;
  17. IntPtr pErrorSecObj = IntPtr.Zero;
  18. #region GetInheritedFrom
  19. public static List<string> GetInheritedFrom(FileSystemInfo item, ObjectSecurity sd)
  20. {
  21. var inheritedFrom = new List<string>();
  22. var sdBytes = sd.GetSecurityDescriptorBinaryForm();
  23. byte[] aclBytes = null;
  24. var rawSd = new RawSecurityDescriptor(sdBytes, 0);
  25. var aceCount = 0;
  26. if (rawSd.SystemAcl != null)
  27. {
  28. aceCount = rawSd.SystemAcl.Count;
  29. aclBytes = new byte[rawSd.SystemAcl.BinaryLength];
  30. rawSd.SystemAcl.GetBinaryForm(aclBytes, 0);
  31. try
  32. {
  33. inheritedFrom = GetInheritedFrom(item.FullName,
  34. aclBytes,
  35. aceCount,
  36. item is DirectoryInfo ? true : false,
  37. SECURITY_INFORMATION.SACL_SECURITY_INFORMATION);
  38. }
  39. catch
  40. {
  41. inheritedFrom = new List<string>();
  42. for (int i = 0; i < aceCount; i++)
  43. {
  44. inheritedFrom.Add("unknown parent");
  45. }
  46. }
  47. }
  48. else if (rawSd.DiscretionaryAcl != null)
  49. {
  50. aceCount = rawSd.DiscretionaryAcl.Count;
  51. aclBytes = new byte[rawSd.DiscretionaryAcl.BinaryLength];
  52. rawSd.DiscretionaryAcl.GetBinaryForm(aclBytes, 0);
  53. try
  54. {
  55. inheritedFrom = GetInheritedFrom(item.FullName,
  56. aclBytes,
  57. aceCount,
  58. item is DirectoryInfo ? true : false,
  59. SECURITY_INFORMATION.DACL_SECURITY_INFORMATION);
  60. }
  61. catch
  62. {
  63. inheritedFrom = new List<string>();
  64. for (int i = 0; i < aceCount; i++)
  65. {
  66. inheritedFrom.Add("unknown parent");
  67. }
  68. }
  69. }
  70. return inheritedFrom;
  71. }
  72. public static List<string> GetInheritedFrom(string path, byte[] aclBytes, int aceCount, bool isContainer, SECURITY_INFORMATION aclType)
  73. {
  74. var inheritedFrom = new List<string>();
  75. path = Path.GetLongPath(path);
  76. uint returnValue = 0;
  77. GENERIC_MAPPING genericMap = new GENERIC_MAPPING();
  78. genericMap.GenericRead = (uint)MappedGenericRights.FILE_GENERIC_READ;
  79. genericMap.GenericWrite = (uint)MappedGenericRights.FILE_GENERIC_WRITE;
  80. genericMap.GenericExecute = (uint)MappedGenericRights.FILE_GENERIC_EXECUTE;
  81. genericMap.GenericAll = (uint)MappedGenericRights.FILE_GENERIC_ALL;
  82. var pInheritInfo = Marshal.AllocHGlobal(aceCount * Marshal.SizeOf(typeof(PINHERITED_FROM)));
  83. returnValue = GetInheritanceSource(
  84. path,
  85. ResourceType.FileObject,
  86. aclType,
  87. isContainer,
  88. IntPtr.Zero,
  89. 0,
  90. aclBytes,
  91. IntPtr.Zero,
  92. ref genericMap,
  93. pInheritInfo
  94. );
  95. if (returnValue != 0)
  96. {
  97. throw new System.ComponentModel.Win32Exception((int)returnValue);
  98. }
  99. for (int i = 0; i < aceCount; i++)
  100. {
  101. var inheritInfo = pInheritInfo.ElementAt<PINHERITED_FROM>(i);
  102. inheritedFrom.Add(
  103. !string.IsNullOrEmpty(inheritInfo.AncestorName) && inheritInfo.AncestorName.StartsWith(@"\\?\") ? inheritInfo.AncestorName.Substring(4) : inheritInfo.AncestorName
  104. );
  105. }
  106. FreeInheritedFromArray(pInheritInfo, (ushort)aceCount, IntPtr.Zero);
  107. Marshal.FreeHGlobal(pInheritInfo);
  108. return inheritedFrom;
  109. }
  110. #endregion GetInheritedFrom
  111. public int GetEffectiveAccess(ObjectSecurity sd, IdentityReference2 identity, string serverName, out bool remoteServerAvailable, out Exception authzException)
  112. {
  113. int effectiveAccess = 0;
  114. remoteServerAvailable = false;
  115. authzException = null;
  116. try
  117. {
  118. GetEffectivePermissions_AuthzInitializeResourceManager(serverName, out remoteServerAvailable);
  119. try
  120. {
  121. GetEffectivePermissions_AuthzInitializeContextFromSid(identity);
  122. effectiveAccess = GetEffectivePermissions_AuthzAccessCheck(sd);
  123. }
  124. catch (Exception ex)
  125. {
  126. authzException = ex;
  127. }
  128. }
  129. catch
  130. { }
  131. finally
  132. {
  133. GetEffectivePermissions_FreeResouces();
  134. }
  135. return effectiveAccess;
  136. }
  137. #region Win32 Wrapper
  138. private void GetEffectivePermissions_AuthzInitializeResourceManager(string serverName, out bool remoteServerAvailable)
  139. {
  140. remoteServerAvailable = false;
  141. var rpcInitInfo = new AUTHZ_RPC_INIT_INFO_CLIENT();
  142. rpcInitInfo.version = AuthzRpcClientVersion.V1;
  143. rpcInitInfo.objectUuid = AUTHZ_OBJECTUUID_WITHCAP;
  144. rpcInitInfo.protocol = RCP_OVER_TCP_PROTOCOL;
  145. rpcInitInfo.server = serverName;
  146. SafeHGlobalHandle pRpcInitInfo = SafeHGlobalHandle.AllocHGlobalStruct(rpcInitInfo);
  147. if (!AuthzInitializeRemoteResourceManager(pRpcInitInfo.ToIntPtr(), out authzRM))
  148. {
  149. int error = Marshal.GetLastWin32Error();
  150. if (error != Win32Error.EPT_S_NOT_REGISTERED) //if not RPC server unavailable
  151. {
  152. throw new Win32Exception(error);
  153. }
  154. if (serverName == "localhost")
  155. {
  156. remoteServerAvailable = true;
  157. }
  158. //
  159. // As a fallback we do AuthzInitializeResourceManager. But the results can be inaccurate.
  160. //
  161. if (!AuthzInitializeResourceManager(
  162. AuthzResourceManagerFlags.NO_AUDIT,
  163. IntPtr.Zero,
  164. IntPtr.Zero,
  165. IntPtr.Zero,
  166. "EffectiveAccessCheck",
  167. out authzRM))
  168. {
  169. throw new Win32Exception(Marshal.GetLastWin32Error());
  170. }
  171. }
  172. else
  173. {
  174. remoteServerAvailable = true;
  175. }
  176. }
  177. private void GetEffectivePermissions_AuthzInitializeContextFromSid(IdentityReference2 id)
  178. {
  179. var rawSid = id.GetBinaryForm();
  180. //
  181. // Create an AuthZ context based on the user account
  182. //
  183. if (!AuthzInitializeContextFromSid(
  184. AuthzInitFlags.Default,
  185. rawSid,
  186. authzRM,
  187. IntPtr.Zero,
  188. Win32.LUID.NullLuid,
  189. IntPtr.Zero,
  190. out userClientCtxt))
  191. {
  192. Win32Exception win32Expn = new Win32Exception(Marshal.GetLastWin32Error());
  193. if (win32Expn.NativeErrorCode != Win32Error.RPC_S_SERVER_UNAVAILABLE)
  194. {
  195. throw win32Expn;
  196. }
  197. }
  198. }
  199. private int GetEffectivePermissions_AuthzAccessCheck(ObjectSecurity sd)
  200. {
  201. var request = new AUTHZ_ACCESS_REQUEST();
  202. request.DesiredAccess = StdAccess.MAXIMUM_ALLOWED;
  203. request.PrincipalSelfSid = null;
  204. request.ObjectTypeList = IntPtr.Zero;
  205. request.ObjectTypeListLength = 0;
  206. request.OptionalArguments = IntPtr.Zero;
  207. var reply = new AUTHZ_ACCESS_REPLY();
  208. reply.ResultListLength = 1;
  209. reply.SaclEvaluationResults = IntPtr.Zero;
  210. reply.GrantedAccessMask = pGrantedAccess = Marshal.AllocHGlobal(sizeof(uint));
  211. reply.Error = pErrorSecObj = Marshal.AllocHGlobal(sizeof(uint));
  212. byte[] rawSD = sd.GetSecurityDescriptorBinaryForm();
  213. if (!AuthzAccessCheck(
  214. AuthzACFlags.None,
  215. userClientCtxt,
  216. ref request,
  217. IntPtr.Zero,
  218. rawSD,
  219. null,
  220. 0,
  221. ref reply,
  222. IntPtr.Zero))
  223. {
  224. var error = Marshal.GetLastWin32Error();
  225. if (error != 0)
  226. {
  227. throw new Win32Exception();
  228. }
  229. }
  230. var grantedAccess = Marshal.ReadInt32(pGrantedAccess);
  231. return grantedAccess;
  232. }
  233. private void GetEffectivePermissions_FreeResouces()
  234. {
  235. Marshal.FreeHGlobal(pGrantedAccess);
  236. Marshal.FreeHGlobal(pErrorSecObj);
  237. if (userClientCtxt != IntPtr.Zero)
  238. {
  239. AuthzFreeContext(userClientCtxt);
  240. userClientCtxt = IntPtr.Zero;
  241. }
  242. }
  243. static RawSecurityDescriptor GetRawSecurityDescriptor(SafeFileHandle handle, SecurityInformationClass infoClass)
  244. {
  245. return new RawSecurityDescriptor(GetByteSecurityDescriptor(handle, infoClass), 0);
  246. }
  247. public static byte[] GetByteSecurityDescriptor(SafeFileHandle handle, SecurityInformationClass infoClass)
  248. {
  249. var tempSD = IntPtr.Zero;
  250. var buffer = new byte[0];
  251. try
  252. {
  253. uint error = GetSecurityInfo(handle,
  254. ObjectType.File,
  255. infoClass,
  256. IntPtr.Zero,
  257. IntPtr.Zero,
  258. IntPtr.Zero,
  259. IntPtr.Zero,
  260. out tempSD);
  261. if (error != Win32Error.ERROR_SUCCESS)
  262. {
  263. throw new Win32Exception(Marshal.GetLastWin32Error());
  264. }
  265. UInt32 sdLength = GetSecurityDescriptorLength(tempSD);
  266. buffer = new byte[sdLength];
  267. Marshal.Copy(tempSD, buffer, 0, (int)sdLength);
  268. }
  269. finally
  270. {
  271. Marshal.FreeHGlobal(tempSD);
  272. tempSD = IntPtr.Zero;
  273. }
  274. return buffer;
  275. }
  276. #endregion
  277. }
  278. }