Reputation: 47
I wrote a windows service (and it runs fine). Now i have a separate app where I want to start this service from, but it seems this is not possible without administrator rights.
How would a proper solution look like that a user can start/stop the service (e.g. from a tray or application)
IMHO its bad that the application must always be started with administrator rights.
Upvotes: 4
Views: 5501
Reputation: 787
@Harry Johnston 's worked fine for me, in case someone wants to do this in C#:
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool SetServiceObjectSecurity(SafeHandle serviceHandle,
UInt32 secInfos,
IntPtr lpSecDesrBuf);
[DllImport("advapi32.dll", EntryPoint = "ConvertStringSecurityDescriptorToSecurityDescriptorW", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern Boolean ConvertStringSecurityDescriptorToSecurityDescriptor(
[MarshalAs(UnmanagedType.LPWStr)] String strSecurityDescriptor,
UInt32 sDRevision,
ref IntPtr securityDescriptor,
ref UInt32 securityDescriptorSize);
public static void SetServicePermissions(string service)
{
System.ServiceProcess.ServiceController sc = new System.ServiceProcess.ServiceController(service);
bool ok;
IntPtr pSD = IntPtr.Zero;
uint securityDescriptorSize = 0;
string secDesc = "D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPWPDTLOCRRC;;;PU)(A;;RPWP;;;IU)";
ok = ConvertStringSecurityDescriptorToSecurityDescriptor(secDesc, 1, ref pSD, ref securityDescriptorSize);
if (!ok)
{
throw new ApplicationException("error calling ConvertStringSecurityDescriptorToSecurityDescriptor(): error code=" + Marshal.GetLastWin32Error());
}
ok = SetServiceObjectSecurity(sc.ServiceHandle, 4 , pSD);
if (!ok)
{
throw new ApplicationException("error calling SetServiceObjectSecurity(); error code=" + Marshal.GetLastWin32Error());
}
}
Upvotes: 0
Reputation: 1234
@Harry Johnston, in addition to response.
Here is c++ builder example.
void __fastcall TService1::ServiceAfterInstall(TService *Sender)
{
wchar_t lpBuffer[256];
long errorCode;
SC_HANDLE hSCManager,hService;
hSCManager = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
if (hSCManager == NULL)
{
errorCode = GetLastError();
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errorCode,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), lpBuffer, 256, NULL);
LogMessage("OpenSCManager Error "+AnsiString(lpBuffer), EVENTLOG_ERROR_TYPE);
return;
}
hService = OpenService(hSCManager, this->Name.c_str(), READ_CONTROL | WRITE_DAC);
if (hService == NULL)
{
errorCode = GetLastError();
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errorCode,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), lpBuffer, 256, NULL);
LogMessage("OpenService Error "+AnsiString(lpBuffer), EVENTLOG_ERROR_TYPE);
CloseServiceHandle(hSCManager);
}
wchar_t sddl[] = L"D:"
L"(A;;CCLCSWRPWPDTLOCRRC;;;SY)" // default permissions for local system
L"(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)" // default permissions for administrators
L"(A;;CCLCSWLOCRRC;;;AU)" // default permissions for authenticated users
L"(A;;CCLCSWRPWPDTLOCRRC;;;PU)" // default permissions for power users
L"(A;;RP;;;IU)" // added permission: start service for interactive users
;
PSECURITY_DESCRIPTOR sd;
if (!ConvertStringSecurityDescriptorToSecurityDescriptor(AnsiString(sddl).c_str(), SDDL_REVISION_1, &sd, NULL))
{
errorCode = GetLastError();
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errorCode,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), lpBuffer, 256, NULL);
LogMessage("ConvertStringSecurityDescriptorToSecurityDescriptor Error "+AnsiString(lpBuffer), EVENTLOG_ERROR_TYPE);
}
if (!SetServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, sd))
{
errorCode = GetLastError();
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errorCode,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), lpBuffer, 256, NULL);
LogMessage("SetServiceObjectSecurity Error "+AnsiString(lpBuffer), EVENTLOG_ERROR_TYPE);
}
CloseServiceHandle(hService);
CloseServiceHandle(hSCManager);
}
Upvotes: 1
Reputation: 36348
You just need to change the permissions on the service object, preferably at the same time you install it.
wchar_t sddl[] = L"D:"
L"(A;;CCLCSWRPWPDTLOCRRC;;;SY)" // default permissions for local system
L"(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)" // default permissions for administrators
L"(A;;CCLCSWLOCRRC;;;AU)" // default permissions for authenticated users
L"(A;;CCLCSWRPWPDTLOCRRC;;;PU)" // default permissions for power users
L"(A;;RP;;;IU)" // added permission: start service for interactive users
;
PSECURITY_DESCRIPTOR sd;
if (!ConvertStringSecurityDescriptorToSecurityDescriptor(sddl, SDDL_REVISION_1, &sd, NULL))
{
fail();
}
if (!SetServiceObjectSecurity(service, DACL_SECURITY_INFORMATION, sd))
{
fail();
}
I'm assuming here you've already opened the service handle. You need WRITE_DAC permission.
If you also want non-admin users to be able to stop the service, add the WP right, i.e.,
L"(A;;RPWP;;;IU)"
// added permissions: start service, stop service for interactive users
SDDL codes for service rights can be found in Wayne Martin's blog entry, Service Control Manager Security for non-admins.
Upvotes: 11
Reputation: 437774
Starting a service programmatically is done with the StartService
function. There is a comprehensive usage example also given under the title starting a service, which also shows how to:
As for administrator rights, this is necessary because if just about any application could shut services down (or, more importantly, install and start new services) there would be very real and very serious security issues.
Upvotes: 0