I need to check if a user is an administrator on the machine where the application is running. Basically, the user will provide the username, password and domain, possibly from another machine. These are transmitted over WCF and at this stage I need to verify whether the provided username, password and domain have admin rights on that machine. This means that there is no need for WMI since everything is happening locally (the user simply sent the username, password, domain as strings over WCF)
Users can be on a domain and hence linked to Active Directory but can can also be a local user, which means that I cannot reply to look it up from Active Directory.
I managed to impersonate the user and can verify that this user is an administrator in the local group. I tested this using the following command:
net localgroup administrators
I am now creating a WindowsPrincipal with the current impersonated user. However, I am getting false when checking if this user is an admin. Here is the important code:
// obtains user token
[DllImport("advapi32.dll", SetLastError=true)]
public static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
// closes open handes returned by LogonUser
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
// creates duplicate token handle
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
bool bImpersonated = LogonUser(sUsername, sDomain, sPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref pExistingTokenHandle);
bool bRetVal = DuplicateToken(pExistingTokenHandle, (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, ref pDuplicateTokenHandle);
WindowsIdentity newId = new WindowsIdentity(pDuplicateTokenHandle);
WindowsImpersonationContext impersonatedUser = newId.Impersonate();
WindowsPrincipal wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());
if (wp.IsInRole(WindowsBuiltInRole.Administrator))
{
//is admin
}
else
{
//is not an admin (I am still getting this when user is an admin)
}
bImpersonated returns true (so Impersonation worked)
bRetVal also returns true (so token is valid)
The Impersonation code is from here (except for the Admin check)
Any help would be greatly appreciated.
I use a check by token :
private static Boolean IsAdministratorByToken(WindowsIdentity identity)
{
WindowsPrincipal principal = new WindowsPrincipal(identity);
// Check if this user has the Administrator role. If they do, return immediately.
// If UAC is on, and the process is not elevated, then this will actually return false.
if (principal.IsInRole(WindowsBuiltInRole.Administrator))
{
return true;
}
// If we're not running in Vista onwards, we don't have to worry about checking for UAC.
if (Environment.OSVersion.Platform != PlatformID.Win32NT || Environment.OSVersion.Version.Major < 6)
{
// Operating system does not support UAC; skipping elevation check.
return false;
}
int tokenInfLength = Marshal.SizeOf(typeof(int));
IntPtr tokenInformation = Marshal.AllocHGlobal(tokenInfLength);
try
{
IntPtr token = identity.Token;
Boolean result = NativeMethods.GetTokenInformation(token, NativeMethods.TokenInformationClass.TokenElevationType, tokenInformation, tokenInfLength, out tokenInfLength);
if (!result)
{
Exception exception = Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
throw new InvalidOperationException("Couldn't get token information", exception);
}
NativeMethods.TokenElevationType elevationType = (NativeMethods.TokenElevationType)Marshal.ReadInt32(tokenInformation);
switch (elevationType)
{
case NativeMethods.TokenElevationType.TokenElevationTypeDefault:
// TokenElevationTypeDefault - User is not using a split token, so they cannot elevate.
return false;
case NativeMethods.TokenElevationType.TokenElevationTypeFull:
// TokenElevationTypeFull - User has a split token, and the process is running elevated. Assuming they're an administrator.
return true;
case NativeMethods.TokenElevationType.TokenElevationTypeLimited:
// TokenElevationTypeLimited - User has a split token, but the process is not running elevated. Assuming they're an administrator.
return true;
default:
// Unknown token elevation type.
return false;
}
}
finally
{
if (tokenInformation != IntPtr.Zero)
{
Marshal.FreeHGlobal(tokenInformation);
}
}
}
this is from this blog.
You can also check by using PrincipalContext, but this solution doesn't work if Server Service is not running.
Code from Xaruth is wrong about TokenElevationTypeLimited, in that case, you should return false and also for build in administrator you will get TokenElevationTypeDefault, so in that case, check if the user is admin instead of just returning false.
Here is code from this post:
https://candritzky.wordpress.com/2012/08/28/uac-elevation-and-the-default-administrator-account/
/// <summary>
/// Base on code found here:
/// http://stackoverflow.com/questions/1220213/c-detect-if-running-with-elevated-privileges
/// </summary>
public static class UacHelper
{
private const string uacRegistryKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System";
private const string uacRegistryValue = "EnableLUA";
private const uint STANDARD_RIGHTS_READ = 0x00020000;
private const uint TOKEN_QUERY = 0x0008;
private const uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass,
IntPtr TokenInformation, uint TokenInformationLength,
out uint ReturnLength);
public enum TOKEN_INFORMATION_CLASS
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
TokenElevationType,
TokenLinkedToken,
TokenElevation,
TokenHasRestrictions,
TokenAccessInformation,
TokenVirtualizationAllowed,
TokenVirtualizationEnabled,
TokenIntegrityLevel,
TokenUIAccess,
TokenMandatoryPolicy,
TokenLogonSid,
MaxTokenInfoClass
}
public enum TOKEN_ELEVATION_TYPE
{
TokenElevationTypeDefault = 1,
TokenElevationTypeFull,
TokenElevationTypeLimited
}
private static bool? _isUacEnabled;
public static bool IsUacEnabled
{
get
{
if (_isUacEnabled == null)
{
var uacKey = Registry.LocalMachine.OpenSubKey(uacRegistryKey, false);
if (uacKey == null)
{
_isUacEnabled = false;
}
else
{
var enableLua = uacKey.GetValue(uacRegistryValue);
_isUacEnabled = enableLua.Equals(1);
}
}
return _isUacEnabled.Value;
}
}
private static bool? _isAdministrator;
public static bool IsAdministrator
{
get
{
if (_isAdministrator == null)
{
var identity = WindowsIdentity.GetCurrent();
Debug.Assert(identity != null);
var principal = new WindowsPrincipal(identity);
_isAdministrator = principal.IsInRole(WindowsBuiltInRole.Administrator);
}
return _isAdministrator.Value;
}
}
private static bool? _isProcessElevated;
public static bool IsProcessElevated
{
get
{
if (_isProcessElevated == null)
{
if (IsUacEnabled)
{
var process = Process.GetCurrentProcess();
IntPtr tokenHandle;
if (!OpenProcessToken(process.Handle, TOKEN_READ, out tokenHandle))
{
throw new ApplicationException("Could not get process token. Win32 Error Code: " +
Marshal.GetLastWin32Error());
}
var elevationResult = TOKEN_ELEVATION_TYPE.TokenElevationTypeDefault;
var elevationResultSize = Marshal.SizeOf((int) elevationResult);
uint returnedSize;
var elevationTypePtr = Marshal.AllocHGlobal(elevationResultSize);
var success = GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenElevationType,
elevationTypePtr, (uint) elevationResultSize, out returnedSize);
if (!success)
{
Marshal.FreeHGlobal(elevationTypePtr);
throw new ApplicationException("Unable to determine the current elevation.");
}
elevationResult = (TOKEN_ELEVATION_TYPE) Marshal.ReadInt32(elevationTypePtr);
Marshal.FreeHGlobal(elevationTypePtr);
// Special test for TokenElevationTypeDefault.
// If the current user is the default Administrator, then the
// process is also assumed to run elevated. This is assumed
// because by default the default Administrator (which is disabled by default)
// gets all access rights even without showing a UAC prompt.
switch (elevationResult)
{
case TOKEN_ELEVATION_TYPE.TokenElevationTypeFull:
_isProcessElevated = true;
break;
case TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited:
_isProcessElevated = false;
break;
default:
// Will come here if either
// 1. We are running as the default Administrator.
// 2. We were started using "Run as administrator" from a non-admin
// account and logged on as the default Administrator account from
// the list of available Administrator accounts.
//
// Note: By default the default Administrator account always behaves
// as if UAC was turned off.
//
// This can be controlled through the Local Security Policy editor
// (secpol.msc) using the
// "User Account Control: Use Admin Approval Mode for the built-in Administrator account"
// option of the Security Settings\Local Policies\Security Options branch.
_isProcessElevated = IsAdministrator;
break;
}
}
else
{
_isProcessElevated = IsAdministrator;
}
}
return _isProcessElevated.Value;
}
}
}
Related
Computer A is on Domain A
Computer B is on Domain B
Domain A and Domain B have a trust allowing Accounts to connect to the other domain's computers
Account A is Admin on Computer A and has Backup&Restore Privileges
Account B is Admin on Computer B and has Backup&Restore Privileges
Account A IS NOT Admin on Computer B and does not have Backup&Restore Privileges
Account B IS NOT Admin on Computer A and does not have Backup&Restore Privileges
A person owns both Account A and Account B.
They want to copy a file from Computer A to Computer B.
They wish to copy this file from and into a folder they do not have permissions for. Therefore, they must both enable Backup&Restore Privileges on their Access Token they got from LogonUser
It is not allowed to change the DACL to grant these permissions on any folders or files and instead both Account A and Account B must enable B&R privileges which is recognized by the Computer, only then will they be able to copy the file without Access Denied.
The problem is I have tried using NETWORK, INTERACTIVE, and NEW_CREDENTIALS Access Tokens and enabling privileges on them, but only NETWORK contains the privileges and they are already enabled by default. Then when I try to WindowsIdentity.Impersonate the NETWORK token and then call "CreateFile" to open the file with privileges, it fails and returns an invalid file handle of -1. I can use INTERACTIVE to read an unrestricted file but it didn't have Access Token Privileges needed ( SeBackupPrivilege & SeRestorePrivilege ) when it was returned from LogonUser. I assume once Impersonate happens it generates a Token that "might" have those privileges, but I assume that's based on the machine the code is running on.
Is there a way to Impersonate Access Token -> Enable Access Token B&R Privileges on the remote computers they would have normally when at that machine running as administrator which could be enabled.
OR
Is there a way to use the NETWORK Token with Impersonation to successfully copy the file from Computer on Domain A to Computer on Domain B. If I run the program as Account B who isn't admin trying to impersonate Account A with network credential, it appears to not work on impersonation
Below is demo code in a Console Application that emulates the situation. You must change parts of it to test it accordingly:
You must create the file paths and the file to be read.
You must edit permissions on "DENYTHEIMPERSONATINGUSERHERE" folder so the impersonating user account is denied and must use privileges.
You must enter an actual account credentials to get an 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;
}
}
So after much research it turns out this is locked down in Windows. Unless the current process token has SeTcbPrivilege (Act as Part of Operating System) and it's enabled, you cannot get an unrestricted/elevated token when deciding to impersonate from a non-elevated token.
Also if you then try to get the LinkedToken/Unrestricted Token of an Access Token but do not have the SeTcbPrivilege enabled, you will end up with a Token that has a SECURITY_IMPERSONATION_LEVEL of SecurityIdentification which cannot be used for impersonation. Without SECURITY_IMPERSONATION_LEVEL being SecurityImpersonation (Local Access) or SecurityDelegation (Local+Remote Access), you cannot impersonate the Unrestricted Access Token and will result in failure with LastError being ERROR_BAD_IMPERSONATION_LEVEL (Either a required impersonation level was not provided, or the provided impersonation level is invalid).
This means if you do not have SeTcbPrivilege and it is not enable on the token, it is required to have an elevated process token (ex. Run As Admin was used to start the program manually or forcefully via requestedExecutionLevel level="requireAdministrator") then Windows will not strip the privileges on Access Token when you decide to impersonate the Access Token.
By using a local administrator account that runs the program as administrator, and then using domain account credentials that are an admin on another machine to get an access token using the params of LOGON32_LOGONTYPE.LOGON32_LOGON_NEW_CREDENTIALS and LOGON32_LOGONPROVIDER.LOGON32_PROVIDER_WINNT50. It is then possible to elevate privileges on the NEWCRED token, impersonate the NEWCRED token which will not strip away the privileges, and have it successfully connect to another computer and read from a file and write to a file on another computer to which it has no permissions or is even explicitly denied, proving it was using the token backup and restore privileges!
Helpful Sources:
how do i convert restricted user token to unrestricted one?
How to call LogonUser() to get a non-restricted full token inside a Windows Service with UAC enabled?
Self service password reset site. For certain operations, I am impersonating a technical user that has account operator privileges in the domain. It works perfectly on my laptop, I can change a user's password, unlock the account or query the domain for all the locked accounts.
It even worked on the server until some 2 weeks ago. I tried to investigate for changes in our environment bot no one is aware of any change that could have had an effect on this.
The best part is that I have no error messages at all. Marshal.GetLastWin32Error() is returning zero, which is basically "everything went OK".
Here's the code where the impersonation happens:
#region accountManagement
[DllImport("advapi32.dll")]
public static extern int LogonUserA(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
WindowsImpersonationContext impersonationContext;
private bool impersonateValidUser(String userName, String domain, String password)
{
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_NETWORK = 3;
const int LOGON32_LOGON_BATCH = 4;
const int LOGON32_LOGON_SERVICE = 5;
const int LOGON32_LOGON_UNLOCK = 7;
const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
const int LOGON32_PROVIDER_DEFAULT = 0;
WindowsIdentity tempWindowsIdentity;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if (RevertToSelf())
{
// Int32 result = LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token);
// Response.Write(">>> " + result.ToString());
if (LogonUserA(userName, domain, password, LOGON32_LOGON_UNLOCK, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
{
CloseHandle(token);
CloseHandle(tokenDuplicate);
return true;
}
}
}
}
if (token != IntPtr.Zero)
CloseHandle(token);
if (tokenDuplicate != IntPtr.Zero)
CloseHandle(tokenDuplicate);
return false;
}
private void undoImpersonation()
{
impersonationContext.Undo();
}
#endregion
And here is the piece that calls it (irrelevant parts removed):
String iuUser = System.Configuration.ConfigurationManager.AppSettings["domain.accountop.user"];
String iuPass = System.Configuration.ConfigurationManager.AppSettings["domain.accountop.pass"];
String iuDomn = System.Configuration.ConfigurationManager.AppSettings["domain.name"];
if (impersonateValidUser(iuUser, iuDomn, iuPass))
{
try
{
email = user.Properties["mail"].Value.ToString();
user.Invoke("SetPassword", new object[] { pw1 });
user.Properties["LockOutTime"].Value = 0; //unlock account
user.CommitChanges();
user.Close();
undoImpersonation();
// clear form and redirect
pPopupSuccess.Visible = true;
hfSuccess.Value = "The account is unlocked now and the password has been reset successfully.";
}
catch (Exception ex)
{
Exception innerException = ex.InnerException;
DirectoryServicesCOMException exds = (DirectoryServicesCOMException)innerException;
String errorMessage = "<p><strong>Your password probably did not meet the requirements.</strong></p>";
errorMessage += "<p>" + System.Configuration.ConfigurationManager.AppSettings["domain.pwreqmessage"] + "</p>";
errorMessage += "<strong>Detailed error message:</strong><br />";
errorMessage += ex.Message;
errorMessage += "<br />";
errorMessage += ex.StackTrace;
if (innerException != null) {
errorMessage = errorMessage + innerException.Message;
}
pPopupError.Visible = true;
hfErrorMessage.Value = errorMessage;
}
}
else
{
// The impersonation failed. Include a fail-safe mechanism here.
pPopupError.Visible = true;
hfErrorMessage.Value = "<p>Impersonation error. Failed to elevate the rights to account operator. Please report this error to the Help Desk.</p>";
hfErrorMessage.Value += "<p>" + Marshal.GetLastWin32Error().ToString() + "</p>";
}
What I keep getting is NOT an exception at the middle, but my own message at the very end of the second code piece saying the impersonation was not successful. And Marshal.GetLastWin32Error() just returs zero.
What can go wrong here and how can I get more information on what's happening? What can be different on the server that makes this code fail, while it's running OK on my dev PC?
Sorry, it was a #PEBKAC. Windows security log suggested that
The user has not been granted the requested logon type at this
machine.
So the answer is "check your windows events!"
This is strange because I had been using this server with this technical account. So I double-checked the user and put it back to the Administrators group of the server. I think it is more than I really need, also a security risk, but I will do some experiments soon to see what is the minimum amount of rights locally to keep it working.
I am writing an application which is used to manipulate the windows services. My computer is in a workgroup. But i couldn't get access to the machine. I've tried Impersonation using LogonUser(). But is not working. I am able to manipulate using Remote Desktop connection, however i couldn't access programatically.
ImpersonateUser ImpersonatedUser = new ImpersonateUser();
ImpersonatedUser.Impersonate(Domain, Username, password);
ServiceController sc = new ServiceController("servicename", host_name);
Console.WriteLine("Success.");
//impersonation.
public class ImpersonateUser
{
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(
String lpszUsername,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
private static IntPtr tokenHandle = new IntPtr(0);
private static WindowsImpersonationContext impersonatedUser;
// If you incorporate this code into a DLL, be sure to demand that it
// runs with FullTrust.
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Impersonate(string domainName, string userName, string password)
{
//try
{
// Use the unmanaged LogonUser function to get the user token for
// the specified user, domain, and password.
const int LOGON32_PROVIDER_DEFAULT = 0;
// Passing this parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 2;
tokenHandle = IntPtr.Zero;
// ---- Step - 1
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(
userName,
domainName,
password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
ref tokenHandle); // tokenHandle - new security token
if (false == returnValue)
{
int ret = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(ret);
}
// ---- Step - 2
WindowsIdentity newId = new WindowsIdentity(tokenHandle);
// ---- Step - 3
{
impersonatedUser = newId.Impersonate();
}
}
}
// Stops impersonation
public void Undo()
{
impersonatedUser.Undo();
// Free the tokens.
if (tokenHandle != IntPtr.Zero)
{
CloseHandle(tokenHandle);
}
}
}
I've found that we cannot impersonate the user on workgroup computer. though we impersonate, WorkGrooup computer force the user to be guest. Thus, if we want to login as the user we need to change the registry value forceguest=0
ASP.NET: Impersonate against a domain on VMWare
This question is what I am asking, but the answer does not provide details on how the _token is derived. It seems to only use WindowsIdentity.GetCurrent().Token so there's no impersonation happening.
Can I impersonate a user on a different Active Directory domain in .NET?
This next question has conflicting answers, with the accepted one bearing a comment "I'm beginning to suspect that my problem lies elsewhere." Not helpful.
LogonUser works only for my domain
This next question seems to imply it is not possible, but it deals with 2 domains so I am not sure if it is relevant.
My real question is:
Is it possible? And if so,
How? or Where did I go wrong?
What I have tried so far is, using the code from http://msdn.microsoft.com/en-us/library/chf6fbt4%28v=VS.80%29.aspx
bool returnValue = LogonUser(user, domain, password,
LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT,
ref tokenHandle);
// after this point, returnValue = false
The Win32 error is
Logon failure: unknown user name or bad password
Very few posts suggest using LOGON_TYPE_NEW_CREDENTIALS instead of LOGON_TYPE_NETWORK or LOGON_TYPE_INTERACTIVE. I had an impersonation issue with one machine connected to a domain and one not, and this fixed it.
The last code snippet in this post suggests that impersonating across a forest does work, but it doesn't specifically say anything about trust being set up. So this may be worth trying:
const int LOGON_TYPE_NEW_CREDENTIALS = 9;
const int LOGON32_PROVIDER_WINNT50 = 3;
bool returnValue = LogonUser(user, domain, password,
LOGON_TYPE_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50,
ref tokenHandle);
MSDN says that LOGON_TYPE_NEW_CREDENTIALS only works when using LOGON32_PROVIDER_WINNT50.
this works for me, full working example (I wish more people would do this):
//logon impersonation
using System.Runtime.InteropServices; // DllImport
using System.Security.Principal; // WindowsImpersonationContext
using System.Security.Permissions; // PermissionSetAttribute
...
class Program {
// obtains user token
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword,
int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
// closes open handes returned by LogonUser
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
public void DoWorkUnderImpersonation() {
//elevate privileges before doing file copy to handle domain security
WindowsImpersonationContext impersonationContext = null;
IntPtr userHandle = IntPtr.Zero;
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
string domain = ConfigurationManager.AppSettings["ImpersonationDomain"];
string user = ConfigurationManager.AppSettings["ImpersonationUser"];
string password = ConfigurationManager.AppSettings["ImpersonationPassword"];
try {
Console.WriteLine("windows identify before impersonation: " + WindowsIdentity.GetCurrent().Name);
// if domain name was blank, assume local machine
if (domain == "")
domain = System.Environment.MachineName;
// Call LogonUser to get a token for the user
bool loggedOn = LogonUser(user,
domain,
password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
ref userHandle);
if (!loggedOn) {
Console.WriteLine("Exception impersonating user, error code: " + Marshal.GetLastWin32Error());
return;
}
// Begin impersonating the user
impersonationContext = WindowsIdentity.Impersonate(userHandle);
Console.WriteLine("Main() windows identify after impersonation: " + WindowsIdentity.GetCurrent().Name);
//run the program with elevated privileges (like file copying from a domain server)
DoWork();
} catch (Exception ex) {
Console.WriteLine("Exception impersonating user: " + ex.Message);
} finally {
// Clean up
if (impersonationContext != null) {
impersonationContext.Undo();
}
if (userHandle != IntPtr.Zero) {
CloseHandle(userHandle);
}
}
}
private void DoWork() {
//everything in here has elevated privileges
//example access files on a network share through e$
string[] files = System.IO.Directory.GetFiles(#"\\domainserver\e$\images", "*.jpg");
}
}
I was having the same problem. Don't know if you've solved this or not, but what I was really trying to do was access a network share with AD credentials. WNetAddConnection2() is what you need to use in that case.
I have been successfull at impersonating users in another domain, but only with a trust set up between the 2 domains.
var token = IntPtr.Zero;
var result = LogonUser(userID, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token);
if (result)
{
return WindowsIdentity.Impersonate(token);
}
It's better to use a SecureString:
var password = new SecureString();
var phPassword phPassword = Marshal.SecureStringToGlobalAllocUnicode(password);
IntPtr phUserToken;
LogonUser(username, domain, phPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out phUserToken);
And:
Marshal.ZeroFreeGlobalAllocUnicode(phPassword);
password.Dispose();
Function definition:
private static extern bool LogonUser(
string pszUserName,
string pszDomain,
IntPtr pszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr phToken);
Invalid login/password could be also related to issues in your DNS server - that's what happened to me and cost me good 5 hours of my life. See if you can specify ip address instead on domain name.
The problem I encountered was when my workstation was on one domain, but I needed to authenticate to a server on a different domain:
ERROR: "Exception impersonating user, error code: 1326"
SOLUTION: Added LOGON32_LOGON_NEW_CREDENTIALS as a fallback to Impersonate/LogonUser()
Impersonation.cs
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
namespace TestDBAccess
{
public class Impersonation : IDisposable
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool LogonUser(String Username, String Domain, String Password, int LogonType, int LogonProvider, out IntPtr Token);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
public const int LOGON32_PROVIDER_DEFAULT = 0;
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_LOGON_NETWORK = 3;
public const int LOGON32_LOGON_BATCH = 4;
public const int LOGON32_LOGON_SERVICE = 5;
public const int LOGON32_LOGON_UNLOCK = 7;
public const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
public const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
private WindowsImpersonationContext impersonationContext = null;
private IntPtr userHandle = IntPtr.Zero;
public Impersonation(string user, string domain, string password)
{
// Extract domain/username from user string
string[] principal = user.Split('\\');
if (principal.Length == 2)
{
domain = principal[0];
user = principal[1];
}
if (string.IsNullOrEmpty(domain))
domain = GetDefaultDomain();
// Call LogonUser to get a token for the user
bool loggedOn =
LogonUser(user, domain, password, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, out userHandle);
if (!loggedOn)
{
int ierr = Marshal.GetLastWin32Error();
if (ierr == 1326)
{
loggedOn =
LogonUser(user, domain, password, LOGON32_LOGON_NEW_CREDENTIALS,
LOGON32_PROVIDER_DEFAULT, out userHandle);
}
if (!loggedOn)
throw new Exception("Exception impersonating user, error code: " + Marshal.GetLastWin32Error());
}
// Begin impersonating the user
impersonationContext = WindowsIdentity.Impersonate(userHandle);
}
public static string GetDefaultDomain ()
{
return System.Environment.UserDomainName;
}
public void Dispose()
{
// Clean up
if (impersonationContext != null)
impersonationContext.Undo();
if (userHandle != IntPtr.Zero)
CloseHandle(userHandle);
}
}
}
ExampleClient.cs
Impersonation Impersonation = null;
try
{
Impersonation = new Impersonation(username, null, password);
LogMsg("Attempting to connect to (" + dbInstance.instance + ")...");
using (SqlConnection connection = new SqlConnection(connString))
{
connection.Open();
string sql = edtTestSql.Text;
LogMsg("Attempting to query (" + sql + ")...");
using (SqlCommand command = new SqlCommand(sql, connection))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
LogMsg("next row: " + DumpRow(reader));
}
}
}
}
catch (Exception ex)
{
LogMsg(ex.Message);
}
finally
{
if (Impersonation != null)
Impersonation.Dispose();
}
I want to know, if a WindowsAccount is Password protected.
For security reasons, you cannot get the Password, that’s clear, but there must be a way, to find out, if a Password is set.
public bool IsAccountPasswordProteced(String userName)
{
String entryString = "WinNT://" + Environment.MachineName + ",Computer";
DirectoryEntry dirEntry = new DirectoryEntry(entryString);
DirectoryEntry user = dirEntry.Children.Find(userName, "User");
// First try was to check the ADS_UF_PASSWD_NOTREQD flag.
// If this flag is set, the account has no password,
// but if not, both is possible.
int userFlags = (int)user.Properties["UserFlags"].Value;
return (userFlags & (int)ActiveDs.ADS_USER_FLAG.ADS_UF_PASSWD_NOTREQD) == 0;
// Second try was to check the PasswordAge.
int pwAge = (int)user.Properties["PasswordAge"].Value;
return pwAge > 0;
}
If there is no better way, I will use the LogonUser-function, but that’s not the way I was looking for. But it’s better than nothing.
If I use this from a local account (not via network, because of the LogonType) and for an enabled account, it should work.
I used this two links:
Calling LogonUser
Detect empty Passwords
public bool IsAccountPasswordProtected(String userName)
{
String entryString = "WinNT://" + Environment.MachineName + ",Computer";
DirectoryEntry dirEntry = new DirectoryEntry(entryString);
DirectoryEntry user = dirEntry.Children.Find(userName, "User");
////EDIT: this flag can also be set, if the account has a password
//int userFlags = (int)user.Properties["UserFlags"].Value;
//if ((userFlags & (int)ActiveDs.ADS_USER_FLAG.ADS_UF_PASSWD_NOTREQD) != 0)
// return false;
IntPtr token;
bool result = LogonUser(
user.Name, Environment.UserDomainName,
"",
LogonTypes.Interactive,
LogonProviders.Default,
out token);
if (result)
{
CloseHandle(token);
return false;
}
else
{
int err = Marshal.GetLastWin32Error();
if (err == 1327) // ERROR_ACCOUNT_RESTRICTION
return false;
//if(err == 1331) // ERROR_ACCOUNT_DISABLED
return true;
}
}
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(
string principal,
string authority,
string password,
LogonTypes logonType,
LogonProviders logonProvider,
out IntPtr token);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);
enum LogonTypes : uint
{
Interactive = 2,
Network,
Batch,
Service,
NetworkCleartext = 8,
NewCredentials
}
enum LogonProviders : uint
{
Default = 0, // default for platform (use this!)
WinNT35, // sends smoke signals to authority
WinNT40, // uses NTLM
WinNT50 // negotiates Kerb or NTLM
}
If you can get the UPN name or user token for the given account (one of the properties of the user object should tell you), then you should just be able to use the WindowsIdentity class something like this:
using System.Security.Principal;
// ...
var identity = new WindowsIdentity("foo-UPN");
var requiresPassword = identity.AuthenticationType != string.Empty;