Win32 User Impersonation Curiosity - c#

I have found some sample code on codeproject that allows for user impersonation.
This code works by importing the following unmanaged Win32 API functions:
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int LogonUser(
string lpszUserName,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int DuplicateToken(IntPtr hToken,int impersonationLevel,ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool CloseHandle(IntPtr handle);
These functions are used to impersonate the target user, then perform some operations, then revert the impersonation context. Impersonating the user is achieved like so:
if ( LogonUser(userName, domainName, password, LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT, ref token ) != 0 )
{
if ( DuplicateToken( token, 2, ref tokenDuplicate ) != 0 )
{
tempWindowsIdentity = new WindowsIdentity( tokenDuplicate );
impersonationContext = tempWindowsIdentity.Impersonate();
}
}
I'm trying to understand why this code first gets the required token using LogonUser, then duplicates that token, before performing the impersonation on the duplicated token. Why not just impersonate using the token that you get from the LogonUser method.
Obviously the person that wrote this article understands this better than I do so it would appear that I am missing something. Could I please get an explanation of why the seemingly redundant token duplication step of this process is required?

As far as I know, token, passed to WindowsIdentity ctor should be an impersonation token. So, the author of that code using
DuplicateToken( token, 2, ref tokenDuplicate )
to create an impersonation token from primary token, returned by LogonUser(). That '2' magic number stands for SecurityImpersonation member of SECURITY_IMPERSONATION_LEVEL enum.
Links:
http://msdn.microsoft.com/en-us/library/aa378184%28v=vs.85%29.aspx
http://msdn.microsoft.com/en-us/library/aa379572%28v=vs.85%29.aspx
http://msdn.microsoft.com/en-us/library/aa446616%28v=vs.85%29.aspx

Related

How to get system privilege

I have a C# program that require SeSystemEnvironmentPrivilege to access the UEFI NVRAM.
I found a really long code, that uses Win32 API to get the privilege, but is there a .NET version to get it? In process class, or somewhere else?
If it is really necessary you can use the AdjustTokenPrivileges function.
Something like this:
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
You can get more info here:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa446619%28v=vs.85%29.aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/aa375728(v=vs.85).aspx#privilege_constants

How to Start a Process in Session 1 from a Windows 7 Service

I have a service running in Windows 7. In Windows 7 all services run in Session 0. From that service I want to create an interactive user session (in a session other than Session 0) and start an application in that session. My problem is that when I call LogonUser to start an interactive user session and then use CreateProcessAsUser to start the application the application ends up running in Session 0.
All of my code is C#.
Here is the relevant code:
[DllImport("advapi32.dll", SetLastError=true)]
static extern bool LogonUser(
string principal,
string authority,
string password,
UInt32 logonType,
UInt32 logonProvider,
out IntPtr token);
[DllImport("advapi32.dll", SetLastError=true)]
static extern bool CreateProcessAsUser(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
int dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
ref PROCESS_INFORMATION lpProcessInformation);
IntPtr token;
LogonUser("UserName", ".", "Password",
LogonTypes.Interactive,LogonProviders.Default, out token)
<code to impersonate user>
string hd = Environment.ExpandEnvironmentVariables("%USERPROFILE%");
IntPtr envBlock = IntPtr.Zero;
CreateProcessAsUser(token, "PathToMenu.exe",
NORMAL_PRIORITY_CLASS |CREATE_UNICODE_ENVIRONMENT,
"WinSta0\\Default", hd, envBlock, "Menu");
Can anyone tell me what I'm doing wrong?
Tons of things can go wrong when trying to launch a process from a service in Vista/7. I would recommend that you start with this article and adapt it to your needs. I can tell you that I've used and modified the code in the article quite a bit, and it works. I'm sorry I can't show it to you because the modified code belongs to my company.

copy a file using UNC and impersonation

I want to use FileInfo and CopyTo to move some files across a network. I have to move the files to a share on a server that has to be accessed using a particular user account. How do I do this - do I have to impersonate that user and then do the copy?
I am using .net 4 and was wondering what the best way accomplish the impersonation is. I've read about using pInvoke and using advapi32.dll, but I was hoping someone could recommend a better way to do this.
Thanks for any thoughts.
EDIT
Thanks for the replies. This is not a service, it is a console app, but it will be run from multiple machines. Is there any advantage to using a mapping over using impersonation, or vice-versa? what is the recommended approach?
I have also considered using a batch file to create the mappings and do the copy, but I wasn't sure how easily it would be to accomplish because the folders to copy from will not always be the same - it will always be within one directory, but the subdirectory name changes.
You don't need impersonation all you have to do is to establish a file mapping using the credentials you want to use. You can accomplish this using either net use as a shell out command or WNetAddConnection2
If you are running as a service, you may need to impersonate. This is not the complete code but the gist of it:
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern unsafe int FormatMessage(int dwFlags, ref IntPtr lpSource, int dwMessageId, int dwLanguageId, ref string lpBuffer, int nSize, IntPtr* arguments);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool CloseHandle(IntPtr handle);
IntPtr token = IntPtr.Zero;
bool isSuccess = LogonUser(username, domain, password, impersonationType, Logon32ProviderDefault, ref token);
if (!isSuccess)
{
RaiseLastError();
}
WindowsIdentity newIdentity = new WindowsIdentity(token);
WindowsImpersonationContext impersonatedUser = newIdentity.Impersonate();
Save the token and then later
CloseHandle(token);

Impersonating a user in wrong domain doesn't throw exception

I used the common impersonation code and it worked just fine, until I inserted random 'dggdgsdg' in domain - and it worked nonetheless...
if (LogonUser(Username, Domain, Password, Logon32LogonInteractive, Logon32ProviderDefault, ref existingTokenHandle) &&
DuplicateToken(existingTokenHandle, (int)SecurityImpersonationLevel.SecurityDelegation, ref duplicateTokenHandle))
{
Identity = new WindowsIdentity(duplicateTokenHandle);
ImpersonationContext = Identity.Impersonate();
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
I used some TestUser on my domain, and it worked. I then switched domain, to random nonsense 'werwerhrg', and it impersonated the TestUser on my domain! Why? I would expect an exception to be thrown, why on earth is it working?
private const int Logon32LogonInteractive = 2;
private const int Logon32ProviderDefault = 0;
public enum SecurityImpersonationLevel
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3
}
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private extern static bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool DuplicateToken(IntPtr existingTokenHandle, int securityImpersonationLevel, ref IntPtr duplicateTokenHandle);
I believe the answer lies in how authentication is performed. LogonUser will attempt to log you on to the local computer it was executed on. If this computer is on a domain, then your password will be checked against an AD controller and be verified.
If you however provide a domain it cannot find it will authenticate against it's own local userbase as a fallback. Locally it will use NTLM (NTLM is used when client and server are the same machine). NTLM verifies password hashes and the username, and seems to not bother with the domain name (ref doc).
If you use UPN format instead and set domain to null then you will get an error if the domain is non-existant, and get your wanted result.
This is similar as if I create a user A with password B on two machines, then those users can access each others machines with the local rights without having to log on.
And all this is a good reason why you should stay away from local accounts in a domain world (at least with the same usernames, prefix them), and why non-domain machines should not be able to see each other on a network. And why you should use Kerberos all over if you can instead of NTLM.

Need to use win32 api to copy files in c# - how do I do this?

I'm trying to copy some files around, and occasionally the lengths of the names exceeds the length that the System.IO.File.Copy method can accept (260 chars according to the exception that is getting thrown)
According to the research I've done, I should be able to use the win32 api's file methods in conjunction with \?\ prepended to paths to get a 32,000 character limit, but I'm not sure witch methods I need to import.
Can someone help me with this? I'm looking for something like (obviously a different function, but you get the idea):
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern SafeFileHandle CreateFileW(string lpFileName, uint dwDesiredAccess,
uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll",
CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CopyFile(
[MarshalAs(UnmanagedType.LPWStr)] string lpExistingFileName,
[MarshalAs(UnmanagedType.LPWStr)] string lpNewFileName,
[MarshalAs(UnmanagedType.Bool)] bool bFailIfExists);
http://www.pinvoke.net/default.aspx/kernel32/CopyFile.html
Try using CopyFile.
For PInvoke syntax, you can check pinvoke.net.

Categories