Is it impossible to access the mapped network drive( mapped in user session) from service after impersonating the current user by using ImpersonateLoggedOnUser Windows API?
Yes, this is impossible. Drive mappings are only established during an interactive logon. The ImpersonateLoggedOnUser function does not impersonate the user's entire logon session, just their security context. This is only one of the many things that cannot be done using impersonation.
I suppose you might be able to do this by duplicating the user's login token (obtained from one of their interactive processes), and then using that to call the CreateProcessAsUser function. You would then launch a process that would work with the mapped network drive(s). I'm not absolutely certain that this will work, as I've never done it, but it seems theoretically possible.
Of course, it begs the question of why you need to follow such a circuitous route. It would be eminently more sensible to just run your code in the user's interactive process to begin with, as a standard Windows application.
This is not something that a service is designed to do. Services do not support mapped network drives. If you want to access a network resource from within a service, you should just use the UNC path.
Related
This question already has an answer here:
Access to a protected network share using Win32 C++
(1 answer)
Closed 7 years ago.
I have a service that lives in a quite restricted server system. It must run under a specific user, let's call it user A.
The service must also be able to access a network share which user A does not have access to, but user B has. So the service must access this network share as user B, while running as user A.
The way I would do this if running locally on the computer is to map a network drive under a different user. But services can't access mapped network drives, even if it was mapped under the same user:
Services and Redirected Drives
Does anyone have a suggestion to what I could do? I'm the creator of the service so I can modify it as I please. Is there perhaps some way to let it access the network share as another user via a winapi call (unmanaged C++)?
The article you link to says:
Instead, the service should use client impersonation to impersonate the user.
In this context, that means using LogonUser and ImpersonateLoggedOnUser.
One caveat: that will only work if you are in a domain, i.e., the account that you want to log into the network server with is also valid on the local machine. If not, then you will have to establish a network connection explicitly using WNetAddConnection2 or similar. It is technically true that this risks exposing the connection to other services, but the risk is minimal in most contexts.
I think this SO post might actually be the answer I'm looking for:
Access to a protected network share using Win32 C++
Unless anyone has a better idea or other objections?
I'm using the _spawnl() function for launching a C++ native application from the main Service function (SvcMain), but the application never gets to run.
Is there any trick to launch applications that interacts with the user?
It runs, you just cannot see it. You have to use CreateProcessAsUser() instead of _spawnl() so the new process runs in a specific user's session, not in the service's own session. In Vista and later, services run in their own isolated sessions (Session 0 Isolation), which users cannot see or interact with. A common solution is to use WTSGetActiveConsoleSessionId() and/or WTSEnumerateSessions() to find the desired user session, then use WTSQueryUserToken() to get a token hanlde can be used with CreateEnvironmentBlock() and CreateProcessAsUser(). Also, when providing a STARTUPINFO to CreateProcessAsUser(), set the lpDesktop field to "WinSta0\\default" (the user's default desktop that they can interact with after logging in).
I want to expand user specific environment variables. I have API for that "ExpandEnvironmentStringsForUser". My code is running in context of service. I want to fetch currently logged in user. Whenever I use GetUserName API it returns "SYSTEM".
My problem is I want to expand %temp% in user specific mode and not in system mode.
Is there any way to get currently logged in user when my code is running in service context?
If your program is running as a service, then there isn't "a logged-in user". The whole notion of multi-user systems makes such a concept meaningless. There could be any number of users logged in, and it is impossible for your program to guess which one you wanted it to pretend to be running under.
Your question is like asking "of all my family members, in whose bedroom is my car currently parked?" when in fact the car is safely and sanely sat outside in the driveway.
You can use the qwinsta command (part of Terminal Services) to obtain a list of currently logged-in users, and do something with that; it'll have absolutely nothing to do with your service, but on some Windows systems that allow only one interactive session at a time (for licencing reasons), it'll be the only one marked Active:
C:\Users\tomalak>qwinsta
SESSIONNAME USERNAME ID STATE TYPE DEVICE
services 0 Disc
>console tomalak 1 Active
That's a little hacky, though; typically you would have a little user-space application that can talk to the back-end service, and do all the user-specific shenanigans in the application.
Are there any alternatives to LogonUser and for impersonating given account in order to access network resources? I'm looking for the method of impersonation which would let me connect to machine in foreign domains (or, workgroup machines for the same matter).
For initial data I have: machine name, username (or domain\username), cleartext password.
I know there's a way to establish connection using WNetAddConnection to a \\machinename\ipc$, then most network functions will run in a context of that account, however win2008 added another twist and some functions still use the account, that thread is running under.
I'm also aware, that there's some way to get an impersonation token using SSPI. Have anyone experimented with those tokens, are they good for accessing shares, SCM, remote registry and stuff? Is is what WNetAddConnection is using?
EDIT: To clarify, the reason I cannot use LogonUser is because I need to impersonate user in a non-trusted domain or workgroup
EDIT2: Another clarification: the item I'm trying to implement is similar to psexec, e.g.:
program should not modify host or active directory configuration (e.g.: create temporary local users, etc). Moreover assumption cannot be made that it is running on DC or not
there can be no assumptions made about which software is pre-installed on the remote host, only condition given is that windows file sharing is enabled on target
Account/password is known to be working on target, but target machine may be in local domain, foreign domain, not in domain at all.
EDIT3: I would really love to hear more about SSPI InitializeSecurityContext / AcquireCredentialsHandle option. Is there anybody who has been working with this API extensively? Is it possible to use the tokens returned with impersonation, so that a thread can access network shares and copy files, etc? Can someone post a working code snippet?
EDIT4: Thanks to Marsh Ray, problem got resolved. If anyone is looking to see the proof-of-concept code, it is here
If you're wanting to "access network resources" outside of your forest, do that with WNetAddConnection2/3 as you mentioned, or use the standard RPC APIs with RPC_ C__ AUTHN__ GSS__ NEGOTIATE and and explicit credentials structure.
Normally, "impersonation" is something that happens on the server side. The server side will be able to impersonate the connection as the account you're connecting as.
But the key is this: impersonation only makes sense for impersonating an account the server can access in his local SAM/domain/forest directory. If the client and server are in different forests, they clearly can't agree on the SID of an account for an impersonation token (except for the case of well-known SIDs like Administrator which serve mainly to confuse this kind of thing), and that seems necessary to check against DACLs etc.
Perhaps what you want is to call LogonUserEx with the LOGON32__ LOGON__ NEW__ CREDENTIALS flag. This should succeed (even in a different forest - it doesn't actually authenticate the credentials you give it) giving you a token with the username/password you specified. You may have to use DuplicateToken to turn this into an impersonation token. Then you can use SetThreadToken to replace the token on your thread.
IMHO this isn't really "impersonation", you're just using the credentials outright, but it allows you to access network resources transparently as the arbitrary username/password you supply.
Edit: Oh yeah, be aware that there is no protection against man-in-the-middle on this type of connection. The client especially cannot strongly authenticate the server (short of heroics like IPSEC), so in theory you can't trust anything the server tells you.
The theory goes that you pass the credentials as a SEC_WINNT_AUTH_IDENTITY structure to the AcquireCredentialsHandle function that creates the handle used in InitializeSecurityContext. I never tried this on foreign domains though and I don't know if it works.
Doing this directly and reliably via the Windows API seems next to impossible, plus Windows does so much work behind the scenes to make network access "just work". Plus the impersonation side of things only works for the single thread that called the APIs.
But... you can run a whole program under a different user... such as when you run a service.
So you could edit the registry in your main program to run various services under different security tokens and use IPC/Sockets to communicate with those processes from your main application. ie. a whole bunch (or restarting and reconfiguring the same process) of helper processes running under the different user(s) which your main app abuses.
I realize this is a hack but it seems viable ;)
You could open a command line, map the drive using the plaintext username and password. Then disconnect the drive:
net use m: \\machinename\share password /user:username
... do stuff ...
net use m: /delete
http://technet.microsoft.com/en-us/library/cc756153(WS.10).aspx
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