Unable to get the Current User's Token information - c#

I have been trying to get the currently logged-in user's token information using the following code :
[DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool WTSQueryUserToken(int sessionId, out IntPtr tokenHandle);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern uint WTSGetActiveConsoleSessionId();
static void Main(string[] args)
{
try
{
int sessionID = (int)WTSGetActiveConsoleSessionId();
if (sessionID != -1)
{
System.IntPtr currentToken = IntPtr.Zero;
bool bRet = WTSQueryUserToken(sessionID, out currentToken);
Console.WriteLine("bRet : " + bRet.ToString());
}
}
catch (Exception)
{
}
}
The problem is that, bRet is always false and currentToken is always 0.
I am getting the sessionid as 1.
Could someone tell me what's going wrong here?
I want to use this token information to pass it to the CreateProcessAsUser function from a windows service.
Thanks,
Ram

Are you running this code as a Windows Service or just in a normal Console or WinForms application? WTSQueryUSerToken can only be called by an application that is running under the LocalSystem account, which could be achieved by running this as a Windows Service.

Related

Starting processes under specific credentials from a Windows service

I've spent a few days on this problem and even though there are tons of different examples online it's a tricky problem and I can't get them to work in my scenario.
I have a Windows service that runs under the Local System account. It has a WCF endpoint listening to API requests. When told via the API, the service is supposed to start a new process in the System session (0) and with the "Worker" account credentials. The process is a worker that checks for work in a queue and does it. If it does not find work, it will sleep for a bit and check again. If it does find work, it starts a new process in the same session and with the same credentials and does the work. After it's done the work it closes.
The "Worker" is a domain account and a member of the local administrators group on the machine, which has execute permissions on the executable. The machine is on the same domain as the account.
The problem is that when the service tries to start the process it gets a ERROR_ACCESS_DENIED (5) error code from the CreateProcessAsUser method.
I tried running the same code on a Windows 7 machine with the same credentials and it works fine, but it gets that error code when running on Windows Server 2008.
The code's too big to show here, so I've put it elsewhere...
ProcessHelper: http://pastie.org/private/y7idu3nw4xv1fxzeizbn9g
The service calls the StartAsUserFromService method to start the process, which internally calls CreateProcessAsUser after establishing a session. The process calls the StartAsUserFromApplication method to start its successor, which internally calls CreateProcessWithLogonW.
ImpersonationContext: http://pastie.org/private/xppc7wnoidajmpq8h8sg
The service needs to get the user token to start a process as them. The process doesn't need that to start its successor. As far as I can tell the impersonation is successful on Server 2008, but it doesn't have some permissions, and I can't figure out which.
EDIT:
I tried both a local administrator account and a domain account on the Windows 7 machine, and they work fine. But neither of them work on the Server 2008 machine. There must be a permission missing somewhere, but I don't know where; the error message isn't helpful.
I also tried ticking the "run as administrator" box in the compatibility tab of the executable, but it made no difference.
EDIT:
I used process monitor to see what's going on in the service and this is where it's getting the error...
Date & Time: 12/02/2014 11:44:03
Event Class: File System
Operation: CreateFile
Result: ACCESS DENIED
Path: D:\..\executable.exe
TID: 6244
Duration: 0.0000450
Desired Access: Read Data/List Directory, Execute/Traverse, Read Attributes, Synchronize
Disposition: Open
Options: Synchronous IO Non-Alert, Non-Directory File
Attributes: n/a
ShareMode: Read, Delete
AllocationSize: n/a
Impersonating: Domain\Worker
and
Date & Time: 12/02/2014 11:44:03
Event Class: File System
Operation: CreateFile
Result: ACCESS DENIED
Path: D:\..\executable.exe
TID: 6244
Duration: 0.0000480
Desired Access: Execute/Traverse, Synchronize
Disposition: Open
Options: Synchronous IO Non-Alert, Non-Directory File
Attributes: n/a
ShareMode: Read, Delete
AllocationSize: n/a
Impersonating: Domain\Worker
Some tips :
How to Impersonate
Impersonation code in C#
Impersonation Libraries (Class & Com Class)
WindowsIdentity.Impersonate Method
Try using this sample (found this somewhere) :
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;
[assembly:SecurityPermissionAttribute(SecurityAction.RequestMinimum,UnmanagedCode=true)]
[assembly:PermissionSetAttribute(SecurityAction.RequestMinimum, Name ="FullTrust")]
public class ImpersonationDemo
{
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int Length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
public enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
public enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation
}
[DllImport("advapi32.dll", CharSet = CharSet.Auto, 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)]
private unsafe static extern int FormatMessage(int dwFlags, ref IntPtr lpSource, int dwMessageId, int dwLanguageId, ref String lpBuffer, int nSize, IntPtr *Arguments);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError=true)]
public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateTokenEx( IntPtr hExistingToken, uint dwDesiredAccess, ref SECURITY_ATTRIBUTES lpTokenAttributes,
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
TOKEN_TYPE TokenType,
out IntPtr phNewToken);
// GetErrorMessage formats and returns an error message
// corresponding to the input errorCode.
public unsafe static string GetErrorMessage(int errorCode)
{
int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
int messageSize = 255;
String lpMsgBuf = "";
int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
IntPtr ptrlpSource = IntPtr.Zero;
IntPtr prtArguments = IntPtr.Zero;
int retVal = FormatMessage(dwFlags, ref ptrlpSource, errorCode, 0, ref lpMsgBuf, messageSize, &prtArguments);
if (0 == retVal)
{
throw new Exception("Failed to format message for error code " + errorCode + ". ");
}
return lpMsgBuf;
}
// Test harness.
// If you incorporate this code into a DLL, be sure to demand FullTrust.
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public static void Main(string[] args)
{
IntPtr tokenHandle = new IntPtr(0);
IntPtr dupeTokenHandle = new IntPtr(0);
try
{
string UserName, MachineName;
// Get the user token for the specified user, machine, and password using the
// unmanaged LogonUser method.
Console.Write("Enter the name of a machine on which to log on: ");
MachineName = Console.ReadLine();
Console.Write("Enter the login of a user on {0} that you wish to impersonate: ", MachineName);
UserName = Console.ReadLine();
Console.Write("Enter the password for {0}: ", UserName);
const int LOGON32_PROVIDER_DEFAULT = 3;
//This parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 8;
tokenHandle = IntPtr.Zero;
dupeTokenHandle = IntPtr.Zero;
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(UserName, MachineName, "mm4geata",
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref tokenHandle);
Console.WriteLine("LogonUser called.");
if (false == returnValue)
{
int ret = Marshal.GetLastWin32Error();
Console.WriteLine("LogonUser failed with error code : {0}", ret);
Console.WriteLine("\nError: [{0}] {1}\n", ret, GetErrorMessage(ret));
return;
}
Console.WriteLine("Did LogonUser Succeed? " + (returnValue? "Yes" : "No"));
Console.WriteLine("Value of Windows NT token: " + tokenHandle);
// Check the identity.
Console.WriteLine("Before impersonation: " + WindowsIdentity.GetCurrent().Name);
//bool retVal = DuplicateToken(tokenHandle, SecurityImpersonation, ref dupeTokenHandle);
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.bInheritHandle = true;
sa.Length = Marshal.SizeOf(sa);
sa.lpSecurityDescriptor = (IntPtr)0;
bool retVal = DuplicateTokenEx(tokenHandle, 0x10000000, ref sa, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenImpersonation, out dupeTokenHandle);
if (false == retVal)
{
CloseHandle(tokenHandle);
Console.WriteLine("Exception thrown in trying to duplicate token.");
return;
}
// The token that is passed to the following constructor must
// be a primary token in order to use it for impersonation.
WindowsIdentity newId = new WindowsIdentity(dupeTokenHandle);
WindowsImpersonationContext impersonatedUser = newId.Impersonate();
// Check the identity.
Console.WriteLine("After impersonation: " + WindowsIdentity.GetCurrent().Name);
// Stop impersonating the user.
impersonatedUser.Undo();
// Check the identity.
Console.WriteLine("After Undo: " + WindowsIdentity.GetCurrent().Name);
// Free the tokens.
if (tokenHandle != IntPtr.Zero)
CloseHandle(tokenHandle);
if (dupeTokenHandle != IntPtr.Zero)
CloseHandle(dupeTokenHandle);
}
catch(Exception ex)
{
Console.WriteLine("Exception occurred. " + ex.Message);
}
}
}
I managed to make the processes start with this code:
ProcessHelper: http://pastie.org/private/dlkytj8rbigs8ixwtg
TokenImpersonationContext: http://pastie.org/private/nu3pvpghoea6pwwlvjuq
The service calls the StartAsUserFromService method, and the process calls the StartAsUserFromApplication method to start its successor.
I'm using LogonType.Batch in the LogonUser call because the process needs to talk to another WCF service and needs to authenticate. LogonType.Network and LogonType.NetworkClearText could be used, but caused permission issues in the Net.Tcp Port Sharing Service with the Worker user account.
This answer was helpful: Using Process.Start() to start a process as a different user from within a Windows Service

Why do I get ERROR_ACCESS_DENIED attempting to open a specific job using OpenPrinter?

According to undocprint given a job ID it should be possible to retrieve the spool file for the job using OpenPrinter and ReadPrinter by opening the printer using a string with format "PrinterName,Job xxxx". The MSDN documentation lists this method as well, though with an additional space after the comma "PrinterName, Job xxxx".
Whenever I try to call this method from my test application (using either string format) I get ERROR_ACCESS_DENIED (Windows 8 x64). Why is this and what do I need to do to get this working?
I'm running the test app as admin and have no trouble pausing jobs or printers or accessing other information.
I know the ID I'm using is valid because for an invalid ID it returns ERROR_INVALID_PRINTER_NAME instead.
The code I'm using:
public static void OpenPrinter(String printerName,
ref IntPtr printerHandle,
ref PRINTER_DEFAULTS defaults) {
if (OpenPrinter(printerName, ref printerHandle, ref defaults) == 0) {
throw new Win32Exception(Marshal.GetLastWin32Error(),
string.Format("Error getting access to printer: {0}", printerName));
}
}
[DllImport("winspool.drv", EntryPoint = "OpenPrinterW", SetLastError = true, CharSet = CharSet.Unicode,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern int OpenPrinter(String pPrinterName, ref IntPtr phPrinter, ref PRINTER_DEFAULTS pDefault);
[StructLayout(LayoutKind.Sequential)]
public struct PRINTER_DEFAULTS {
public IntPtr pDatatype;
public IntPtr pDevMode;
public uint DesiredAccess;
}
Turns out that pDefaults must be passed NULL and then everything works.
This requires changing the extern definition to take an IntPtr or similar.
I haven't seen any documentation about why this might be (in fact the MSDN docs state that this passing NULL should be the same as requesting USE access), but it definitely fixes the issue in our testing.
Permissions. Are you running with administrator rights?

windows service gets UnauthorizedAccessException on reading file from remote share while impersonating

I have a windows service that runs under the local machine system account. Inside this service, it tries to read a remote .ini file that is available on a remote shared folder. The code trying to read this file is wrapped in impersonation using LogonUser (a simplified version of the code is below for an idea of what it is doing). The impersonation successfully starts impersonating the user configured, however the instant it attempts to read the remote ini file found on the remote network share, an UnauthorizedAccessException is thrown. This happens even though the configured user has read/write permissions on the remote machine. When I modify the code to remove all impersonation, and instead run the windows service as the configured user, all access to the remote .ini file are successful. I would prefer to use impersonation to get access to this file rather than a hack such as running the service as the user. Can anyone see errors with the impersonation code in the example? Is there something I need to do on my Vista 64 bit box to enable this? Are there specific active directory permissions my IT co-workers need to enable?
private WindowsImpersonationContext _impersonatedUser;
private IntPtr _token;
// Declare signatures for Win32 LogonUser and CloseHandle APIs
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(
string principal,
string authority,
string password,
LogonSessionType logonType,
LogonProvider logonProvider,
out IntPtr token);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);
enum LogonSessionType : uint
{
Interactive = 2,
Network,
Batch,
Service,
NetworkCleartext = 8,
NewCredentials
}
enum LogonProvider : uint
{
Default = 0, // default for platform (use this!)
WinNT35, // sends smoke signals to authority
WinNT40, // uses NTLM
WinNT50 // negotiates Kerb or NTLM
}
....
var result = LogonUser(exchangeUserId, exchangeDomain,
password,
LogonSessionType.Network,
LogonProvider.Default,
out _token);
var id = new WindowsIdentity(_token);
_impersonatedUser = id.Impersonate();
try
{
//Validate access to the file on the remote computer/share
File.GetAccessControl(remoteFileAvailableInSharedFolder);
}
catch (UnauthorizedAccessException unauthorized)
{
...
}
....
// Stop impersonation and revert to the process identity
if (_impersonatedUser != null)
{
_impersonatedUser.Undo();
_impersonatedUser = null;
}
if (_token != IntPtr.Zero)
{
CloseHandle(_token);
_token = IntPtr.Zero;
}
Doing further research I discovered the core issue to be caused by the impersonation code. I had to add the use of the api DuplicateToken and added usage of the api RevertToSelf too. On making these changes (see class below) the impersonation worked as well as the code did when the whole service was ran under my domain user. Hope the code helps others with this same issue.
public class Impersonation : IDisposable
{
private WindowsImpersonationContext _impersonatedUserContext;
// Declare signatures for Win32 LogonUser and CloseHandle APIs
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(
string principal,
string authority,
string password,
LogonSessionType logonType,
LogonProvider logonProvider,
out IntPtr token);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool RevertToSelf();
// ReSharper disable UnusedMember.Local
enum LogonSessionType : uint
{
Interactive = 2,
Network,
Batch,
Service,
NetworkCleartext = 8,
NewCredentials
}
// ReSharper disable InconsistentNaming
enum LogonProvider : uint
{
Default = 0, // default for platform (use this!)
WinNT35, // sends smoke signals to authority
WinNT40, // uses NTLM
WinNT50 // negotiates Kerb or NTLM
}
// ReSharper restore InconsistentNaming
// ReSharper restore UnusedMember.Local
/// <summary>
/// Class to allow running a segment of code under a given user login context
/// </summary>
/// <param name="user">domain\user</param>
/// <param name="password">user's domain password</param>
public Impersonation(string user, string password)
{
var token = ValidateParametersAndGetFirstLoginToken(user, password);
var duplicateToken = IntPtr.Zero;
try
{
if (DuplicateToken(token, 2, ref duplicateToken) == 0)
{
throw new Exception("DuplicateToken call to reset permissions for this token failed");
}
var identityForLoggedOnUser = new WindowsIdentity(duplicateToken);
_impersonatedUserContext = identityForLoggedOnUser.Impersonate();
if (_impersonatedUserContext == null)
{
throw new Exception("WindowsIdentity.Impersonate() failed");
}
}
finally
{
if (token != IntPtr.Zero)
CloseHandle(token);
if (duplicateToken != IntPtr.Zero)
CloseHandle(duplicateToken);
}
}
private static IntPtr ValidateParametersAndGetFirstLoginToken(string user, string password)
{
if (string.IsNullOrEmpty(user))
{
throw new ConfigurationErrorsException("No user passed into impersonation class");
}
var userHaves = user.Split('\\');
if (userHaves.Length != 2)
{
throw new ConfigurationErrorsException("User must be formatted as follows: domain\\user");
}
if (!RevertToSelf())
{
throw new Exception("RevertToSelf call to remove any prior impersonations failed");
}
IntPtr token;
var result = LogonUser(userHaves[1], userHaves[0],
password,
LogonSessionType.Interactive,
LogonProvider.Default,
out token);
if (!result)
{
throw new ConfigurationErrorsException("Logon for user " + user + " failed.");
}
return token;
}
public void Dispose()
{
// Stop impersonation and revert to the process identity
if (_impersonatedUserContext != null)
{
_impersonatedUserContext.Undo();
_impersonatedUserContext = null;
}
}
}

C# Get active url from Active Process

Greetings
I'm using the following code to get the active process.
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
public extern short GetKeyState(int keyCode);
[DllImport("user32.dll")]
private extern Int32 GetWindowThreadProcessId(
IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll")]
private extern IntPtr GetForegroundWindow();
private Process GetProcessByHandle(IntPtr hwnd)
{
try
{
uint processID;
GetWindowThreadProcessId(hwnd, out processID);
return Process.GetProcessById((int)processID);
}
catch { return null; }
}
private Process GetActiveProcess()
{
IntPtr hwnd = GetForegroundWindow();
return hwnd != null ? GetProcessByHandle(hwnd) : null;
}
I was wondering if with this, or any other code, I could get the active URL / Tab of any webbrowser when GetActiveProcess returns a webbrowser as active process?
Greetings
Found my answer at Retrieve current URL from C# windows forms application . Which does get the url for google chrome (My most used broswer) but fails at internet explorer.
If anyone wishes to help on that it would be lovely.. but as I have it now it's good enough!

How to get the name of an External window in C# Application?

i've developed a simple application (.dll) in LABVIEW and i implorted that dll to a C# windows application(Winforms) . Like
[DllImport(#".\sample.dll")]
public static extern void MyFunc(char[] a, StringBuilder b ,Int32 c);
so when i call the function MyFunc a window will be popped up( the Lab View window( Front panel of my labview application
i need to get the window name (ExpectedFuncName) in my C# application. i.e i need to get the name of the external window which is opend by my C# application. Can we use FileVersionInfo or assembly loader to get the name?
Is there any idea to do this?
Thanks in advance.
If you have the window handle, this is relatively easy:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern int GetWindowTextLength(IntPtr hWnd);
...
int len;
// Window caption
if ((len = GetWindowTextLength(WindowHandle)) > 0) {
sb = new StringBuilder(len + 1);
if (GetWindowText(WindowHandle, sb, sb.Capacity) == 0)
throw new Exception(String.Format("unable to obtain window caption, error code {0}", Marshal.GetLastWin32Error()));
Caption = sb.ToString();
}
Here, 'WindowHandle' is the handle of the created window.
In the case you do not have a window handle (I see you don't), you have to enumerate every desktop top-level window, filter them by the creating process (I see the window is created by you application by calling MyFunc, so you know the process ID [*]), and then use some heuristic to determine the required information.
Here is the C# import of the functions you shall use in the case you do not have the handle:
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
Basically EnumWindows calls EnumWindowsProc for each window found in the current desktop. So you can get the window caption.
List<string> WindowLabels = new List<string>();
string GetWindowCaption(IntPtr hWnd) { ... }
bool MyEnumWindowsProc(IntPtr hWnd, IntPtr lParam) {
int pid;
GetWindowThreadProcessId(hWnd, out pid);
if (pid == Process.GetCurrentProcess().Id) {
// Window created by this process -- Starts heuristic
string caption = GetWindowCaption(hWnd);
if (caption != "MyKnownMainWindowCaption") {
WindowLabels.Add(caption);
}
}
return (true);
}
void DetectWindowCaptions() {
EnumWindows(MyEnumWindowsProc, IntPtr.Zero);
foreach (string s in WindowLabels) {
Console.WriteLine(s);
}
}
[*] In the case the window is not created by your application (i.e but from another background process), you shall filter the values returned by GetWindowThreadProcessId using another process ID, but this requires another question...
If you activate LabVIEW scripting (LabVIEW 2010), or install it (LV 8.6, 2009) there is a front-panel property called 'FP.nativewindow'. This returns a handle to the front panel window.
Use the following snippet to get the property:

Categories

Resources