Start / Stop a Windows Service from a non-Administrator user account - windows-services

I have a WindowsService named, say, BST. And I need to give a non-Administrator user, UserA, the permissions to Start/Stop this particular service. My service runs on a variety of Windows OS, starting from Windows Server 2003 to Windows 7.
How can I do this?
I Googled and found some stuff about giving permissions using the command [sc sdset], but I am not exactly sure about the parameters. I do not want to set the permissions for a group, but ONLY to a particular user, UserA in this case.

Below I have put together everything I learned about Starting/Stopping a Windows Service from a non-Admin user account, if anyone needs to know.
Primarily, there are two ways in which to Start / Stop a Windows Service.
1. Directly accessing the service through logon Windows user account.
2. Accessing the service through IIS using Network Service account.
Command line command to start / stop services:
C:/> net start <SERVICE_NAME>
C:/> net stop <SERVICE_NAME>
C# Code to start / stop services:
ServiceController service = new ServiceController(SERVICE_NAME);
//Start the service
if (service.Status == ServiceControllerStatus.Stopped)
{
service.Start();
service.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(10.0));
}
//Stop the service
if (service.Status == ServiceControllerStatus.Running)
{
service.Stop();
service.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(10.0));
}
Note 1:
When accessing the service through IIS, create a Visual Studio C# ASP.NET Web Application and put the code in there. Deploy the WebService to IIS Root Folder (C:\inetpub\wwwroot\) and you're good to go.
Access it by the url http:///.
1. Direct Access Method
If the Windows User Account from which either you give the command or run the code is a non-Admin account, then you need to set the privileges to that particular user account so it has the ability to start and stop Windows Services. This is how you do it.
Login to an Administrator account on the computer which has the non-Admin account from which you want to Start/Stop the service. Open up the command prompt and give the following command:
C:/>sc sdshow <SERVICE_NAME>
Output of this will be something like this:
D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)
It lists all the permissions each User / Group on this computer has with regards to .
A description of one part of above command is as follows:
D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)
It has the default owner, default group, and it has the Security descriptor control flags (A;;CCLCSWRPWPDTLOCRRC;;;SY):
ace_type - "A": ACCESS_ALLOWED_ACE_TYPE,
ace_flags - n/a,
rights - CCLCSWRPWPDTLOCRRC, please refer to the Access Rights and Access Masks and Directory Services Access Rights
CC: ADS_RIGHT_DS_CREATE_CHILD - Create a child DS object.
LC: ADS_RIGHT_ACTRL_DS_LIST - Enumerate a DS object.
SW: ADS_RIGHT_DS_SELF - Access allowed only after validated rights checks supported by the object are performed. This flag can be used alone to perform all validated rights checks of the object or it can be combined with an identifier of a specific validated right to perform only that check.
RP: ADS_RIGHT_DS_READ_PROP - Read the properties of a DS object.
WP: ADS_RIGHT_DS_WRITE_PROP - Write properties for a DS object.
DT: ADS_RIGHT_DS_DELETE_TREE - Delete a tree of DS objects.
LO: ADS_RIGHT_DS_LIST_OBJECT - List a tree of DS objects.
CR: ADS_RIGHT_DS_CONTROL_ACCESS - Access allowed only after extended rights checks supported by the object are performed. This flag can be used alone to perform all extended rights checks on the object or it can be combined with an identifier of a specific extended right to perform only that check.
RC: READ_CONTROL - The right to read the information in the object's security descriptor, not including the information in the system access control list (SACL). (This is a Standard Access Right, please read more http://msdn.microsoft.com/en-us/library/aa379607(VS.85).aspx)
object_guid - n/a,
inherit_object_guid - n/a,
account_sid - "SY": Local system. The corresponding RID is SECURITY_LOCAL_SYSTEM_RID.
Now what we need to do is to set the appropriate permissions to Start/Stop Windows Services to the groups or users we want. In this case we need the current non-Admin user be able to Start/Stop the service so we are going to set the permissions to that user. To do that, we need the SID of that particular Windows User Account. To obtain it, open up the Registry (Start > regedit) and locate the following registry key.
LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
Under that there is a seperate Key for each an every user account in this computer, and the key name is the SID of each account. SID are usually of the format S-1-5-21-2103278432-2794320136-1883075150-1000. Click on each Key, and you will see on the pane to the right a list of values for each Key. Locate "ProfileImagePath", and by it's value you can find the User Name that SID belongs to. For instance, if the user name of the account is SACH, then the value of "ProfileImagePath" will be something like "C:\Users\Sach". So note down the SID of the user account you want to set the permissions to.
Note2:
Here a simple C# code sample which can be used to obtain a list of said Keys and it's values.
//LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList RegistryKey
RegistryKey profileList = Registry.LocalMachine.OpenSubKey(keyName);
//Get a list of SID corresponding to each account on the computer
string[] sidList = profileList.GetSubKeyNames();
foreach (string sid in sidList)
{
//Based on above names, get 'Registry Keys' corresponding to each SID
RegistryKey profile = Registry.LocalMachine.OpenSubKey(Path.Combine(keyName, sid));
//SID
string strSID = sid;
//UserName which is represented by above SID
string strUserName = (string)profile.GetValue("ProfileImagePath");
}
Now that we have the SID of the user account we want to set the permissions to, let's get down to it. Let's assume the SID of the user account is S-1-5-21-2103278432-2794320136-1883075150-1000.
Copy the output of the [sc sdshow ] command to a text editor. It will look like this:
D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)
Now, copy the (A;;CCLCSWRPWPDTLOCRRC;;;SY) part of the above text, and paste it just before the S:(AU;... part of the text. Then change that part to look like this:
(A;;RPWPCR;;;S-1-5-21-2103278432-2794320136-1883075150-1000)
Then add sc sdset at the front, and enclose the above part with quotes. Your final command should look something like the following:
sc sdset <SERVICE_NAME> "D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;RPWPCR;;;S-1-5-21-2103278432-2794320136-1883075150-1000)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)"
Now execute this in your command prompt, and it should give the output as follows if successful:
[SC] SetServiceObjectSecurity SUCCESS
Now we're good to go! Your non-Admin user account has been granted permissions to Start/Stop your service! Try loggin in to the user account and Start/Stop the service and it should let you do that.
2. Access through IIS Method
In this case, we need to grant the permission to the IIS user "Network Services" instead of the logon Windows user account. The procedure is the same, only the parameters of the command will be changed. Since we set the permission to "Network Services", replace SID with the string "NS" in the final sdset command we used previously. The final command should look something like this:
sc sdset <SERVICE_NAME> "D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;RPWPCR;;;NS)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)"
Execute it in the command prompt from an Admin user account, and voila! You have the permission to Start / Stop the service from any user account (irrespective of whether it ia an Admin account or not) using a WebMethod. Refer to Note1 to find out how to do so.

I use the SubInACL utility for this. For example, if I wanted to give the user job on the computer VMX001 the ability to start and stop the World Wide Web Publishing Service (also know as w3svc), I would issue the following command as an Administrator:
subinacl.exe /service w3svc /grant=VMX001\job=PTO
The permissions you can grant are defined as follows (list taken from here):
F : Full Control
R : Generic Read
W : Generic Write
X : Generic eXecute
L : Read controL
Q : Query Service Configuration
S : Query Service Status
E : Enumerate Dependent Services
C : Service Change Configuration
T : Start Service
O : Stop Service
P : Pause/Continue Service
I : Interrogate Service
U : Service User-Defined Control Commands
So, by specifying PTO, I am entitling the job user to Pause/Continue, Start, and Stop the w3svc service.
Edit: updated links to web.archive.org since the original MS links are dead.

Login as an administrator.
Download subinacl.exe from Microsoft:
http://www.microsoft.com/en-us/download/details.aspx?id=23510
Grant permissions to the regular user account to manage the BST
services.
(subinacl.exe is in C:\Program Files (x86)\Windows Resource Kits\Tools\).
cd C:\Program Files (x86)\Windows Resource Kits\Tools\
subinacl /SERVICE \\MachineName\bst /GRANT=domainname.com\username=F or
subinacl /SERVICE \\MachineName\bst /GRANT=username=F
Logout and log back in as the user. They should now be able to
launch the BST service.

There is a free GUI Tool ServiceSecurityEditor
Which allows you to edit Windows Service permissions. I have successfully used it to give a non-Administrator user the rights to start and stop a service.
I had used "sc sdset" before I knew about this tool.
ServiceSecurityEditor feels like cheating, it's that easy :)

It's significantly easier to grant management permissions to a service using one of these tools:
Group Policy
Security Template
subinacl.exe command-line tool.
Here's the MSKB article with instructions for Windows Server 2008 / Windows 7, but the instructions are the same for 2000 and 2003.

subinacl.exe command-line tool is probably the only viable and very easy to use from anything in this post. You cant use a GPO with non-system services and the other option is just way way way too complicated.

Windows Service runs using a local system account.It can start automatically as the user logs into the system or it can be started manually.However, a windows service say BST can be run using a particular user account on the machine.This can be done as follows:start services.msc and go to the properties of your windows service,BST.From there you can give the login parameters of the required user.Service then runs with that user account and no other user can run that service.

Related

Windows Service running with Local System rights, wants to create child processes from windows service which will running with Local User rights

I am working on windows service application where currently it is running on administrator privileges.
For that I need to set administrator info in service log on settings.
Above windows service is creating child processes with local user privileges.
This is working fine.
Now I want to change windows service application to run with "Local System" privileges instead of administrator.
But if I change the log on setting to "Local System" then, Child processes are not starting and throws exception "access denied".
I think that "local system" account has all permission to do anything.
It is even stronger than administrator account.
Then why it cannot start child processes?
Please guide me if my perception is wrong
Local System certainly DOES NOT have "all permission to do anything". That would mean that if someone managed to get INTO that process.. they could really run amok on the system.
Group Policy Editor will show you EXACTLY who can do what so you don't need to guess your way through.
Open GPEDIT.MSC
Navigate to Computer Configuration -> Windows Settings -> Security Settings -> Local Policies -> User Rights Assignment
On the right hand side, Look for Obtain an impersonation token for another user in the same session.
I am not 100% sure that this is the setting you need (I haven't tried it) but I THINK it is. Even if it isn't, browse around this section of group policy to see who can do what. Clearly the setting you want won't have "Local System" on the list ;)
I hope this helps. :) GOOD LUCK!

OpenProcessToken() gives "Access is denied"

As part of a project which runs as a service that spawns a process in the login screen (for desktop control) we call OpenProcessToken(), which is then duplicated and a process created. This works successfully as expected under LocalSystem, however this does not work under a domain account. The code fragment is below...
procedure LaunchProcess;
var dwPid, dwSessionId: DWord;
hUserToken, hProcess: THANDLE;
begin
dwPid := GetProcessID('winlogon.exe', WTSGetActiveConsoleSessionId);
hProcess := OpenProcess(MAXIMUM_ALLOWED, FALSE, dwPid);
if (not OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY or TOKEN_DUPLICATE or
TOKEN_ASSIGN_PRIMARY or TOKEN_ADJUST_SESSIONID or TOKEN_READ or TOKEN_WRITE, hUserToken)) then
raise Exception.Create('OpenProcessToken failed (' + SysErrorMessage(GetLastError) + ').');
{...go on to duplicate token, create environment and launch process...}
end;
Full source of the surrounding supporting function can be found here.
This is where it gets a little vague. I understand OpenProcessToken() requires privileges, which is ultimately why I am getting the error, however it wasn't clear what privilege I require, and how to effectively assign that against a domain account.
This would suggest that the required privilege is SeTcbPrivilege ("Act as part of the operating system").
I've read the Microsoft page (can't link, not enough reputation - sorry) on privileges which suggests that the SeTcbPrivilege can be assigned to a domain account using Local or Group Security Policy. It has also been suggested that the destination process (i.e. winlogon.exe) may simply not allow anything other than LocalSystem to obtain its token.
I have tried to configure the domain account explicitly using account the service, but in the Local Security and Group Policy, have restarted and performed gporesult to ensure the policy has taken effect, but each time whoami /priv returns SeTcbPrivilege is disabled
My question is if this is even possible (can I obtain winlogon.exe token using a domain account) and if so can the privilege be set programmatically, or does this need to be through GPO? (and if so, given my previous attempts at using GPO had no effect, how is it possible)
You can Activate Rights using LsaAddAccountRights, you still need to at least log off/on possibly reboot.
You then need to enable the Privileges for those rights in your code. Lots of things happen automatically for Local-system that do not for Users.
For a quick test I Activated and Enabled SE_TCB_NAME, SE_ASSIGNPRIMARYTOKEN_NAME and SE_INCREASE_QUOTA_NAME
I was then able to successfully call OpenProcessToken with only TOKEN_DUPLICATE
Activating those right gave me a new SessionID after Log off/on. SessionID of 2 so your call to WTSGetActiveConsoleSessionId would have falsely returned 1
All this was done as elevated Admin
Does your application run successfully when you run it with administrator privileges? If so, go to your project options, select Application and check "Enable Administrator Privileges" under Manifest File.

Windows service application self-defense in Delphi ( rad studio )

I am building a Windows service app using Delphi , RAD Studio 10
Upon my investigation I came across with a Eset Windows service which wonderfully was protecting itself from being Stopped or terminated.
On stopping the service using (windows service manager) or (end process button)
or (end task button) following error messages occurs :
The operation couldn't be completed.
access denied.
same thing is true with its registry keys . The error message is :
Can not delete info: Error while deleting key
I tested Administrator access and system access. in both cases i was not successful.
I want to build such self-defense mechanism for my own application protecting my service and registry key.
Any idea would be helpful.
Thank you for your time.
update :
I want to know how i can do it in Delphi ... that's why it is tagged Delphi
and If someone wants to stop the service or uninstall it ....
he or she can just use my own applications UI to do it.
edit 2 :
As Remko mentioned I thnk DACL and ACL is better way to handle it , I couldn't find any good reference for it. Is there any good reference for Delphi language?
Protecting from SCM stop is very easy. Assuming you are using TService, you can handle the TService.OnStop event and set its Stopped parameter to False. And assign an error code to the TService.ErrCode or TService.Win32ErrCode property.
Unless you are writing security software, you really should not protect from TaskManager termination. Admins should be allowed to kill misbehaving processes. That being said, you can use SetSecurityInfo() to assign a DACL to your service process that grants/denies access to particular users and/or groups as needed.
You can also use ChangeServiceConfig2() to configure your service's "failure actions" to restart the service if it terminates unexpectedly.
To protect your Registry key, you can use the lpSecurityAttributes parameter of RegCreateKeyEx(), or use the RegSetKeySecurity() function, to assign a SECURITY_DESCRIPTOR to your key that contains a DACL that grants/denies access to particular users and/or groups as needed.

Network Service account does not accept local paths

I am creating a program that runs as a service and creates database backups (using pg_dump.exe) at certain points during the day. This program needs to be able to write the backup files to local drives AND mapped network drives.
At first, I was unable to write to network drives, but solved the problem by having the service log on as an administrator account. However, my boss wants the program to run without users having to key in a username and password for the account.
I tried to get around this by using the Network Service account (which does not need a password and always has the same name). Now my program will write to network drives, but not local drives! I tried using the regular C:\<directory name>\ path syntax as well as \\<computer name>\C$\<directory name>\ syntax and also \\<ip address>\C$\<directory name>\, none of which work.
Is there any way to get the Network Service account to access local drives?
Just give the account permission to access those files/directories, it should work. For accessing local files, you need to tweak ACLs on the files and directories. For accessing via network share, you have to make changes to file ACLs, as well as permissions on network share.
File ACLs can be modified in Exploler UI, or from command line, using standard icacls.exe. E.g. this command line will give directory and all files underneath Read, Write and Delete permissions for Network Service.
icacls c:\MyDirectory /T /grant "NT AUTHORITY\Network Service":(R,W,D)
File share permissions are easier to modify from UI, using fsmgmt.msc tool.
You will need to figure out what minimal set of permissions necessary to be applied. If you don't worry about security at all, you can give full permissions, but it is almost always an overkill, and opens you up more if for any reason the service is compromised.
I worked around this problem by creating a new user at install time which I add to the Administrators group. This allows the service to write to local and network drives, without ever needing password/username info during the setup.

Application Settings + IniFile + User Rights

Could you tell me please. I wrote an application that uses 'Settings.ini' to save settings. The application is installed using InnoSetup. If there are several user accounts and the application is installed in one of them, so, if I try to run the application from another account I get error "Unable to write to....". That is, if run from another account, the application has no rights to write to the settings file. I don't know what happens in Windows XP, but that's the way application behaves in Windows 7.
So could you tell me please, is there something I should define in my application to make it work for multiple users or it should be changed during the installation, somewhere in InnoSetup parameters?
Thank you.
You should - install the very program into Program Files or some other common folder - folder that any user have rights to read and run, but not to write or delete. USually installers would allow to override the folder, but the rule remains: every user should have non-destructive rights (read and execute) and none - destructive (delete, overwrite)
That means your installer should request Admin rights and UAC Elevation - ask InnoSetup forum or documentation how to do it. That may deal with including manifests resources into installer. It is normal when a regular user runs the installer and installer then asks for another username and password with admin rights.
The settings and other user-unique data should be saved into special per-user data folders. Usually that is AppLocal folder under user profile. However it is task for your application rather than the installer. Installer may create global settings templates, that the application would just copy into user-local settings on the 1st run under given user, or would override by user-local settings if cascading settings storage is implemented.
http://msdn.microsoft.com/en-us/library/windows/apps/hh465094.aspx
http://blogs.msdn.com/b/patricka/archive/2010/03/18/where-should-i-store-my-data-and-configuration-files-if-i-target-multiple-os-versions.aspx
Run command prompt, issue set command - and u would see all needed paths as environment variables wit hnames like AppPath, LocalAppPath and UserProfile
http://greatis.com/delphicb/tips/lib/systemifo-envstrings.html
http://www.delphidabbler.com/articles?article=6

Resources