- Компьютер A находится в Домене A
- Компьютер B находится в Домене B
- Домен A и домен B имеют доверие, позволяющее учетным записям подключаться к компьютерам другого домена
- Учетная запись A является администратором на компьютере A и имеет права на резервное копирование и восстановление
- Учетная запись B является администратором на компьютере B и имеет права на резервное копирование и восстановление
- Учетная запись A НЕ является администратором на компьютере B и не имеет прав на резервное копирование и восстановление
- Учетная запись B НЕ является администратором на компьютере A и не имеет прав на резервное копирование и восстановление
Лицо владеет как счетом А, так и счетом Б.
Они хотят скопировать файл с компьютера А на компьютер Б.
Они хотят скопировать этот файл из папки в папку, для которой у них нет разрешений. Следовательно, они оба должны включить полномочия резервного копирования и восстановления на своем токене доступа, который они получили от LogonUser
не разрешено изменять DACL для предоставления этих разрешений для любых папок или файлов, и вместо этого и Учетная запись A, и Учетная запись B должны включать привилегии B & R, которые распознаются компьютером, только тогда они будут возможность скопировать файл без отказа в доступе.
Проблема в том, что я пытался использовать NETWORK, INTERACTIVE и NEW_CREDENTIALS токены доступа и включить для них привилегии, но только NETWORK содержит привилегии, и они уже включены по умолчанию. Затем, когда я пытаюсь выполнить WindowsIdentity. Имитировать маркер NETWORK, а затем вызвать «CreateFile», чтобы открыть файл с привилегиями, он завершается неудачно и возвращает недопустимый дескриптор файла со значением -1. Я могу использовать INTERACTIVE для чтения неограниченного файла, но у него не было необходимых привилегий токена доступа (SeBackupPrivilege & SeRestorePrivilege), когда он был возвращен из LogonUser. Я полагаю, что когда Impersonate происходит, он генерирует токен, который «может» иметь эти привилегии, но я предполагаю, что он основан на машине, на которой выполняется код.
Существует ли способ олицетворения токена доступа -> включить права доступа B & R токена доступа на удаленных компьютерах, которые они обычно имели бы при работе на этом компьютере в качестве администратора, которые могли быть включены.
OR
Есть ли способ использовать токен NETWORK с олицетворением для успешного копирования файла с компьютера в домене A на компьютер в домене B. Если я запускаю программу под учетной записью B, которая не является администратором и пытается выдать себя за учетную запись A с сетью Похоже, учетные данные не работают на подражание
Ниже приведен демонстрационный код в консольном приложении, имитирующий ситуацию. Вы должны изменить части этого, чтобы проверить это соответственно:
Вы должны создать пути к файлам и файл для чтения.
Необходимо отредактировать разрешения для папки «DENYTHEIMPERSONATINGUSERHERE», чтобы учетная запись олицетворенного пользователя была отклонена и должна была использовать привилегии.
Вы должны ввести действительные учетные данные, чтобы получить AccessToken.
class Program
{
static void Main(string[] args)
{
//Credentials of Account A
string usernameA = "AccountA";
string DomainA = "DomainA.com";
SecureString passwordA = new SecureString();
passwordA.AppendChar('P');
passwordA.AppendChar('W');
passwordA.AppendChar('D');
passwordA.AppendChar('A');
IntPtr AccessToken = IntPtr.Zero;
//Getting Network Access Token. (Network is an Impersonation Token, most other types are returned as Primary Tokens)
AccessTokenHelper.LOGON32_LOGONERROR lgnCode = AccessTokenHelper.GetAccessToken(DomainA, usernameA, passwordA, AccessTokenHelper.LOGON32_LOGONTYPE.LOGON32_LOGON_NETWORK, AccessTokenHelper.LOGON32_LOGONPROVIDER.LOGON32_PROVIDER_WINNT50, out AccessToken);
/*
//Getting INTERACTIVE Access Token. (returns Primary Token)
//Impersonation will work but won't have enabled privileges so will error when trying to write file.
AccessTokenHelper.LOGON32_LOGONERROR lgnCode = AccessTokenHelper.GetAccessToken(DomainA, usernameA, passwordA, AccessTokenHelper.LOGON32_LOGONTYPE.LOGON32_LOGON_INTERACTIVE, AccessTokenHelper.LOGON32_LOGONPROVIDER.LOGON32_PROVIDER_WINNT50, out AccessToken);
*/
if(AccessToken == IntPtr.Zero || AccessToken.ToInt32() == -1)
{
//Not valid creds
System.Diagnostics.Debug.WriteLine(lgnCode.ToString());
return;
}
//Enable Token Security Privileges
AccessTokenHelper.ElevateSecurityPrivileges(AccessToken);
//List Enabled Token Privileges
List<string> tokenEnabledPrivileges = AccessTokenHelper.ListEnabledPrivileges(AccessToken);
foreach(string s in tokenEnabledPrivileges)
{
System.Diagnostics.Debug.WriteLine(s);
}
string sourceFile = @"C:\Temp\Test1\TestData.txt";
string destFile = @"C:\Temp\Test1\DENYTHEIMPERSONATINGUSERHERE\PrivCopy.txt";
bool bCopied = PrivilegedEnterpriseCopyFile(sourceFile, destFile, AccessToken, AccessToken);
System.Diagnostics.Debug.WriteLine("DID THE COPY WORK? " + bCopied.ToString().ToUpper());
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
OPENFILE_SHAREACCESS dwShareMode, //If you are reading folders you better have OPENFILE_SHAREACCESS.FILE_SHARE_READ
IntPtr SecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile
);
private enum OPENFILE_SHAREACCESS : uint
{
NO_SHARING = 0x00000000,
FILE_SHARE_READ = 0x00000001,
FILE_SHARE_WRITE = 0x00000002,
FILE_SHARE_DELETE = 0x00000004
};
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool GetFileSizeEx(IntPtr hFile, out long lpFileSize);
/// <summary>
/// Goal: Enterprise Level Copy.... can copy file data between remote machines between domains.
/// </summary>
public static bool PrivilegedEnterpriseCopyFile(string sourceAbsoluteFileName, string destAbsoluteFileName, IntPtr sourceAccessToken, IntPtr destAccessToken, bool bOverwrite = false)
{
const uint GENERIC_READ = 0x80000000;
const uint GENERIC_WRITE = 0x40000000; //If you are writing to a file, best allow both READ and WRITE because Windows Performance suffers without.
const uint CREATE_ALWAYS = 2; //Create and overwrite if necesarry
const uint OPEN_EXISTING = 3; //Open only if exists
const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; //This makes it take enabled Backup&Restore privileges into account
const uint FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000; //This helps speed up reading.
const uint FILE_FLAG_WRITE_THROUGH = 0x80000000; //This helps speed up writing!
//We pick a value that is the largest multiple of 4096 that is still smaller than the large object heap threshold (85K).
// The CopyTo/CopyToAsync buffer is short-lived and is likely to be collected at Gen0, and it offers a significant
// improvement in Copy performance.
const int DefaultCopyBufferSize = 81920; //80kb
bool bSuccess = false;
//Get File Ptr Handles. DO NOT ALLOW DELETE ON SOURCE OR DEST WHILE THIS IS HAPPENING.
//Get the file pointer by using an access token with access.
IntPtr pSource = IntPtr.Zero;
IntPtr pDest = IntPtr.Zero;
//As source user, connect for optimized READING
using (WindowsImpersonationContext impersonatedUser = WindowsIdentity.Impersonate(sourceAccessToken))
{
pSource = CreateFile(sourceAbsoluteFileName, GENERIC_READ, OPENFILE_SHAREACCESS.FILE_SHARE_READ,
IntPtr.Zero, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN, IntPtr.Zero);
}
if (pSource == IntPtr.Zero || pSource.ToInt32() == -1)
{
//Failed to get Source, return false
return bSuccess;
}
//Get Dest Path
string DestPath = Path.GetDirectoryName(destAbsoluteFileName);
string DestFileName = Path.GetFileName(destAbsoluteFileName);
//As dest user
using (WindowsImpersonationContext impersonatedUser = WindowsIdentity.Impersonate(destAccessToken))
{
try
{
bool bProceed = true;
if (!bOverwrite)
{
//We don't want to overwrite existing file, ensure it doesn't exist.
List<string> files = Directory.EnumerateFiles(DestPath).ToList();
if (files.Any(s => s.Equals(DestFileName, StringComparison.OrdinalIgnoreCase)))
{
//File exists, do not proceed
bProceed = false;
}
}
//Do we proceed?
if (bProceed)
{
//Create/Overwrite existing File
pDest = CreateFile(destAbsoluteFileName, GENERIC_READ | GENERIC_WRITE, OPENFILE_SHAREACCESS.NO_SHARING,
IntPtr.Zero, CREATE_ALWAYS,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_WRITE_THROUGH, IntPtr.Zero);
}
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
//If we successfully have both File Handles, we can proceed with Reading from source and writing to Dest.
//If valid file pointers!!!
if (pSource != IntPtr.Zero && pSource.ToInt32() != -1 &&
pDest != IntPtr.Zero && pDest.ToInt32() != -1)
{
//Put Handle into more supported SafeFileHandle Ptr.
SafeFileHandle safeSourcePtr = new SafeFileHandle(pSource, false); //We will close handle manually
SafeFileHandle safeDestPtr = new SafeFileHandle(pDest, false); //We will close handle manually
try
{
using (FileStream fsSource = new FileStream(safeSourcePtr, FileAccess.Read, DefaultCopyBufferSize))
{
using (FileStream fsDest = new FileStream(safeDestPtr, FileAccess.ReadWrite, DefaultCopyBufferSize))
{
//Here we read X bytes up to limit from fsSource and write to fsDest, until no more bytes!
// Read the source file into a byte array.
byte[] buffer = new byte[DefaultCopyBufferSize];
int bytesRead = -1;
while ((bytesRead = fsSource.Read(buffer, 0, buffer.Length)) > 0)
{
fsDest.Write(buffer, 0, bytesRead);
}
//Force data to be flushed to harddisk.
fsDest.Flush(true);
}
}
}
catch { }
//Compare File Size to know if we successfully wrote bytes to destination!
//We'll assume it'd error out if it didn't.
GetFileSizeEx(pSource, out long sourceSize);
GetFileSizeEx(pDest, out long destSize);
if (sourceSize == destSize)
{
//consider it a success
bSuccess = true;
}
}
if (pSource != IntPtr.Zero && pSource.ToInt32() != -1)
{
//Close file handle manually
CloseHandle(pSource);
}
if (pDest != IntPtr.Zero && pDest.ToInt32() != -1)
{
//Close file handle manually
CloseHandle(pDest);
}
return bSuccess;
}
}
public class AccessTokenHelper
{
public enum LOGON32_LOGONTYPE : int
{
LOGON32_LOGON_INTERACTIVE = 2,
LOGON32_LOGON_NETWORK = 3,
LOGON32_LOGON_BATCH = 4,
LOGON32_LOGON_SERVICE = 5,
LOGON32_LOGON_UNLOCK = 7,
LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
LOGON32_LOGON_NEW_CREDENTIALS = 9,
};
public enum LOGON32_LOGONPROVIDER : int
{
LOGON32_PROVIDER_DEFAULT = 0,
LOGON32_PROVIDER_WINNT40 = 2,
LOGON32_PROVIDER_WINNT50 = 3,
};
public enum LOGON32_LOGONERROR : int
{
ERROR_SUCCESS = 0,
ERROR_NO_LOGON_SERVERS = 1311,
ERROR_INVALID_ACCOUNT_NAME = 1315,
ERROR_LOGON_FAILURE = 1326,
ERROR_ACCOUNT_RESTRICTION = 1327,
ERROR_INVALID_LOGON_HOURS = 1328,
ERROR_INVALID_WORKSTATION_LOGONDENIED = 1329,
ERROR_PASSWORD_EXPIRED = 1330,
ERROR_ACCOUNT_DISABLED = 1331,
ERROR_INVALID_LOGON_TYPE = 1367,
ERROR_LOGON_NOT_GRANTED = 1380,
ERROR_NETLOGON_NOT_STARTED = 1792,
ERROR_ACCOUNT_EXPIRED = 1793,
ERROR_PASSWORD_MUST_CHANGE = 1907,
ERROR_ACCOUNT_LOCKED_OUT = 1909,
};
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private extern static bool LogonUser(string username, string domain, IntPtr password, LOGON32_LOGONTYPE logonType, LOGON32_LOGONPROVIDER logonProvider, out IntPtr token);
/// <summary>
/// Attempts to create Access token from information given.
/// </summary>
public static LOGON32_LOGONERROR GetAccessToken(string domain, string username, SecureString securepassword, LOGON32_LOGONTYPE eLOGONTYPE, LOGON32_LOGONPROVIDER eLOGONPROVIDER, out IntPtr token)
{
token = IntPtr.Zero;
// Marshal the SecureString to unmanaged memory. I hate doing this but it's currently most secure way offered by Microsoft to get Access Token from Credentials.
IntPtr passwordPtr = Marshal.SecureStringToGlobalAllocUnicode(securepassword);
bool bSuccess = LogonUser(username,
domain,
passwordPtr,
eLOGONTYPE,
eLOGONPROVIDER,
out token);
//return the error code, useful if not successful.
int errResult = Marshal.GetLastWin32Error();
// Zero-out and free the unmanaged string reference.
Marshal.ZeroFreeGlobalAllocUnicode(passwordPtr);
return (LOGON32_LOGONERROR)errResult;
}
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool LookupPrivilegeName(string lpSystemName, IntPtr lpLuid, StringBuilder lpName, ref int cchName);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool LookupPrivilegeDisplayName(string systemName, string privilegeName, StringBuilder displayName, ref int cchDisplayName, out uint languageId);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out long lpLuid);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool AdjustTokenPrivileges(
IntPtr TokenHandle,
[MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges,
ref TOKEN_PRIVILEGES NewState,
uint prevStateBuffer,
IntPtr prevStateNA,
IntPtr prevBufferNA);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool GetTokenInformation(
IntPtr hToken,
uint TokenInformationClass,
IntPtr TokenInformation,
int TokenInformationLength,
out int ReturnLength);
[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public uint LowPart;
public uint HighPart;
};
[StructLayout(LayoutKind.Sequential)]
public struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public uint Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_PRIVILEGES
{
public int PrivilegeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public LUID_AND_ATTRIBUTES[] Privileges;
};
[Flags]
public enum SE_PRIVILEGE_STATE : uint
{
SE_PRIVILEGE_DISABLED = 0x00,
SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x01,
SE_PRIVILEGE_ENABLED = 0x02,
SE_PRIVILEGE_REMOVED = 0x04,
SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000
};
/// <summary>
/// Will elevate Backup and Restore Privileges of Access Token (be it Primary Type or Impersonation Type) if Access Token has Privilege
/// 1) SeBackupPrivilege -- this grants read access regardless of DACL entries.
/// 2) SeRestorePrivilege -- this grants write access regardless of DACL entries.
/// 3) SeSecurityPrivilege -- this grants access to SACL for audits/security log.
/// </summary>
public static void ElevateSecurityPrivileges(IntPtr hToken, bool bEnabled = true)
{
SE_PRIVILEGE_STATE privSTATE = bEnabled ? SE_PRIVILEGE_STATE.SE_PRIVILEGE_ENABLED : SE_PRIVILEGE_STATE.SE_PRIVILEGE_DISABLED;
List<string> SecurityPrivNames = new List<string>() { "SeBackupPrivilege", "SeRestorePrivilege", "SeSecurityPrivilege" };
if (hToken != IntPtr.Zero)
{
AdjustAccessTokenPrivileges(hToken, SecurityPrivNames, privSTATE);
}
}
public static void AdjustAccessTokenPrivileges(IntPtr TokenHandle, List<string> PrivilegeNames, SE_PRIVILEGE_STATE eNewPrivilegeState, string remoteMachineName = null)
{
if (TokenHandle != IntPtr.Zero && PrivilegeNames != null && PrivilegeNames.Count > 0)
{
DataTable privDT = GetAccessTokenPrivilegesAsDataTable(TokenHandle, remoteMachineName);
if (privDT != null && privDT.Rows.Count > 0)
{
//If we have privileges, try to set state!
foreach (string privName in PrivilegeNames)
{
DataRow row = privDT.Select(string.Format("[{0}]='{1}'", privDT.Columns[1].ColumnName, privName)).FirstOrDefault();
if (row != null)
{
UpdateExistingTokenPrivilege(TokenHandle, row.Field<LUID>(0), eNewPrivilegeState);
}
}
}
}
}
private static bool UpdateExistingTokenPrivilege(IntPtr tokenHandle, LUID privLuid, SE_PRIVILEGE_STATE eNewPrivilegeState)
{
//Create our updated tokenPriv to send what privs we want updated.
TOKEN_PRIVILEGES tokenPrivs = new TOKEN_PRIVILEGES();
tokenPrivs.PrivilegeCount = 1;
tokenPrivs.Privileges = new LUID_AND_ATTRIBUTES[] { new LUID_AND_ATTRIBUTES() { Luid = privLuid, Attributes = (uint)eNewPrivilegeState } };
//Adjust Token Privilege!
bool bSuccess = AdjustTokenPrivileges(tokenHandle, false, ref tokenPrivs, 0, IntPtr.Zero, IntPtr.Zero);
//Return result of trying to adjust token privilege.
return bSuccess;
}
public static DataTable GetAccessTokenPrivilegesAsDataTable(IntPtr TokenHandle, string remoteMachineName = null)
{
TOKEN_PRIVILEGES tokenPrivData = GetAccessTokenPrivileges(TokenHandle);
DataTable privDT = new DataTable();
privDT.Columns.Add("PrivilegeLUID", typeof(LUID));
privDT.Columns.Add("PrivilegeName");
privDT.Columns.Add("PrivilegeState", typeof(SE_PRIVILEGE_STATE));
foreach (LUID_AND_ATTRIBUTES privData in tokenPrivData.Privileges)
{
string PrivilegeName = LookupPrivilegeName(privData.Luid, remoteMachineName);
if (!string.IsNullOrEmpty(PrivilegeName))
{
DataRow row = privDT.NewRow();
row[0] = privData.Luid;
row[1] = PrivilegeName;
row[2] = (SE_PRIVILEGE_STATE)privData.Attributes;
//Add Row
privDT.Rows.Add(row);
}
}
return privDT;
}
private static TOKEN_PRIVILEGES GetAccessTokenPrivileges(IntPtr TokenHandle)
{
uint TOKEN_INFORMATION_CLASS_TokenPrivileges = 3;
if (TokenHandle != IntPtr.Zero)
{
int nBufferSize = 0;
bool TokenInfoResult;
//First call is to get buffer size!
TokenInfoResult = GetTokenInformation(TokenHandle, TOKEN_INFORMATION_CLASS_TokenPrivileges, IntPtr.Zero, nBufferSize, out nBufferSize);
//Allocate Token Info correctly.
IntPtr pTokenInfo = Marshal.AllocHGlobal(nBufferSize);
//Get our Token Info Data
TokenInfoResult = GetTokenInformation(TokenHandle, TOKEN_INFORMATION_CLASS_TokenPrivileges, pTokenInfo, nBufferSize, out nBufferSize);
if (TokenInfoResult)
{
TOKEN_PRIVILEGES returnedPrivilegeSet = (TOKEN_PRIVILEGES)Marshal.PtrToStructure(pTokenInfo, typeof(TOKEN_PRIVILEGES));
int PrivilegeCount = returnedPrivilegeSet.PrivilegeCount;
//lets create the array we should have had returned in the first place
LUID_AND_ATTRIBUTES[] AllPrivs = new LUID_AND_ATTRIBUTES[PrivilegeCount]; //initialize an array to the right size, including 0!
if (PrivilegeCount > 0)
{
LUID_AND_ATTRIBUTES currentPriv = new LUID_AND_ATTRIBUTES();
//pPrivileges will hold our new location to read from by taking the last pointer plus the size of the last structure read
IntPtr pPrivilege = new IntPtr(pTokenInfo.ToInt32() + sizeof(int)); //pointer math, we point to the first element of Privileges array in the struct.
currentPriv = (LUID_AND_ATTRIBUTES)Marshal.PtrToStructure(pPrivilege, typeof(LUID_AND_ATTRIBUTES)); //Get Privilege from pointer
AllPrivs[0] = currentPriv; //We'll add the first element to our array.
//After getting our first structure we can loop through the rest since they will all be the same
for (int i = 1; i < PrivilegeCount; ++i)
{
pPrivilege = new IntPtr(pPrivilege.ToInt32() + Marshal.SizeOf(currentPriv)); //This will point to the next Privilege element in the array
currentPriv = (LUID_AND_ATTRIBUTES)Marshal.PtrToStructure(pPrivilege, typeof(LUID_AND_ATTRIBUTES)); //Get Privilege from pointer
AllPrivs[i] = currentPriv; //Add element to the array
}
}
//Create our complete struct of TOKEN_PRIVILEGES
TOKEN_PRIVILEGES completePrivilegeSet = new TOKEN_PRIVILEGES();
completePrivilegeSet.PrivilegeCount = PrivilegeCount;
completePrivilegeSet.Privileges = AllPrivs;
//We can get release all the pointers now, we got what we wanted!
Marshal.FreeHGlobal(pTokenInfo); //Free up the reserved space in unmanaged memory (Should be done any time AllocHGlobal is used)
//Return our completePrivilegeSet!
return completePrivilegeSet;
}
}
return new TOKEN_PRIVILEGES() { PrivilegeCount = 0, Privileges = new LUID_AND_ATTRIBUTES[] { } };
}
private static string LookupPrivilegeName(LUID privLuid, string remoteMachineName = null)
{
string PrivilegeName = null;
StringBuilder sb = new StringBuilder();
int cchName = 0; //Holds the length of structure we will be receiving LookupPrivilagename
IntPtr ipLuid = Marshal.AllocHGlobal(Marshal.SizeOf(privLuid)); //Allocate a block of memory large enough to hold the structure
Marshal.StructureToPtr(privLuid, ipLuid, true); //Write the structure into the reserved space in unmanaged memory
LookupPrivilegeName(remoteMachineName, ipLuid, null, ref cchName); // call once to get the name length we will be receiving
sb.Capacity = cchName; //Our string builder is buffered for the name!
if (LookupPrivilegeName(remoteMachineName, ipLuid, sb, ref cchName))
{
// Successfully retrieved name!
PrivilegeName = sb.ToString();
}
Marshal.FreeHGlobal(ipLuid); //Free up the reserved space in unmanaged memory
return PrivilegeName;
}
public static List<string> ListEnabledPrivileges(IntPtr TokenHandle, string remoteMachineName = null)
{
List<string> enabledPrivs = null;
DataTable dt = GetAccessTokenPrivilegesAsDataTable(TokenHandle, remoteMachineName);
if (dt != null && dt.Rows.Count > 0)
{
uint nEnabled = (uint)SE_PRIVILEGE_STATE.SE_PRIVILEGE_ENABLED;
uint nEnabledAndDefault = (uint)(SE_PRIVILEGE_STATE.SE_PRIVILEGE_ENABLED_BY_DEFAULT | SE_PRIVILEGE_STATE.SE_PRIVILEGE_ENABLED);
string query = string.Format("[{0}] IN ( {1}, {2} )"
, dt.Columns[2].ColumnName
, nEnabled
, nEnabledAndDefault);
IEnumerable<DataRow> rows = dt.Select(query);
if (rows != null && rows.Count() > 0)
{
enabledPrivs = dt.Select(query).Select(r => r.Field<string>(1)).ToList();
}
}
return enabledPrivs;
}
}