MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.IO;6using System.Management.Automation;7using System.Management.Automation.Internal;8using System.Management.Automation.Provider;9using System.Security.AccessControl;10 11namespace Microsoft.PowerShell.Commands12{13 /// <summary>14 /// The FileSystemProvider provides stateless namespace navigation15 /// of the file system.16 /// </summary>17 public sealed partial class FileSystemProvider : NavigationCmdletProvider, IContentCmdletProvider, IPropertyCmdletProvider, ISecurityDescriptorCmdletProvider18 {19 #region ISecurityDescriptorCmdletProvider members20 21 /// <summary>22 /// Gets the SecurityDescriptor at the specified path, including only the specified23 /// AccessControlSections.24 /// </summary>25 /// <param name="path">26 /// The path of the item to retrieve. It may be a drive or provider-qualified path and may include.27 /// glob characters.28 /// </param>29 /// <param name="sections">30 /// The sections of the security descriptor to include.31 /// </param>32 /// <returns>33 /// Nothing. An object that represents the security descriptor for the item34 /// specified by path is written to the context's pipeline.35 /// </returns>36 /// <exception cref="System.ArgumentException">37 /// path is null or empty.38 /// path doesn't exist39 /// sections is not valid.40 /// </exception>41 public void GetSecurityDescriptor(string path,42 AccessControlSections sections)43 {44 ObjectSecurity sd = null;45 path = NormalizePath(path);46 47 if (string.IsNullOrEmpty(path))48 {49 throw PSTraceSource.NewArgumentNullException(nameof(path));50 }51 52 if ((sections & ~AccessControlSections.All) != 0)53 {54 throw PSTraceSource.NewArgumentException(nameof(sections));55 }56 57 var currentPrivilegeState = new PlatformInvokes.TOKEN_PRIVILEGE();58 try59 {60 PlatformInvokes.EnableTokenPrivilege("SeBackupPrivilege", ref currentPrivilegeState);61 62 if (Directory.Exists(path))63 {64 sd = new DirectorySecurity(path, sections);65 }66 else67 {68 sd = new FileSecurity(path, sections);69 }70 }71 catch (System.Security.SecurityException e)72 {73 WriteError(new ErrorRecord(e, e.GetType().FullName, ErrorCategory.PermissionDenied, path));74 }75 finally76 {77 PlatformInvokes.RestoreTokenPrivilege("SeBackupPrivilege", ref currentPrivilegeState);78 }79 80 WriteSecurityDescriptorObject(sd, path);81 }82 83 /// <summary>84 /// Sets the SecurityDescriptor at the specified path.85 /// </summary>86 /// <param name="path">87 /// The path of the item to set the security descriptor on.88 /// It may be a drive or provider-qualified path and may include.89 /// glob characters.90 /// </param>91 /// <param name="securityDescriptor">92 /// The new security descriptor for the item.93 /// </param>94 /// <exception cref="System.ArgumentException">95 /// path is null or empty.96 /// </exception>97 /// <exception cref="System.ArgumentNullException">98 /// securitydescriptor is null.99 /// </exception>100 public void SetSecurityDescriptor(101 string path,102 ObjectSecurity securityDescriptor)103 {104 if (string.IsNullOrEmpty(path))105 {106 throw PSTraceSource.NewArgumentException(nameof(path));107 }108 109 path = NormalizePath(path);110 111 if (securityDescriptor == null)112 {113 throw PSTraceSource.NewArgumentNullException(nameof(securityDescriptor));114 }115 116 if (!File.Exists(path) && !Directory.Exists(path))117 {118 ThrowTerminatingError(CreateErrorRecord(path,119 "SetSecurityDescriptor_FileNotFound"));120 }121 122 FileSystemSecurity sd = securityDescriptor as FileSystemSecurity;123 124 if (sd == null)125 {126 throw PSTraceSource.NewArgumentException(nameof(securityDescriptor));127 }128 else129 {130 // This algorithm works around the following security descriptor complexities:131 //132 // - In order to copy an ACL between files, you need to use the133 // binary form, and transfer all sections. If you don't use the binary form,134 // then the FileSystem only applies changes that have happened to that specific135 // ACL object -- which will not be present if you are just stamping a specific136 // ACL on a lot of files.137 // - Copying a full ACL means copying its Audit section, which normal users138 // don't have access to.139 //140 // In order to make this cmdlet support regular users modifying their own files,141 // the solution is to:142 //143 // - First attempt to copy the entire security descriptor as we did in V1.144 // This ensures backward compatibility for administrator scripts that currently145 // work.146 // - If the attempt fails due to a PrivilegeNotHeld exception, try again with147 // an estimate of the minimum required subset. This is an estimate, since the148 // ACL object doesn't tell you exactly what's changed.149 // - If their ACL doesn't include any audit rules, don't try to set the150 // audit section. If it does contain Audit rules, continue to try and151 // set the section, so they get an appropriate error message.152 // - If their ACL has the same Owner / Group as the destination file,153 // also don't try to set those sections.154 // If they added audit rules, or made changes to the Owner / Group, they will155 // still get an error message.156 //157 // We can't roll the two steps into one, as the second step can't handle the158 // situation where an admin wants to _clear_ the audit entries. It would be nice to159 // detect a difference in audit entries (like we do with Owner and Group,) but160 // retrieving the Audit entries requires SeSecurityPrivilege as well.161 162 try163 {164 // Try to set the entire security descriptor165 SetSecurityDescriptor(path, sd, AccessControlSections.All);166 }167 catch (PrivilegeNotHeldException)168 {169 // Get the security descriptor of the destination path170 ObjectSecurity existingDescriptor = new FileInfo(path).GetAccessControl();171 // Use SecurityIdentifier to avoid having the below comparison steps172 // fail when dealing with an untranslatable SID in the SD173 Type identityType = typeof(System.Security.Principal.SecurityIdentifier);174 175 AccessControlSections sections = AccessControlSections.All;176 177 // If they didn't modify any audit information, don't try to set178 // the audit section.179 int auditRuleCount = sd.GetAuditRules(true, true, identityType).Count;180 if ((auditRuleCount == 0) &&181 (sd.AreAuditRulesProtected == existingDescriptor.AreAccessRulesProtected))182 {183 sections &= ~AccessControlSections.Audit;184 }185 186 // If they didn't modify the owner, don't try to set that section.187 if (sd.GetOwner(identityType) == existingDescriptor.GetOwner(identityType))188 {189 sections &= ~AccessControlSections.Owner;190 }191 192 // If they didn't modify the group, don't try to set that section.193 if (sd.GetGroup(identityType) == existingDescriptor.GetGroup(identityType))194 {195 sections &= ~AccessControlSections.Group;196 }197 198 // Try to set the security descriptor again, this time with a reduced set199 // of sections.200 SetSecurityDescriptor(path, sd, sections);201 }202 }203 }204 205 private void SetSecurityDescriptor(string path, ObjectSecurity sd, AccessControlSections sections)206 {207 var currentPrivilegeState = new PlatformInvokes.TOKEN_PRIVILEGE();208 byte[] securityDescriptorBinary = null;209 210 try211 {212 // Get the binary form of the descriptor.213 PlatformInvokes.EnableTokenPrivilege("SeBackupPrivilege", ref currentPrivilegeState);214 securityDescriptorBinary = sd.GetSecurityDescriptorBinaryForm();215 }216 finally217 {218 PlatformInvokes.RestoreTokenPrivilege("SeBackupPrivilege", ref currentPrivilegeState);219 }220 221 try222 {223 PlatformInvokes.EnableTokenPrivilege("SeRestorePrivilege", ref currentPrivilegeState);224 225 // Transfer it to the new file / directory.226 // We keep these two code branches so that we can have more227 // granular information when we output the object type via228 // WriteSecurityDescriptorObject.229 if (Directory.Exists(path))230 {231 DirectorySecurity newDescriptor = new DirectorySecurity();232 newDescriptor.SetSecurityDescriptorBinaryForm(securityDescriptorBinary, sections);233 new DirectoryInfo(path).SetAccessControl(newDescriptor);234 WriteSecurityDescriptorObject(newDescriptor, path);235 }236 else237 {238 FileSecurity newDescriptor = new FileSecurity();239 newDescriptor.SetSecurityDescriptorBinaryForm(securityDescriptorBinary, sections);240 new FileInfo(path).SetAccessControl(newDescriptor);241 WriteSecurityDescriptorObject(newDescriptor, path);242 }243 }244 finally245 {246 PlatformInvokes.RestoreTokenPrivilege("SeRestorePrivilege", ref currentPrivilegeState);247 }248 }249 250 /// <summary>251 /// Creates a new empty security descriptor of the same type as252 /// the item specified by the path. If "path" points to a file system directory,253 /// then the descriptor returned will be of type DirectorySecurity.254 /// </summary>255 /// <param name="path">256 /// Path of the item to use to determine the type of resulting257 /// SecurityDescriptor.258 /// </param>259 /// <param name="sections">260 /// The sections of the security descriptor to create.261 /// </param>262 /// <returns>263 /// A new ObjectSecurity object of the same type as264 /// the item specified by the path.265 /// </returns>266 public ObjectSecurity NewSecurityDescriptorFromPath(267 string path,268 AccessControlSections sections)269 {270 ItemType itemType = ItemType.Unknown;271 272 if (IsItemContainer(path))273 {274 itemType = ItemType.Directory;275 }276 else277 {278 itemType = ItemType.File;279 }280 281 return NewSecurityDescriptor(itemType);282 }283 284 /// <summary>285 /// Creates a new empty security descriptor of the specified type.286 /// </summary>287 /// <param name="type">288 /// The type of Security Descriptor to create. Valid types are289 /// "file", "directory," and "container."290 /// </param>291 /// <param name="sections">292 /// The sections of the security descriptor to create.293 /// </param>294 /// <returns>295 /// A new ObjectSecurity object of the specified type.296 /// </returns>297 public ObjectSecurity NewSecurityDescriptorOfType(298 string type,299 AccessControlSections sections)300 {301 ItemType itemType = ItemType.Unknown;302 303 itemType = GetItemType(type);304 return NewSecurityDescriptor(itemType);305 }306 307 private static ObjectSecurity NewSecurityDescriptor(308 ItemType itemType)309 {310 ObjectSecurity sd = null;311 312 switch (itemType)313 {314 case ItemType.File:315 sd = new FileSecurity();316 break;317 318 case ItemType.Directory:319 sd = new DirectorySecurity();320 break;321 }322 323 return sd;324 }325 326 private static ErrorRecord CreateErrorRecord(string path,327 string errorId)328 {329 string message = null;330 331 message = StringUtil.Format(FileSystemProviderStrings.FileNotFound, path);332 333 ErrorRecord er =334 new ErrorRecord(new FileNotFoundException(message),335 errorId,336 ErrorCategory.ObjectNotFound,337 null);338 339 return er;340 }341 342 #endregion ISecurityDescriptorCmdletProvider members343 }344}345 