views:

727

answers:

12

Hello all,

I've written a small service (plain Win32) and I'd like to know if it's possible to run multiple instances of it when multiple users are logged on.

Basically, let's say we've got UserA and UserB for UserA the service would log on as "domain\UserA" and for UserB the service would log on as "domain\UserB" - this is from the same executable of course. I can change the logon dynamically using the ChangeServiceConfig() function, but it changes it system-wide it seems, while I'd like each user to have his own copy of the service running only for him.

Thank you in advance for any pointers.

+1  A: 

Win32 services are designed to be system-wide, and start running before any user is logged in. If you want something to run on a per-user basis, it's probably better to design it as a regular application and run it from the user's Startup group.

Greg Hewgill
A: 

The whole concept of a service is that it is started before any user is even logged on. so even if this was possible, you wouldn't be able to choose between userA and userB when the service starts because none of them is logged on yet.


A possible direction would be for the service to run as SYSTEM And every few minutes check if there is a user logged in, if there is- impersonate that user and do this stuff.

shoosh
Yes, but I'd like the service to run logged in as a user even if no one is logged on. Or as multiple users at the same time, if multiple users used it on a machine.
dennisV
A: 

Thanks Greg, I did consider running as a regular application, but ultimately I'd like the service(s) to run without users logged on. So, the computer is turned on and then the services should start and log on as users on the machine and start doing their stuff. Is that possible are am I expecting too much?

dennisV
It sounds like you might want to create multiple services, with the same executable but running as a different user. This assumes you know the list of users you want to run as beforehand!
Greg Hewgill
It's not an unheard-of situation, and the Task Scheduler service takes care of this with child processes of taskeng.exe, one for each user with tasks.
Chris Charabaruk
Yes, sounds right (I think).
dennisV
A: 

Is it possible to perhaps have the service create child processes which then adopt the user credentials (or be started with them)? This way you're still limited to a single instance of the service, but it is able to do its per-user jobs all the same. IIRC the Windows Task Scheduler service does this.

Chris Charabaruk
Perhaps that's the way. By child processes do you mean that I would re-launch the service executable with some command line that I would process internally to run as a different user?
dennisV
Perhaps. Or perhaps by using job processes (like IE8 and Chrome use for tabs) which supposedly reduces some overhead.
Chris Charabaruk
Thank you - I'll look into this as a possibility. I wonder how Task Scheduler does this...
dennisV
The Task Scheduler service itself is implemented in schedsvc.dll, and hosted in an instance of svchost.exe. When a task becomes active, the service launches an instance of taskeng.exe for each account with an active task, IIRC using impersonation. Check it out in Process Explorer.
Chris Charabaruk
A: 

Yes, that sounds close (I'm answering comment from Greg, but comments are too short to fit my reply).

I don't know the list of users beforehand, but there's a GUI control application that would be used to enter username/password pairs for each user. So, userA would log on, run the application, enter his credentials and service would use that. At the same time (after userA has logged off, but the service is still running with userA's credentials) userB logs on, uses the app, and another copy of the service starts running as logged on userB. Thus, at the same time userA and userB services are running.

Is that possible?

dennisV
It sounds like it might be easy enough to use the Task Scheduler to run your application on behalf of the users. Have a configuration UI that lets each user set up your service, which adds an entry to their Scheduled Tasks to run your app. Then your app gets run on whatever schedule you set up.
Greg Hewgill
Hmm, interesting approach. I will need to research more on the scheduler system to see if it will work. But will it cover me if no one is logged on? I don't know if scheduled tasks run without anyone logged on (they should, I suppose).
dennisV
They do indeed. Task Scheduler takes care of setting up the correct user context to run the task, relieving you of that responsibility.
Greg Hewgill
Thank you - I'll read up on that and get back here to let you know if it will work or, for whatever reason, not.
dennisV
A: 

You are probably looking to Impersonate the users. Check out some references I found with a quick Google search here:

Aydsman
Thank you - I'll take a look at those as well, I don't know much about impersonating at the moment, but I'm not sure if it'll help me, because the main problem that I have is that I want to run the same service at the same time as different users.
dennisV
A: 

It sounds as if you actually have two different, conflicting requirements, as to timing and identity.

  1. Run as each logged in user
  2. Run automatically even if no user is logged in.

No way to do this trivially, instead consider wrapping your program in a service; the program will run normally on startup for each user (either thru the startup folder or taskscheduler), and in addition create a service to run your app as a system user (or any other user you define).
Since you also need (you mention this in the comments) the app to keep running as the enduser even after he logs out, you can have the service manage this process for you.

HOWEVER this might not be the best idea, since the user is still effectively logged in. This can have numerous side effects, including security, performance (too many users logged in at once...), etc.

AviD
There's only a service, so no program to wrap (saved one step!). What I don't understand is how to actually register a service for each user, so that I could change that service's log on credentials. One way to go (but it's ugly) is to create a service for each user (with a different service name).
dennisV
As mentioned above, Windows Services are intended to be run at a system level, and not per user. What really is the requirement here, maybe you're approaching this in the wrong way?
AviD
If you do find you need to do this, don't have a separate service for each user - have a single central service, that spawns processes per user as needed. The CreateProc set of functions allow you to define the user's credentials...
AviD
A: 

You could create an service application and a non-service(normal) application and make them communicate through IPC (Mapped File, Pipes, MailSolts ... you name it).

This way you solve all the troubles.

NOTE: The same application can behave differently - when started as a process and when started by a user, but in the end it is the same thing, you still have 2 applications (no matter if you got only one executable).

Iulian Şerbănoiu
Thank you, but how would it solve the issue of running the same service as multiple users? I mean, I need multiple services running when computer is turned on, each logged in as a different user.
dennisV
A: 

Running with different accounts is possible. In fact, this is common. See svchost.exe, which implements a bunch of OS services.

I just don't get how you determine which accounts. In a big company, many PCs are set up so all 100.000+ employees could use it. You don't want to run your service as the logged-in users, nor can you want to run it for all 100.000 users. So for which accounts, I have to ask?

MSalters
Yes, it would be an issue in such an environment, I agree. But this is for a "home" application, so I don't expect more than 3-4 accounts on any one machine, with the most common being 1 and 2.
dennisV
A: 

A Windows process can only execute with the privileges of one single user at a time. This applies to services and other processes. With enough privileges it is possible to "switch" between different users by using impersonation. The most common pattern for what you are trying to do is to have one instance of a privileged service which registers to log in/log out events and creates children processes accordingly, each one of them impersonating the logged in user. The pattern will also simplify UI as each process runs on each separate user's Desktop, as if it were a regular application.

If you keep the privileged service's code as simple as possible this pattern has the added benefit that you are minimizing the attack surface of your code. If a user finds a security problem on the "running as user" side of your service it is a non-issue, while security problems in the privileged services could lead to privilege escalation. In fact, before Vista privileged services implementing a Windows message processing loop are vulnerable to a type of attack called Shatter attacks, which you should be aware of given what you are trying to do.

Max Caceres
Thank you - I'll have a read on those attacks. I was also thinking along similar lines of launching child processes (from one "central" system service) which would then be logged on as the users.
dennisV
A: 

You don't need multiple instances of your service. From the description of your problem it looks like what you need is one service that can impersonate users and execute jobs on their behalf.

You can do this by implementing a COM object hosted in a service. Your client application (that the end user runs) will call CoCreateInstanceEx on your CLSID. This would cause new instance of your COM object to be created in your service. Then the application can use a method on one of your interfaces to pass the collected user credentials to the COM object (though I'd be wary of collecting credentials and instead see if I can pass the user token instead). The COM object which is running in the context of the service can then call LogonUser() to log on the user and impersonate it, so it can do whatever on her behalf (like finding the user local appdata folder :-)). Other answers havve good links to impersonating users using credentials or token.

If you feel comfortable with COM, I'd suggest you create your objects as multithreaded (living in the MTA), so that their execution is not serialized by COM. If not, the default single threaded model would be good enough for you.

The Visual Studio ATL wizard can generate the skeleton of a COM object living in a service. You can also read about implementing Windows Service with ATL here: http://msdn.microsoft.com/en-us/library/74y2334x%28VS.80%29.aspx

If you don't know COM at all, you can use other communication channels to pass the credentials to your service.

In any case, once your service gets the credentials, all the work on behalf of the user will have to be executed on a background thread, so as to not block the application running as the user.

Franci Penov
Thank you for the idea. The question that I do have is how will I be able to control whether individual user's services are run or not, let's say UserA wants to start the service automatically, but UserB manually? Will that be possible?
dennisV
To start it upon user login, add simple executable to the user's HKCU\Software\Microsoft\Windows\CurrentVersion\Run. This executable will start the user job in your service, as described above.
Franci Penov
To start it upon OS boot, you need to store somewhere the user's credentials, so you can logon the user and impersonate. I wouldn't do that, as it leads to serious security risks.
Franci Penov
A: 

You want this running all the time, so you want a service.

You want something tracking each user, so you want an application which runs in the user session and communicates with the service (using named pipes or DCOM or whatever fits your requirements).

richard.albury