I've seen people pulling the hair out for not getting this API workign for them.
The API, even if impersonating the current user, returns error 1309. "An attempt has been made to operate on an impersonation token by a thread that is not currently impersonating a client."
The clue is that this API, (and this is not clearly documented on the MSDN) needs a duplicated handle.
Anyway, spare your hair, have fun with the code. B.t.w. You can hire me for smart code and research on components etc..
http://www.adccure.nl for contact.
[StructLayout(LayoutKind.Sequential)]
internal struct GENERIC_MAPPING
{
internal uint GenericRead;
internal uint GenericWrite;
internal uint GenericExecute;
internal uint GenericAll;
}
[DllImport("advapi32.dll", SetLastError = false)]
static extern void MapGenericMask([In, MarshalAs(UnmanagedType.U4)] ref TokenAccessLevels AccessMask,
[In] ref GENERIC_MAPPING map);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DuplicateToken(IntPtr ExistingTokenHandle,
[MarshalAs(UnmanagedType.U4)] TokenImpersonationLevel level,
out int DuplicateTokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)] static extern bool AccessCheck(
[MarshalAs(UnmanagedType.LPArray)]
byte[] pSecurityDescriptor,
IntPtr ClientToken,
[MarshalAs(UnmanagedType.U4)]
TokenAccessLevels accessmask,
[In] ref GENERIC_MAPPING GenericMapping,
IntPtr PrivilegeSet,
ref int PrivilegeSetLength,
out uint GrantedAccess,
[MarshalAs(UnmanagedType.Bool)]
out bool AccessStatus);
[DllImport("kernel32")]
static extern void CloseHandle(IntPtr ptr);
internal static bool hasReadAccess(string path)
{
// Obtain the authenticated user's Identity
WindowsIdentity winId = WindowsIdentity.GetCurrent(TokenAccessLevels.Duplicate | TokenAccessLevels.Query);
WindowsImpersonationContext ctx = null;
int statError = 0;
IntPtr dupToken = IntPtr.Zero;
try
{
// Start impersonating
//ctx = winId.Impersonate(); works but AccessCheck does not like this
int outPtr;
//AccessCheck needs a duplicated token!
DuplicateToken(winId.Token, TokenImpersonationLevel.Impersonation, out outPtr);
dupToken = new IntPtr(outPtr);
ctx = WindowsIdentity.Impersonate(dupToken);
Folder.GENERIC_MAPPING map = new Folder.GENERIC_MAPPING();
map.GenericRead = 0x80000000;
map.GenericWrite = 0x40000000;
map.GenericExecute = 0x20000000;
map.GenericAll = 0x10000000;
TokenAccessLevels required = TokenAccessLevels.Query | TokenAccessLevels.Read | TokenAccessLevels.AssignPrimary | (TokenAccessLevels)0x00100000; // add synchronization
MapGenericMask(ref required, ref map);
uint status = 0;
bool accesStatus = false;
// dummy area the size should be 20 we don't do anything with it
int sizeps = 20;
IntPtr ps = Marshal.AllocCoTaskMem(sizeps);
//AccessControlSections.Owner | AccessControlSections.Group MUST be included,
//otherwise the descriptor would be seen with ERROR 1338
var ACE = Directory.GetAccessControl(path,
AccessControlSections.Access | AccessControlSections.Owner |
AccessControlSections.Group);
bool success = AccessCheck(ACE.GetSecurityDescriptorBinaryForm(), dupToken, required, ref map,
ps, ref sizeps, out status, out accesStatus);
Marshal.FreeCoTaskMem(ps);
if (!success)
{
statError = Marshal.GetLastWin32Error();
}
else
{
return accesStatus;
}
}
// Prevent exceptions from propagating
catch (Exception ex)
{
Trace.Write(ex.Message);
}
finally
{
// Revert impersonation
if (ctx != null)
ctx.Undo();
CloseHandle(dupToken);
}
if (statError != 0)
{
throw new Win32Exception(statError);
}
return false;
}
This code is just a cut and paste. You can make it pretty.