I'm using the following API call to determine the name of the remote client in a Terminal Server session:
ret = WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION,
WTS_INFO_CLASS.WTSClientName, buffer, bufferLen)
Usually, when the application is not running in a terminal server session, the call succeeds (ret != 0) and buffer just returns a null pointer. So I figured that this was the expected behavior.
On one customer's machine, however, the function started to fail (ret == 0) and GetLastError returns 5: Access Denied. (As usual, the customer claims that his system configuration did not change in any way.)
So, my questions are the following:
What is the expected behavior when querying the WTSClientName on a non-terminal-services machine (such as, e.g., a "regular" Windows 7 PC)?
What could be the reason that this error code is returned? Note that, on our test machines, the function call always behaves as expected (success and empty buffer), even if the user does not have administrative rights.
I don't know if this is relevant, but we are able to reproduce this problem (error 5) by calling the function from inside a web application. This, however, is not the case in this case (it's a regular Windows application).
I agree with you and also suppose that some from system configuration at the client are changed. To find out which one from the changes in file system or in registry are important for WTSQuerySessionInformation you can try to use Process Monitor.
You can also ask your customer to download the tool, start the Process Monitor and then start your test program used WTSQuerySessionInformation, save the log and post it to you. I suppose, that you will see some "access denied" errors in the log. I hope it will help you to find the problem in the configuration of the client's computer.
I don't know the expected behavior but I encountered error code 5, access denied, when calling WTSEnumerateSessions on Win7. Set DWORD HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\AllowRemoteRPC to 1 and issue was immediately fixed.
This is happening when you call it from web application becasue the web server is running under the (session 0) of windows.
read more about session 0
Related
Operating system is Windows 7 or higher with UAC enabled. Calling process has admin rights, already confirmed by the UAC box.
I want to spawn a new Console window (cmd.exe) under user SYSTEM (don't ask why). I can do this interactively by using PsExec tool from Sysinternals or something similar, but I don't have the source code and I need to understand how this works.
I understand that I have to call CreateProcessAsUser() and that works fine with the first parameter (hToken) set to NULL, but now I need to know how to get the hToken. I understand that I can get such a token by calling LogonUser() - but not for SYSTEM. How would I get the token for SYSTEM?
I thought of using DuplicateTokenEx(), but that requires an original token, that I don't have.
Would I have to query the process list, find any SYSTEM process and try to get that token duplicated or something? I don't want to reverse engineer the PsExec tool or one of the others doing exactly this.
Typically you would install and launch a service, configured to log in as SYSTEM. You can then use OpenProcessToken and DuplicateTokenEx to make a copy of the token.
You will probably need to use SetTokenInformation to change the session ID for the token to match that of the interactive user. You need Act As Part Of the Operating System privilege to do that, so you should do this from inside the service itself. Once the duplicate token is ready to use, you can use DuplicateHandle to copy the handle into the administrative process, or (with the right options) you could launch the command shell directly from the service too.
alternative open the winlogon process with maximum permitted access, try to open the process token, (also with maximum permitted) and then try to duplicate this winlogon handle with impersonate rights. On win8.1 this will succeed. On others, you will need to temporary change the token dacl, with either a null or your own admin process token
EDIT:
The ERROR_PARTIAL_COPY error was not caused by the call to GetBinaryType() but by an earlier call to EnumProcessModules(), that was attempting to acquire the full path of the executable for a process to pass to GetBinaryType(), that failed. There was exception chaining occurring which I missed and that set the error message of the exception to the same error message in the original post but retained the OS error code of ERROR_PARTIAL_COPY.
This function is called from within a 32-bit Windows Service. I have searched the internet and stackoverflow and found no other instance where GetBinaryType() failed and GetLastError() returned ERROR_PARTIAL_COPY:
Only part of a ReadProcessMemory or WriteProcessMemory request was completed.
A possibility I considered was that ERROR_PARTIAL_COPY was being set by an earlier WINAPI call and GetBinaryType() does not SetLastError() correctly. I attempted to reproduce this by:
executing it with the path of a file that does not exist
executing it with the path of a non-executable file
executing it with the path of a file that denies access
with no success (I called SetLastError(ERROR_PARTIAL_COPY); prior to each of the attempts): each of these attempts produced the expected result.
The code snippet is:
std::string full_exe_path =
a_impl->exe_installation_dir + "\\" + a_impl->exe_name;
DWORD bin_type;
if (FALSE == GetBinaryType(full_exe_path.c_str(), &bin_type))
{
throw Base_exception(
__LINE__,
__FILE__,
"Failed to get binary type for " + a_impl->exe_name);
// Optional argument here that defaults to GetLastError()
}
Does anyone know, or have any suggestions, as to the cause of this?
Note: this is happening on a machine that I do not have access to (and works on over 99% of the machines on which the Windows Service is running)
I can't test this right now so the following is a bit speculative :-(
It is really hard to be sure what is causing what you see - AFAIK you need to check the following possibilities:
Windows Service
In which user/context is the service running ?
Since Windows Vista MS has applied several changes to improve security that changed behaviour for example when accessing a network share from a Windows Service!
Bitness
ERROR_PARTIAL_COPY can happen esp. when the bitness of your application differs from the bitness of the file you are checking.
.NET files (EXE/DLL)
GetBinaryType() can behave strangely if the file you are checking happens to be a .NET file.
Virus scanner etc.
Any virus scanner etc. can interfer in strange ways with GetBinaryType().
rootkit infection and similar
This can give really some strange results since no rootkit is perfect...
virtual machine
IF the Windows Service is hosted inside a virtual machine I had several strange experiences with network access.
Another point you could check is whether the behaviour is the same when running this code from a "normal application" - this could give you a clue whether the "Windows Service/Network share" has anything to do with it.
Also check whether you can open the respective file (readonly, no locks) and what GetFileType() returns in that case.
I have a line of code that I can run locally as part of a service that works perfectly fine.
sReportPath = objCrystalUtils.ExportReportToPDF("Report Name", iReportInfoID)
This code is run as a part of a service, and when I unit test it by feeding it data, it ultimately builds the report and prints it.
When I run the exact same piece of code inside an .ashx from an ajax call. The reports are generated (I can see the pdf files being created on disk) but the printing is not happening.
oRpt.PrintToPrinter(objReport.DefaultAutoPrint, True, 0, 0)
In both scenarios the same code is used to print the report. (objReport.DefaultAutoPrint = 0 in both cases)
My only thought is that the location of the code that is calling this method is in a different spot relative to the location of the bills themselves.
The printer that I'm trying to print to is a network printer intalled on my machine, and I'm running Windows 7 IIS 6.1
Any thoughts?
Edit:
Here is a thought... if I'm running one as a unit test locally and im running the other through a web app that is running via IIS, is there a difference in user id and user access to the default printer?
Edit:
So I added my local ASP, IUSR and SYSTEM users to the printer security and allowed them to print... no dice. So I checked the EVERYONE user and it is set to access and NO users are denied... so I think that kinda kills that line of reasoning.
Edit:
I changed the name of this post since I no longer think that the issue is ajax related since If I try to do the same process in code bebehind from a post back instead of running it from an ajax call i still get the same problem.
Patrick, for me it is a known issue of crystal reports, printing a certain report from a running application via IIS.
I got the same issue before, and upon our search for that issue, we got the following;
Report to be generated, exported, and then to be downloaded to client machine,
so user can print it locally (say, report will be exported as PDf file,
user can use print option of PDF reader).
It's not Crystal Reports or other third party app's problem. It's usually the IIS_IUSER's permission problem because it has no access to any network printers. A possible solution is in Process.Start doesn't work in IIS
I have a Windows executable that is launched from within a service by calling CreateProcessWithLogonW() with a set of specfied user details.
This works fine and the process starts as expected. However, when this process tries to launch other processes itself, currently just using CreateProcess() these start then die straight away - they are executables that require desktop access.
After reading up on Microsoft's article on CreateProcess() - http://msdn.microsoft.com/en-us/library/ms682425(VS.85).aspx
I think can see why this is happening and it makes sense to an extent. CreateProcess() knows the calling process is impersonating a user so it uses it's parent process, which in this case is the Local System account. But of course anything run in the local system account doesn't have the access we need, so the launched process dies.
Oddly when I was previously using LogonUser() and CreateProcessAsUser() to launch the initial executable within the service, it worked fine. But I had to change this to CreateProcessWithLogonW() due to problems with not having the correct privileges.
Does anybody know of a solution to this? I've seen talk about this elsewhere on the web but not with any definite solution. It seems like I possibly need the token of the user i'm logging on with in CreateProcessWithLogonW() with so I can use it to launch the other processes later? But I have no way of getting hold of this token, can this be retreived for the current user in any way?
Any help would be greatly appreciated, thanks :)
We solved the problem using some code that I found long-ago. The "copyright" section of one of the source modules contains the following:
/////////////////////////////////////////////////////////////
// CreateProcessAsUser.cpp
//
// Written by Valery Pryamikov (1999)
//
// Command line utility that executes a command under specified user identity
// by temporarily installing itself as a service.
//
// Based on Keith Brown's AsLocalSystem utility (http://www.develop.com/kbrown)
// Uses some code from Mike Nelson's dcomperm sample utility
// and from tlist sample (Microsoft Source Code Samples)
//
// Use:
// CreateProcessAsUser.exe [-i[nteractive]]|[-s[ystem]]|
// [-u"UserName" -d"DomainName" -p"Password"]|[-a"AppID"] command
// Command must begin with the process (path to the exe file) to launch
// -i process will be launched under credentials of the
// "Interactive User" (retrieved from winlogon\shell process)
// -a process will be launched under credentials of the user
// specified in "RunAs" parameter of AppID.
// -s process will be launched as local system
// -u -d -p process will be launched on the result token of the
// LogonUser(userName,domainName,password,LOGON32_LOGON_BATCH...)
//
// either (-s) or (-i) or (-a) or (-u -d -p) parameters must supplied
//
// Examples:
// CreateProcessAsUser -s cmd.exe
// CreateProcessAsUser -a"{731A63AF-2990-11D1-B12E-00C04FC2F56F}" winfile.exe
//
/////////////////////////////////////////////////////////////
Perhaps this information will yield hits within your Google searches - I attempted a few quick attempts but came up empty-handed.
We decomposed the internals into a set of API that yielded the results we needed.
Do you own the code launched using CreateProcessWithLogonW (and which in turn calls CreateProcess)? If you do not then you might need to perform IAT (or API) hooking on it (i.e. at run-time), as to substitute any calls to CreateProcess with an appropriate procedure that also uses CreateProcessWithLogonW or CreateProcessWithTokenW. See APIHijack, Detours.
After this is done, the child process may require access to HKCU. If you are not already doing this, you should load the profile of each impersonated user, once per user, before calling CreateProcessWithLogonW.
By default, CreateProcessWithLogonW
does not load the specified user
profile into the HKEY_USERS registry
key. This means that access to
information in the HKEY_CURRENT_USER
registry key may not produce results
that are consistent with a normal
interactive logon. It is your
responsibility to load the user
registry hive into HKEY_USERS before
calling CreateProcessWithLogonW, by
using LOGON_WITH_PROFILE, or by
calling the LoadUserProfile function.
Isn't there an option for services to allow them to interact with the desktop? If setting that option for your service is a possibility, that would probably be the simplest solution.
I'm assuming that this process is a service; that isn't specified in the question, but seems logical given that it is running as Local System account.
Where you're getting stuck isn't in CreateProcess, It's in CreateService. If you want your service to be able to interact with the desktop, you have to specify SERVICE_INTERACTIVE_PROCESS as one of the flags to the argument dwServiceType. This setting is inherited by child processes of the service.
You can also modify an existing service's setting by using the Services tool, select Properties for the service, click on the "Log On" tab, and select the check box "Allow service to interact with desktop".
In Windows Server 2003, how I can check if my program is running in local console ("on the screen of the server machine") instead of remote session?
I'm using Delphi Win32, so any Windows API based method should work..
Wouldn't the session number tell you this ?
ProcessIdToSessionId (GetCurrentProcessId(),&dwSessionNum)
You'd have to check the OS version as well, using GetVersionEx: for everything up to XP/Server 2003 session 0 is local (service or interactive console), anything higher is virtual. For Vista/2008 session 0 and 1 are both local (0 is service, 1 is console), everything else is virtual.
I'm guessing your Delphi units would declare the session number as var, so you wouldn't need the ampersand.
WTSGetActiveConsoleSessionId() should return the ID of the session attached to the console. You can then compare that session id with your application's current session ID to determine whether you are running on the console or not. Vista (not sure about Windows Server 2008) does not necessarily give the console session the ID of 1 (Fast User Switching, anyone?).
For me, ProcessIdToSessionId returned 0 both when run directly at the physical console and when logged in to the administrative session (mstsc /admin).
However, when you login via RDP, Windows (XP Pro in this case) creates a new session which it shows on the physical console which just has the "this computer is locked" display. WTSGetActiveConsoleSessionId returns the session id for that second session which in my case was 2.
So even though your app is running on the console, there are now two console sessions and your app is not running on the active one. In my code I compare session id against 0 instead.
Brian is correct, I have since encountered Vista reporting a session id of 2 for an interactive console, despite the fact that Fast User Switching was not in use. Of course, this may be just be a bug :-)