Mister X
Mister X

Reputation: 47

Start Windows Service From Application without Admin right(c++)

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

Answers (4)

Tearsdontfalls
Tearsdontfalls

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

İsmail Kocacan
İsmail Kocacan

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

Harry Johnston
Harry Johnston

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

Jon
Jon

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:

  • detect that the service is for some reason shutting down
  • wait until the service is in a stable state (started/stopped)
  • start the service programmatically

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

Related Questions