How to determine whether an LSA session is active in Windows XP - c++

I'm trying to get the list of users currently logged into a machine.
On Windows 7, I can call LsaEnumerateLogonSessions, then WTSQuerySessionInformation with WTSConnectState.
But on XP, each LSA session has 0 for the TS Session field (unless it's a Remote Desktop session), which always has WTSConnectState of WTSActive, and I end up listing all of the people who have logged out of the machine already. WTSQuerySessionInformation fails when the session is a Remote Desktop session.

I believe this codeproject article uses a workaround that might be what you are after, it enumerates all running processes, checking the AuthenticationId (TokenStatistics on the process token) against the list of LUID's LsaEnumerateLogonSessions gives you. This allows you to filter out stale LUID's

Have you Tried the WMI class Win32_loggedOnUser.

I think with
I'm trying to get the list of users currently logged into a machine.
you want to enumerate all users that are logged on interactively, i.e. have a desktop and such. So it is the physical console and the RDP connections.
So you need to distinguish between "interactively logged on" and "logged on" because of LogonUser was called.
With LogonUser you can spawn a process into a session of an interactive user or start a batch process. LSA returns all these users.
Use WTSEnumerateSessions and check if the session is in a state you need it to be.

Related

Detect if Windows device is locked while running as Local System account

Is is usually possible to check if a desktop is locked by using the SHQueryUserNotificationState API, but when running as LocalSystem, the state is not correctly detected.
Is anyone aware of any workarounds or alternative APIs that could be used to detect if the device is locked?
SHQueryUserNotificationState() queries the state of the desktop session of the calling user. But multiple users can be logged in at a time. So you will have to query the specific user session you are interested in.
You can use WTEnumerateSessions() to see which user sessions are running, and then use WTSQuerySessionInformation(WTSSessionInfoEx) to query the desired user session for its WTSINFOEX_LEVEL1 session info, which has a SessionFlags member that will be set to WTS_SESSIONSTATE_LOCK if the session is locked.
Alternatively, you can have your app track session locks/unlocks in real-time by listening for WM_WTSSESSION_CHANGE messages from WTSRegisterSessionNotification(). Or, you can write a separate background service that listens for SERVICE_CONTROL_SESSIONCHANGE notifications and then notifies your main app as needed via an IPC mechanism of you choosing.

How to tell that the logon screen is currently displayed?

I am writing a service application that will run with local system credentials. I will need to know from my service if the Windows logon screen is displayed at any particular time. Is there any way to do this?
PS. The screens that can be brought up by locking the workstation:
Or by trying to switch the user:
Or after a Ctrl+Alt+Del:
PS. I need this to run on Windows XP and up.
EDIT: The only viable solution that I came up with so far is to see if LogonUI.exe process is running. The issue with this approach is how to distinguish between the actual system logon process and any other process that has that image name?
As described in the comments you are trying to detect whether or not a process in an interactive desktop session should show a message box. There being no point doing so if the interactive session is not active.
In which case I believe that your proposed solution is the wrong one. Instead you should register for session change notifications by calling WTSRegisterSessionNotification. When you do this you'll get sent WM_WTSSESSION_CHANGE messages that allow you to keep track of the current state.
Note that you do this in your desktop app rather than the service. The service still sends its messages to the desktop app. But the desktop app now knows whether or not it is worth showing them.
Update
Remy suggests a better way in the comments:
And if a separate app is being used, there is no reason to detect session changes at all, that app can simply check if its currently assigned workstation/desktop is the currently interactive workstation/desktop instead, comparing GetThreadDesktop() to OpenInputDesktop(), for instance.
All such screens are presented on a separate desktop. You may try to enumerate the user's desktops and compare it with the current (I am not sure the service in session 0 - Vista and up - can do that; if not, spawn a helper process in the user session). This however may give a false positive if an UAC desktop is up. Another corner case is a userless situation (right after boot before any user looged on).
There are several states in the windows.
Logged-Off State
When Winlogon is in the logged-off state, users are prompted to identify themselves and provide authentication information. If a user provides correct user account information and no restrictions prevent it, the user is logged on and a shell program (such as Windows Explorer) is executed in the application desktop. Winlogon changes to the logged-on state.
Logged-On State
When Winlogon is in the logged-on state, users can interact with the shell, activate additional applications, and do their work. From the logged-on state, users can either stop all work and log off, or lock their workstations (leaving all work in place). If the user decides to log off, Winlogon will terminate all processes associated with that logon session and the workstation will be available for another user. If, instead, the user decides to lock the workstation, Winlogon changes to the workstation-locked state.
Workstation-Locked State
When Winlogon is in the workstation-locked state, a secure desktop is displayed until the user unlocks the workstation by providing the same identification and authentication information as the user who originally logged on, or until an administrator forces a logoff. If the workstation is unlocked, the application desktop is displayed, and work can resume.
reference: https://msdn.microsoft.com/ko-kr/library/windows/desktop/aa380547(v=vs.85).aspx
p.s. registering a secure attention sequence (SAS, CTRL+ALT+Delete) is included in Workstation-Locked state
Similarly, there are several desktop types on windows.
Winlogon desktop
Application desktop(=Default desktop)
Screensaver desktop
Secure desktop
I recommend you read this:
https://msdn.microsoft.com/ko-kr/library/windows/desktop/aa375994(v=vs.85).aspx
I don't know my answers are what you want... but I hope it helps in some ways.

Restrict application to one instance per shell session on Windows

There are a lot of solutions for restricting an application from running twice. Searching by process name, using a named mutex etc. But I all of these methods don't work if I want to restrict my application to the shell session.
A user may have more than login session and shell on windows (right?)? If this is true I want to be able to run one instance of my application in every shell session but allow only one.
Is there a way to get a shell identifier which could then be put into the mutex name?
You can create local (session only) or global (whole system) mutexes. See http://msdn.microsoft.com/en-us/library/system.threading.mutex.aspx for more info. Look for global and local.
The shell identifier you want to use is the user name. This is available as Environment::UserName or GetUserName()
You can go from process id of the current process to a WTS session ID, I think that will do what you need. ProcessIdToSessionId
You should be aware that a terminal services session can be disconnected from one desktop and then connected to by another, so the 'shell' can actually change, but the session ID should remain the same.
If you want to restrict it to one instance per login session, even if the same user account has multiple login sessions running at the same time (Server/Terminal Server) you could use the handle from GetShellWindow to check if there is a instance running already for this desktop.

App started by logged off XP user can seen on desktop of a different user

This is a coding question. Please read it before you flag it as belonging on ServerFault as the last one I wrote got thrown over there in less than 5 minutes.
I'm testing my win32/c++ application on XP which has the latest service packs. It contains two administrative user accounts both without passwords. I log in as User1 and start my app. The app runs, its main window appears and all is well with the world. I then log User1 off without first closing my app. Yes, I used "log off" not "switch user"
I then log in as User2 and my application is still running. I see it on the User 2 desktop, and I can even interact with it. It appears to be functioning normally. And task manager shows it running as User1.
Any ideas which might be going on here? Other applications (like notepad) don't exhibit this issue, yet mine does. Seems to me I'm doing something wrong in my code, but it really is a rather standard win32/c++ app. Perhaps I'm not processing some shutdown message properly? I'm sorry I can't give more specifics right now. I'm really hoping for some clue to spark further research.
Check windows task manager's for 2 things:
"Session ID" column
"User Name" column
If either of these columns do not show up then select them from View -> Select columns.
Check which username and session your application that is staying open with is on. Then go and start notepad.exe and compare to the session ID and User Name that it is started with.
When you do a logoff it will close the applications running under your Session ID and username.
I'm guessing that your application is running in it's own session ID and/or username.
When you login with the other user it checks to see if it can re-use a session that is already started for the new username. So that is why you will see it running again when you login with the second user.
Are you sure your application isn't running as a service? A service with "Interact with Desktop" could look like this.
UPDATE:
It must be somehow related to a service. A normal application, running in a session will be forced to close by Windows before the logoff is complete. Even if you don't handle the end session messages, Windows will tell the user about the nonresponding process and/or just kill it.
Do you need to be listening for a shutdown or logoff events?
Check out this answer for a similar question.
That answer refers to listening for WM_QUERYENDSESSION.
See WM_QUERYENDSESSION Message

How can a Windows service execute a GUI application?

I have written a Windows service that allows me to remotely run and stop applications. These applications are run using CreateProcess, and this works for me because most of them only perform backend processing. Recently, I need to run applications that present GUI to the current log in user. How do I code in C++ to allow my service to locate the currently active desktop and run the GUI on it?
Roger Lipscombe's answer, to use WTSEnumerateSessions to find the right desktop, then CreateProcessAsUser to start the application on that desktop (you pass it the handle of the desktop as part of the STARTUPINFO structure) is correct.
However, I would strongly recommend against doing this. In some environments, such as Terminal Server hosts with many active users, determining which desktop is the 'active' one isn't easy, and may not even be possible.
But most importantly, if an application will suddenly appear on a user's desktop, this may very well occur at a bad time (either because the user simply isn't expecting it, or because you're trying to launch the app when the session isn't quite initialized yet, in the process of shutting down, or whatever).
A more conventional approach would be to put a shortcut to a small client app for your service in the global startup group. This app will then launch along with every user session, and can be used start other apps (if so desired) without any juggling of user credentials, sessions and/or desktops.
Also, this shortcut can be moved/disabled by administrators as desired, which will make deployment of your application much easier, since it doesn't deviate from the standards used by other Windows apps...
The short answer is "You don't", as opening a GUI program running under another user context is a security vulnerability commonly known as a Shatter Attack.
Take a look at this MSDN article: Interactive Services. It gives some options for a service to interact with a user.
In short you have these options:
Display a dialog box in the user's session using the WTSSendMessage function.
Create a separate hidden GUI application and use the CreateProcessAsUser function to run the application within the context of the interactive user. Design the GUI application to communicate with the service through some method of interprocess communication (IPC), for example, named pipes. The service communicates with the GUI application to tell it when to display the GUI. The application communicates the results of the user interaction back to the service so that the service can take the appropriate action. Note that IPC can expose your service interfaces over the network unless you use an appropriate access control list (ACL).
If this service runs on a multiuser system, add the application to the following key so that it is run in each session: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run. If the application uses named pipes for IPC, the server can distinguish between multiple user processes by giving each pipe a unique name based on the session ID.
WTSEnumerateSessions and CreateProcessAsUser.
Several people suggested WTSEnumerateSessions and CreateProcessAsUser. I wonder why no one suggested WTSGetActiveConsoleSessionId, since you said you only want to target one logged in user.
Several people sure are right to suggest CreateProcessAsUser though. If you call plain old CreateProcess the way you said, then the application's GUI will run with your service's privileges instead of the user's privileges.
That problems Session 0 , Interactive Services ,
Windows Service Allow Service To Interact With Desktop
on Windows 7 or Windows Vista
You can read this article
http://www.codeproject.com/KB/vista-security/SubvertingVistaUAC.aspx
I try explained here it's working on Windows 7
On Win2K, XP and Win2K3 the console user is logged on in Session 0, the same session the services live in. If a service is configured as interactive, it'll be able to show the UI on the user's desktop.
However, on Vista, no user can be logged on in Session 0. Showing UI from a service there is a bit trickier. You need to enumerate the active sessions using WTSEnumerateSessions API, find the console session and create the process as that user. Of course, you need also a token or user credentials to be able to do that. You can read more details about this process here.
I think as long as you have only one user logged in, it will automatically display on that user's desktop.
Anyway, be very careful when having a service start an exe.
If the write access to the folder with the exe is not restricted, any user can replace that exe with any other program, which will then be run with sytem rights. Take for example cmd.exe (available on all windows sytems). The next time the service tries to start your exe, you get a command shell with system rights...
If you launch a GUI from your service it will show up on the currently active desktop.
But only if you adjusted the service permissions: You need to allow it to interact with the desktop.
Important Services cannot directly interact with a user as of Windows Vista. Therefore, the techniques mentioned in the section titled Using an Interactive Service should not be used in new code.
This is taken from : http://msdn.microsoft.com/en-us/library/ms683502(VS.85).aspx