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.

265 lines
14 KiB

  1. /* Copyright (C) 2008-2016 Peter Palotas, Jeffrey Jangli, Alexandr Normuradov
  2. *
  3. * Permission is hereby granted, free of charge, to any person obtaining a copy
  4. * of this software and associated documentation files (the "Software"), to deal
  5. * in the Software without restriction, including without limitation the rights
  6. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. * copies of the Software, and to permit persons to whom the Software is
  8. * furnished to do so, subject to the following conditions:
  9. *
  10. * The above copyright notice and this permission notice shall be included in
  11. * all copies or substantial portions of the Software.
  12. *
  13. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. * THE SOFTWARE.
  20. */
  21. using System;
  22. using System.Diagnostics.CodeAnalysis;
  23. using System.Runtime.InteropServices;
  24. using System.Security;
  25. using System.Security.AccessControl;
  26. using Alphaleonis.Win32.Security;
  27. using Microsoft.Win32.SafeHandles;
  28. namespace Alphaleonis.Win32.Filesystem
  29. {
  30. partial class File
  31. {
  32. /// <summary>Applies access control list (ACL) entries described by a <see cref="FileSecurity"/> FileSecurity object to the specified file.</summary>
  33. /// <exception cref="ArgumentNullException"/>
  34. /// <exception cref="ArgumentException"/>
  35. /// <exception cref="NotSupportedException"/>
  36. /// <param name="path">A file to add or remove access control list (ACL) entries from.</param>
  37. /// <param name="fileSecurity">A <see cref="FileSecurity"/> object that describes an ACL entry to apply to the file described by the <paramref name="path"/> parameter.</param>
  38. [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
  39. [SecurityCritical]
  40. public static void SetAccessControl(string path, FileSecurity fileSecurity)
  41. {
  42. SetAccessControlCore(path, null, fileSecurity, AccessControlSections.All, PathFormat.RelativePath);
  43. }
  44. /// <summary>Applies access control list (ACL) entries described by a <see cref="DirectorySecurity"/> object to the specified directory.</summary>
  45. /// <exception cref="ArgumentNullException"/>
  46. /// <exception cref="ArgumentException"/>
  47. /// <exception cref="NotSupportedException"/>
  48. /// <param name="path">A directory to add or remove access control list (ACL) entries from.</param>
  49. /// <param name="fileSecurity">A <see cref="FileSecurity "/> object that describes an ACL entry to apply to the directory described by the path parameter.</param>
  50. /// <param name="includeSections">One or more of the <see cref="AccessControlSections"/> values that specifies the type of access control list (ACL) information to set.</param>
  51. [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
  52. [SecurityCritical]
  53. public static void SetAccessControl(string path, FileSecurity fileSecurity, AccessControlSections includeSections)
  54. {
  55. SetAccessControlCore(path, null, fileSecurity, includeSections, PathFormat.RelativePath);
  56. }
  57. /// <summary>[AlphaFS] Applies access control list (ACL) entries described by a <see cref="FileSecurity"/> FileSecurity object to the specified file.</summary>
  58. /// <exception cref="ArgumentNullException"/>
  59. /// <exception cref="ArgumentException"/>
  60. /// <exception cref="NotSupportedException"/>
  61. /// <param name="path">A file to add or remove access control list (ACL) entries from.</param>
  62. /// <param name="fileSecurity">A <see cref="FileSecurity"/> object that describes an ACL entry to apply to the file described by the <paramref name="path"/> parameter.</param>
  63. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  64. [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
  65. [SecurityCritical]
  66. public static void SetAccessControl(string path, FileSecurity fileSecurity, PathFormat pathFormat)
  67. {
  68. SetAccessControlCore(path, null, fileSecurity, AccessControlSections.All, pathFormat);
  69. }
  70. /// <summary>[AlphaFS] Applies access control list (ACL) entries described by a <see cref="DirectorySecurity"/> object to the specified directory.</summary>
  71. /// <exception cref="ArgumentNullException"/>
  72. /// <exception cref="ArgumentException"/>
  73. /// <exception cref="NotSupportedException"/>
  74. /// <param name="path">A directory to add or remove access control list (ACL) entries from.</param>
  75. /// <param name="fileSecurity">A <see cref="FileSecurity "/> object that describes an ACL entry to apply to the directory described by the path parameter.</param>
  76. /// <param name="includeSections">One or more of the <see cref="AccessControlSections"/> values that specifies the type of access control list (ACL) information to set.</param>
  77. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  78. [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
  79. [SecurityCritical]
  80. public static void SetAccessControl(string path, FileSecurity fileSecurity, AccessControlSections includeSections, PathFormat pathFormat)
  81. {
  82. SetAccessControlCore(path, null, fileSecurity, includeSections, pathFormat);
  83. }
  84. /// <summary>Applies access control list (ACL) entries described by a <see cref="FileSecurity"/> FileSecurity object to the specified file.</summary>
  85. /// <exception cref="ArgumentNullException"/>
  86. /// <exception cref="ArgumentException"/>
  87. /// <exception cref="NotSupportedException"/>
  88. /// <param name="handle">A <see cref="SafeFileHandle"/> to a file to add or remove access control list (ACL) entries from.</param>
  89. /// <param name="fileSecurity">A <see cref="FileSecurity"/> object that describes an ACL entry to apply to the file described by the <paramref name="handle"/> parameter.</param>
  90. [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
  91. [SecurityCritical]
  92. public static void SetAccessControl(SafeFileHandle handle, FileSecurity fileSecurity)
  93. {
  94. SetAccessControlCore(null, handle, fileSecurity, AccessControlSections.All, PathFormat.LongFullPath);
  95. }
  96. /// <summary>Applies access control list (ACL) entries described by a <see cref="FileSecurity"/> FileSecurity object to the specified file.</summary>
  97. /// <exception cref="ArgumentNullException"/>
  98. /// <exception cref="ArgumentException"/>
  99. /// <exception cref="NotSupportedException"/>
  100. /// <param name="handle">A <see cref="SafeFileHandle"/> to a file to add or remove access control list (ACL) entries from.</param>
  101. /// <param name="fileSecurity">A <see cref="FileSecurity"/> object that describes an ACL entry to apply to the file described by the <paramref name="handle"/> parameter.</param>
  102. /// <param name="includeSections">One or more of the <see cref="AccessControlSections"/> values that specifies the type of access control list (ACL) information to set.</param>
  103. [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
  104. [SecurityCritical]
  105. public static void SetAccessControl(SafeFileHandle handle, FileSecurity fileSecurity, AccessControlSections includeSections)
  106. {
  107. SetAccessControlCore(null, handle, fileSecurity, includeSections, PathFormat.LongFullPath);
  108. }
  109. /// <summary>[AlphaFS] Applies access control list (ACL) entries described by a <see cref="FileSecurity"/>/<see cref="DirectorySecurity"/> object to the specified file or directory.</summary>
  110. /// <remarks>Use either <paramref name="path"/> or <paramref name="handle"/>, not both.</remarks>
  111. /// <exception cref="ArgumentNullException"/>
  112. /// <exception cref="ArgumentException"/>
  113. /// <exception cref="NotSupportedException"/>
  114. /// <param name="path">A file/directory to add or remove access control list (ACL) entries from. This parameter This parameter may be <see langword="null"/>.</param>
  115. /// <param name="handle">A <see cref="SafeFileHandle"/> to add or remove access control list (ACL) entries from. This parameter This parameter may be <see langword="null"/>.</param>
  116. /// <param name="objectSecurity">A <see cref="FileSecurity"/>/<see cref="DirectorySecurity"/> object that describes an ACL entry to apply to the file/directory described by the <paramref name="path"/>/<paramref name="handle"/> parameter.</param>
  117. /// <param name="includeSections">One or more of the <see cref="AccessControlSections"/> values that specifies the type of access control list (ACL) information to set.</param>
  118. /// <param name="pathFormat">Indicates the format of the path parameter(s).</param>
  119. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
  120. [SecurityCritical]
  121. internal static void SetAccessControlCore(string path, SafeFileHandle handle, ObjectSecurity objectSecurity, AccessControlSections includeSections, PathFormat pathFormat)
  122. {
  123. if (pathFormat == PathFormat.RelativePath)
  124. Path.CheckSupportedPathFormat(path, true, true);
  125. if (objectSecurity == null)
  126. throw new ArgumentNullException("objectSecurity");
  127. byte[] managedDescriptor = objectSecurity.GetSecurityDescriptorBinaryForm();
  128. using (var safeBuffer = new SafeGlobalMemoryBufferHandle(managedDescriptor.Length))
  129. {
  130. string pathLp = Path.GetExtendedLengthPathCore(null, path, pathFormat, GetFullPathOptions.RemoveTrailingDirectorySeparator | GetFullPathOptions.CheckInvalidPathChars);
  131. safeBuffer.CopyFrom(managedDescriptor, 0, managedDescriptor.Length);
  132. SecurityDescriptorControl control;
  133. uint revision;
  134. if (!Security.NativeMethods.GetSecurityDescriptorControl(safeBuffer, out control, out revision))
  135. NativeError.ThrowException(Marshal.GetLastWin32Error(), pathLp);
  136. PrivilegeEnabler privilegeEnabler = null;
  137. try
  138. {
  139. var securityInfo = SecurityInformation.None;
  140. IntPtr pDacl = IntPtr.Zero;
  141. if ((includeSections & AccessControlSections.Access) != 0)
  142. {
  143. bool daclDefaulted, daclPresent;
  144. if (!Security.NativeMethods.GetSecurityDescriptorDacl(safeBuffer, out daclPresent, out pDacl, out daclDefaulted))
  145. NativeError.ThrowException(Marshal.GetLastWin32Error(), pathLp);
  146. if (daclPresent)
  147. {
  148. securityInfo |= SecurityInformation.Dacl;
  149. securityInfo |= (control & SecurityDescriptorControl.DaclProtected) != 0
  150. ? SecurityInformation.ProtectedDacl
  151. : SecurityInformation.UnprotectedDacl;
  152. }
  153. }
  154. IntPtr pSacl = IntPtr.Zero;
  155. if ((includeSections & AccessControlSections.Audit) != 0)
  156. {
  157. bool saclDefaulted, saclPresent;
  158. if (!Security.NativeMethods.GetSecurityDescriptorSacl(safeBuffer, out saclPresent, out pSacl, out saclDefaulted))
  159. NativeError.ThrowException(Marshal.GetLastWin32Error(), pathLp);
  160. if (saclPresent)
  161. {
  162. securityInfo |= SecurityInformation.Sacl;
  163. securityInfo |= (control & SecurityDescriptorControl.SaclProtected) != 0
  164. ? SecurityInformation.ProtectedSacl
  165. : SecurityInformation.UnprotectedSacl;
  166. privilegeEnabler = new PrivilegeEnabler(Privilege.Security);
  167. }
  168. }
  169. IntPtr pOwner = IntPtr.Zero;
  170. if ((includeSections & AccessControlSections.Owner) != 0)
  171. {
  172. bool ownerDefaulted;
  173. if (!Security.NativeMethods.GetSecurityDescriptorOwner(safeBuffer, out pOwner, out ownerDefaulted))
  174. NativeError.ThrowException(Marshal.GetLastWin32Error(), pathLp);
  175. if (pOwner != IntPtr.Zero)
  176. securityInfo |= SecurityInformation.Owner;
  177. }
  178. IntPtr pGroup = IntPtr.Zero;
  179. if ((includeSections & AccessControlSections.Group) != 0)
  180. {
  181. bool groupDefaulted;
  182. if (!Security.NativeMethods.GetSecurityDescriptorGroup(safeBuffer, out pGroup, out groupDefaulted))
  183. NativeError.ThrowException(Marshal.GetLastWin32Error(), pathLp);
  184. if (pGroup != IntPtr.Zero)
  185. securityInfo |= SecurityInformation.Group;
  186. }
  187. uint lastError;
  188. if (!Utils.IsNullOrWhiteSpace(pathLp))
  189. {
  190. // SetNamedSecurityInfo()
  191. // In the ANSI version of this function, the name is limited to MAX_PATH characters.
  192. // To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path.
  193. // 2013-01-13: MSDN does not confirm LongPath usage but a Unicode version of this function exists.
  194. lastError = Security.NativeMethods.SetNamedSecurityInfo(pathLp, ObjectType.FileObject, securityInfo, pOwner, pGroup, pDacl, pSacl);
  195. if (lastError != Win32Errors.ERROR_SUCCESS)
  196. NativeError.ThrowException(lastError, pathLp);
  197. }
  198. else
  199. {
  200. if (NativeMethods.IsValidHandle(handle))
  201. {
  202. lastError = Security.NativeMethods.SetSecurityInfo(handle, ObjectType.FileObject, securityInfo, pOwner, pGroup, pDacl, pSacl);
  203. if (lastError != Win32Errors.ERROR_SUCCESS)
  204. NativeError.ThrowException((int) lastError);
  205. }
  206. }
  207. }
  208. finally
  209. {
  210. if (privilegeEnabler != null)
  211. privilegeEnabler.Dispose();
  212. }
  213. }
  214. }
  215. }
  216. }