6bdd276d05
Former-commit-id: fd56571888259555122d8a0f58c68838229cea2b
433 lines
14 KiB
C#
433 lines
14 KiB
C#
//
|
|
// System.Security.AccessControl.NativeObjectSecurity implementation
|
|
//
|
|
// Authors:
|
|
// Dick Porter <dick@ximian.com>
|
|
// James Bellinger <jfb@zer7.com>
|
|
//
|
|
// Copyright (C) 2005, 2006 Novell, Inc (http://www.novell.com)
|
|
// Copyright (C) 2012 James Bellinger
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security.Principal;
|
|
|
|
namespace System.Security.AccessControl
|
|
{
|
|
public abstract class NativeObjectSecurity : CommonObjectSecurity
|
|
{
|
|
ExceptionFromErrorCode exception_from_error_code;
|
|
#if WIN_PLATFORM
|
|
ResourceType resource_type;
|
|
#endif
|
|
|
|
protected internal delegate Exception ExceptionFromErrorCode (int errorCode,
|
|
string name, SafeHandle handle,
|
|
object context);
|
|
|
|
internal NativeObjectSecurity (CommonSecurityDescriptor securityDescriptor, ResourceType resourceType)
|
|
: base (securityDescriptor)
|
|
{
|
|
#if WIN_PLATFORM
|
|
resource_type = resourceType;
|
|
#endif
|
|
}
|
|
|
|
protected NativeObjectSecurity (bool isContainer,
|
|
ResourceType resourceType)
|
|
: this (isContainer, resourceType, null, null)
|
|
{
|
|
}
|
|
|
|
protected NativeObjectSecurity (bool isContainer,
|
|
ResourceType resourceType,
|
|
ExceptionFromErrorCode exceptionFromErrorCode,
|
|
object exceptionContext)
|
|
: base (isContainer)
|
|
{
|
|
exception_from_error_code = exceptionFromErrorCode;
|
|
#if WIN_PLATFORM
|
|
resource_type = resourceType;
|
|
#endif
|
|
}
|
|
|
|
protected NativeObjectSecurity (bool isContainer,
|
|
ResourceType resourceType,
|
|
SafeHandle handle,
|
|
AccessControlSections includeSections)
|
|
: this (isContainer, resourceType, handle, includeSections, null, null)
|
|
{
|
|
}
|
|
|
|
protected NativeObjectSecurity (bool isContainer,
|
|
ResourceType resourceType,
|
|
string name,
|
|
AccessControlSections includeSections)
|
|
: this (isContainer, resourceType, name, includeSections, null, null)
|
|
{
|
|
}
|
|
|
|
protected NativeObjectSecurity (bool isContainer,
|
|
ResourceType resourceType,
|
|
SafeHandle handle,
|
|
AccessControlSections includeSections,
|
|
ExceptionFromErrorCode exceptionFromErrorCode,
|
|
object exceptionContext)
|
|
: this (isContainer, resourceType, exceptionFromErrorCode, exceptionContext)
|
|
{
|
|
RaiseExceptionOnFailure (InternalGet (handle, includeSections),
|
|
null, handle, exceptionContext);
|
|
ClearAccessControlSectionsModified ();
|
|
}
|
|
|
|
protected NativeObjectSecurity (bool isContainer,
|
|
ResourceType resourceType,
|
|
string name,
|
|
AccessControlSections includeSections,
|
|
ExceptionFromErrorCode exceptionFromErrorCode,
|
|
object exceptionContext)
|
|
: this (isContainer, resourceType, exceptionFromErrorCode, exceptionContext)
|
|
{
|
|
RaiseExceptionOnFailure (InternalGet (name, includeSections),
|
|
name, null, exceptionContext);
|
|
ClearAccessControlSectionsModified ();
|
|
}
|
|
|
|
void ClearAccessControlSectionsModified ()
|
|
{
|
|
WriteLock ();
|
|
try {
|
|
AccessControlSectionsModified = AccessControlSections.None;
|
|
} finally {
|
|
WriteUnlock ();
|
|
}
|
|
}
|
|
|
|
protected override sealed void Persist (SafeHandle handle,
|
|
AccessControlSections includeSections)
|
|
{
|
|
Persist (handle, includeSections, null);
|
|
}
|
|
|
|
protected override sealed void Persist (string name,
|
|
AccessControlSections includeSections)
|
|
{
|
|
Persist (name, includeSections, null);
|
|
}
|
|
|
|
internal void PersistModifications (SafeHandle handle)
|
|
{
|
|
WriteLock();
|
|
try {
|
|
Persist (handle, AccessControlSectionsModified, null);
|
|
} finally {
|
|
WriteUnlock ();
|
|
}
|
|
}
|
|
|
|
protected void Persist (SafeHandle handle,
|
|
AccessControlSections includeSections,
|
|
object exceptionContext)
|
|
{
|
|
WriteLock ();
|
|
try {
|
|
RaiseExceptionOnFailure (InternalSet (handle, includeSections), null, handle, exceptionContext);
|
|
AccessControlSectionsModified &= ~includeSections;
|
|
} finally {
|
|
WriteUnlock ();
|
|
}
|
|
}
|
|
|
|
internal void PersistModifications (string name)
|
|
{
|
|
WriteLock();
|
|
try {
|
|
Persist (name, AccessControlSectionsModified, null);
|
|
} finally {
|
|
WriteUnlock ();
|
|
}
|
|
}
|
|
|
|
protected void Persist (string name,
|
|
AccessControlSections includeSections,
|
|
object exceptionContext)
|
|
{
|
|
if (null == name)
|
|
throw new ArgumentNullException ("name");
|
|
|
|
WriteLock ();
|
|
try {
|
|
RaiseExceptionOnFailure (InternalSet (name, includeSections), name, null, exceptionContext);
|
|
AccessControlSectionsModified &= ~includeSections;
|
|
} finally {
|
|
WriteUnlock ();
|
|
}
|
|
}
|
|
|
|
internal static Exception DefaultExceptionFromErrorCode (int errorCode,
|
|
string name, SafeHandle handle,
|
|
object context)
|
|
{
|
|
switch (errorCode) {
|
|
case 2: return new FileNotFoundException ();
|
|
case 3: return new DirectoryNotFoundException ();
|
|
case 5: return new UnauthorizedAccessException ();
|
|
case 1314: return new PrivilegeNotHeldException (); // happens with audit rules
|
|
default: return new InvalidOperationException ("OS error code " + errorCode.ToString());
|
|
}
|
|
}
|
|
|
|
void RaiseExceptionOnFailure (int errorCode, string name, SafeHandle handle, object context)
|
|
{
|
|
if (errorCode == 0) return;
|
|
throw (exception_from_error_code ?? DefaultExceptionFromErrorCode)(errorCode, name, handle, context);
|
|
}
|
|
|
|
// InternalGet/InternalSet are virtual so that non-Windows platforms which do not share an
|
|
// API between files, mutexes, etc. can override in the subclass and do their own thing.
|
|
internal virtual int InternalGet (SafeHandle handle,
|
|
AccessControlSections includeSections)
|
|
{
|
|
#if !WIN_PLATFORM
|
|
throw new PlatformNotSupportedException ();
|
|
#else
|
|
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
|
|
throw new PlatformNotSupportedException ();
|
|
|
|
return Win32GetHelper (delegate (SecurityInfos securityInfos,
|
|
out IntPtr owner, out IntPtr group,
|
|
out IntPtr dacl, out IntPtr sacl, out IntPtr descriptor)
|
|
{
|
|
return GetSecurityInfo (handle, ResourceType, securityInfos,
|
|
out owner, out group,
|
|
out dacl, out sacl, out descriptor);
|
|
}, includeSections);
|
|
#endif
|
|
}
|
|
|
|
internal virtual int InternalGet (string name,
|
|
AccessControlSections includeSections)
|
|
{
|
|
#if !WIN_PLATFORM
|
|
throw new PlatformNotSupportedException ();
|
|
#else
|
|
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
|
|
throw new PlatformNotSupportedException ();
|
|
|
|
return Win32GetHelper (delegate (SecurityInfos securityInfos,
|
|
out IntPtr owner, out IntPtr group,
|
|
out IntPtr dacl, out IntPtr sacl, out IntPtr descriptor)
|
|
{
|
|
return GetNamedSecurityInfo (Win32FixName (name), ResourceType, securityInfos,
|
|
out owner, out group,
|
|
out dacl, out sacl, out descriptor);
|
|
}, includeSections);
|
|
#endif
|
|
}
|
|
|
|
#if !WIN_PLATFORM
|
|
internal virtual int InternalSet (SafeHandle handle, AccessControlSections includeSections)
|
|
{
|
|
throw new PlatformNotSupportedException ();
|
|
}
|
|
|
|
internal virtual int InternalSet (string name, AccessControlSections includeSections)
|
|
{
|
|
throw new PlatformNotSupportedException ();
|
|
}
|
|
#else
|
|
internal virtual int InternalSet (SafeHandle handle,
|
|
AccessControlSections includeSections)
|
|
{
|
|
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
|
|
throw new PlatformNotSupportedException ();
|
|
|
|
return Win32SetHelper ((securityInfos, owner, group, dacl, sacl) =>
|
|
SetSecurityInfo (handle, ResourceType, securityInfos, owner, group, dacl, sacl),
|
|
includeSections);
|
|
}
|
|
|
|
internal virtual int InternalSet (string name,
|
|
AccessControlSections includeSections)
|
|
{
|
|
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
|
|
throw new PlatformNotSupportedException ();
|
|
|
|
return Win32SetHelper ((securityInfos, owner, group, dacl, sacl) =>
|
|
SetNamedSecurityInfo (Win32FixName (name), ResourceType, securityInfos, owner, group, dacl, sacl),
|
|
includeSections);
|
|
}
|
|
|
|
internal ResourceType ResourceType {
|
|
get { return resource_type; }
|
|
}
|
|
|
|
#region Win32 Details
|
|
int Win32GetHelper (GetSecurityInfoNativeCall nativeCall,
|
|
AccessControlSections includeSections)
|
|
{
|
|
bool getOwner = 0 != (includeSections & AccessControlSections.Owner);
|
|
bool getGroup = 0 != (includeSections & AccessControlSections.Group);
|
|
bool getDacl = 0 != (includeSections & AccessControlSections.Access);
|
|
bool getSacl = 0 != (includeSections & AccessControlSections.Audit);
|
|
|
|
SecurityInfos securityInfos = 0;
|
|
if (getOwner) securityInfos |= SecurityInfos.Owner;
|
|
if (getGroup) securityInfos |= SecurityInfos.Group;
|
|
if (getDacl ) securityInfos |= SecurityInfos.DiscretionaryAcl;
|
|
if (getSacl ) securityInfos |= SecurityInfos.SystemAcl;
|
|
|
|
IntPtr owner, group, dacl, sacl, descriptor;
|
|
int result = nativeCall (securityInfos,
|
|
out owner, out group, out dacl, out sacl, out descriptor);
|
|
if (0 != result) return result;
|
|
|
|
try {
|
|
int binaryLength = 0;
|
|
if (IsValidSecurityDescriptor (descriptor))
|
|
binaryLength = GetSecurityDescriptorLength (descriptor);
|
|
|
|
byte[] binaryForm = new byte[binaryLength];
|
|
Marshal.Copy (descriptor, binaryForm, 0, binaryLength);
|
|
SetSecurityDescriptorBinaryForm (binaryForm, includeSections);
|
|
} finally {
|
|
LocalFree (descriptor);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int Win32SetHelper (SetSecurityInfoNativeCall nativeCall,
|
|
AccessControlSections includeSections)
|
|
{
|
|
// SE_REGISTRY_KEY will fail UnauthorizedAccessException without this check.
|
|
if (AccessControlSections.None == includeSections) return 0;
|
|
|
|
SecurityInfos securityInfos = 0;
|
|
byte[] owner = null, group = null, dacl = null, sacl = null;
|
|
|
|
if (0 != (includeSections & AccessControlSections.Owner)) {
|
|
securityInfos |= SecurityInfos.Owner;
|
|
SecurityIdentifier ownerSid = (SecurityIdentifier)GetOwner (typeof (SecurityIdentifier));
|
|
if (null != ownerSid) {
|
|
owner = new byte[ownerSid.BinaryLength];
|
|
ownerSid.GetBinaryForm (owner, 0);
|
|
}
|
|
}
|
|
|
|
if (0 != (includeSections & AccessControlSections.Group)) {
|
|
securityInfos |= SecurityInfos.Group;
|
|
SecurityIdentifier groupSid = (SecurityIdentifier)GetGroup (typeof (SecurityIdentifier));
|
|
if (null != groupSid) {
|
|
group = new byte[groupSid.BinaryLength];
|
|
groupSid.GetBinaryForm (group, 0);
|
|
}
|
|
}
|
|
|
|
if (0 != (includeSections & AccessControlSections.Access)) {
|
|
securityInfos |= SecurityInfos.DiscretionaryAcl;
|
|
if (AreAccessRulesProtected)
|
|
securityInfos |= unchecked((SecurityInfos)0x80000000);
|
|
else
|
|
securityInfos |= (SecurityInfos)0x20000000;
|
|
dacl = new byte[descriptor.DiscretionaryAcl.BinaryLength];
|
|
descriptor.DiscretionaryAcl.GetBinaryForm (dacl, 0);
|
|
}
|
|
|
|
if (0 != (includeSections & AccessControlSections.Audit)) {
|
|
if (null != descriptor.SystemAcl) {
|
|
securityInfos |= SecurityInfos.SystemAcl;
|
|
if (AreAuditRulesProtected)
|
|
securityInfos |= (SecurityInfos)0x40000000;
|
|
else
|
|
securityInfos |= (SecurityInfos)0x10000000;
|
|
sacl = new byte[descriptor.SystemAcl.BinaryLength];
|
|
descriptor.SystemAcl.GetBinaryForm (sacl, 0);
|
|
}
|
|
}
|
|
|
|
return nativeCall (securityInfos, owner, group, dacl, sacl);
|
|
}
|
|
|
|
string Win32FixName (string name)
|
|
{
|
|
if (ResourceType == ResourceType.RegistryKey) {
|
|
// For (Get|Set)NamedSecurityInfo, registry paths lack the HKEY_ prefix.
|
|
if (!name.StartsWith ("HKEY_")) throw new InvalidOperationException ();
|
|
name = name.Substring ("HKEY_".Length);
|
|
}
|
|
|
|
return name;
|
|
}
|
|
#endregion
|
|
|
|
#region Win32 P/Invokes
|
|
delegate int GetSecurityInfoNativeCall (SecurityInfos securityInfos,
|
|
out IntPtr owner, out IntPtr group, out IntPtr dacl, out IntPtr sacl,
|
|
out IntPtr descriptor);
|
|
|
|
[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="GetSecurityInfo")]
|
|
static extern int GetSecurityInfo (SafeHandle handle, ResourceType resourceType, SecurityInfos securityInfos,
|
|
out IntPtr owner, out IntPtr group, out IntPtr dacl, out IntPtr sacl,
|
|
out IntPtr descriptor);
|
|
|
|
[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="GetNamedSecurityInfo")]
|
|
static extern int GetNamedSecurityInfo (string name, ResourceType resourceType, SecurityInfos securityInfos,
|
|
out IntPtr owner, out IntPtr group, out IntPtr dacl, out IntPtr sacl,
|
|
out IntPtr descriptor);
|
|
|
|
[DllImport ("kernel32.dll", EntryPoint="LocalFree")]
|
|
static extern IntPtr LocalFree (IntPtr handle);
|
|
|
|
delegate int SetSecurityInfoNativeCall (SecurityInfos securityInfos,
|
|
byte[] owner, byte[] group, byte[] dacl, byte[] sacl);
|
|
|
|
[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="SetSecurityInfo")]
|
|
static extern int SetSecurityInfo (SafeHandle handle, ResourceType resourceType, SecurityInfos securityInfos,
|
|
byte[] owner, byte[] group, byte[] dacl, byte[] sacl);
|
|
|
|
[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="SetNamedSecurityInfo")]
|
|
static extern int SetNamedSecurityInfo (string name, ResourceType resourceType, SecurityInfos securityInfos,
|
|
byte[] owner, byte[] group, byte[] dacl, byte[] sacl);
|
|
|
|
[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="GetSecurityDescriptorLength")]
|
|
static extern int GetSecurityDescriptorLength (IntPtr descriptor);
|
|
|
|
[DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="IsValidSecurityDescriptor")]
|
|
[return: MarshalAs (UnmanagedType.Bool)]
|
|
static extern bool IsValidSecurityDescriptor (IntPtr descriptor);
|
|
|
|
/*
|
|
struct SecurityDescriptor
|
|
{
|
|
public byte Revision, Size;
|
|
public ushort ControlFlags;
|
|
public IntPtr Owner, Group, Sacl, Dacl;
|
|
}
|
|
*/
|
|
#endregion
|
|
#endif
|
|
}
|
|
}
|
|
|