Handle running without administrator privileges.
authorIain Patterson <me@iain.cx>
Tue, 15 May 2012 10:08:34 +0000 (11:08 +0100)
committerIain Patterson <me@iain.cx>
Sun, 20 May 2012 13:23:25 +0000 (14:23 +0100)
If the service is configured to run as an unprivileged user NSSM will
not be able to perform certain tasks which are not critical and will
therefore log some errors which can be ignored.

It will not be able to register itself as an event log source.
It will not be able to create a default AppExit key.
It will not be able to configure service failure handling.

Try to perform these tasks where first installing the service (which
we are guaranteed to do with administrator rights) as well as at
runtime, and don't log the errors if the runtime actions can't be
performed.

nssm.cpp
service.cpp
service.h

index d543c46..7a73114 100644 (file)
--- a/nssm.cpp
+++ b/nssm.cpp
@@ -1,6 +1,7 @@
 #include "nssm.h"\r
 \r
 extern unsigned long tls_index;\r
+extern bool is_admin;\r
 \r
 /* String function */\r
 int str_equiv(const char *a, const char *b) {\r
@@ -27,30 +28,28 @@ int usage(int ret) {
   return(ret);\r
 }\r
 \r
-int check_admin(char *action) {\r
+void check_admin() {\r
+  is_admin = false;\r
+\r
   /* Lifted from MSDN examples */\r
   PSID AdministratorsGroup;\r
   SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;\r
-  BOOL ok = AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup);\r
-  if (ok) {\r
-    if (! CheckTokenMembership(0, AdministratorsGroup, &ok)) ok = 0;\r
-    FreeSid(AdministratorsGroup);\r
-\r
-    if (ok) return 0;\r
-\r
-    fprintf(stderr, "Administator access is needed to %s a service.\n", action);\r
-    return 1;\r
-  }\r
-\r
-  /* Can't tell if we are admin or not; later operations may fail */\r
-  return 0;\r
+  if (! AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup)) return;\r
+  CheckTokenMembership(0, AdministratorsGroup, /*XXX*/(PBOOL) &is_admin);\r
+  FreeSid(AdministratorsGroup);\r
 }\r
 \r
 int main(int argc, char **argv) {\r
+  /* Remember if we are admin */\r
+  check_admin();\r
+\r
   /* Elevate */\r
   if (argc > 1) {\r
     if (str_equiv(argv[1], "install") || str_equiv(argv[1], "remove")) {\r
-      if (check_admin(argv[1])) exit(100);\r
+      if (! is_admin) {\r
+        fprintf(stderr, "Administrator access is needed to %s a service.\n", argv[1]);\r
+        exit(100);\r
+      }\r
     }\r
 \r
     /* Valid commands are install or remove */\r
@@ -66,7 +65,7 @@ int main(int argc, char **argv) {
   tls_index = TlsAlloc();\r
 \r
   /* Register messages */\r
-  create_messages();\r
+  if (is_admin) create_messages();\r
 \r
   /* Start service magic */\r
   SERVICE_TABLE_ENTRY table[] = { { NSSM, service_main }, { 0, 0 } };\r
index 7e9a9d7..d44998a 100644 (file)
@@ -1,5 +1,6 @@
 #include "nssm.h"\r
 \r
+bool is_admin;\r
 SERVICE_STATUS service_status;\r
 SERVICE_STATUS_HANDLE service_handle;\r
 HANDLE process_handle;\r
@@ -29,7 +30,7 @@ static inline int throttle_milliseconds() {
 SC_HANDLE open_service_manager() {\r
   SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);\r
   if (! ret) {\r
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);\r
+    if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);\r
     return 0;\r
   }\r
 \r
@@ -130,6 +131,8 @@ int install_service(char *name, char *exe, char *flags) {
     return 6;\r
   }\r
 \r
+  set_service_recovery(service, name);\r
+\r
   /* Cleanup */\r
   CloseServiceHandle(service);\r
   CloseServiceHandle(services);\r
@@ -204,10 +207,12 @@ void WINAPI service_main(unsigned long argc, char **argv) {
   service_status.dwWaitHint = throttle_delay + NSSM_WAITHINT_MARGIN;\r
   SetServiceStatus(service_handle, &service_status);\r
 \r
-  /* Try to create the exit action parameters; we don't care if it fails */\r
-  create_exit_action(argv[0], exit_action_strings[0]);\r
+  if (is_admin) {\r
+    /* Try to create the exit action parameters; we don't care if it fails */\r
+    create_exit_action(argv[0], exit_action_strings[0]);\r
 \r
-  set_service_recovery(service_name);\r
+    set_service_recovery(0, service_name);\r
+  }\r
 \r
   /* Used for signalling a resume if the service pauses when throttled. */\r
   throttle_timer = CreateWaitableTimer(0, 1, 0);\r
@@ -219,12 +224,16 @@ void WINAPI service_main(unsigned long argc, char **argv) {
 }\r
 \r
 /* Make sure service recovery actions are taken where necessary */\r
-void set_service_recovery(char *service_name) {\r
-  SC_HANDLE services = open_service_manager();\r
-  if (! services) return;\r
+void set_service_recovery(SC_HANDLE service, char *service_name) {\r
+  SC_HANDLE services = 0;\r
+\r
+  if (! service) {\r
+    services = open_service_manager();\r
+    if (! services) return;\r
 \r
-  SC_HANDLE service = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);\r
-  if (! service) return;\r
+    service = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);\r
+    if (! service) return;\r
+  }\r
 \r
   SERVICE_FAILURE_ACTIONS_FLAG flag;\r
   ZeroMemory(&flag, sizeof(flag));\r
@@ -238,6 +247,11 @@ void set_service_recovery(char *service_name) {
       log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CHANGESERVICECONFIG2_FAILED, service_name, error_string(error), 0);\r
     }\r
   }\r
+\r
+  if (services) {\r
+    CloseServiceHandle(service);\r
+    CloseServiceHandle(services);\r
+  }\r
 }\r
 \r
 int monitor_service() {\r
index 3838700..2db92fd 100644 (file)
--- a/service.h
+++ b/service.h
@@ -13,7 +13,7 @@ int pre_install_service(int, char **);
 int pre_remove_service(int, char **);\r
 int install_service(char *, char *, char *);\r
 int remove_service(char *);\r
-void set_service_recovery(char *);\r
+void set_service_recovery(SC_HANDLE, char *);\r
 int monitor_service();\r
 int start_service();\r
 int stop_service(unsigned long, bool, bool);\r