Browse Source

Use well-known account aliases.

Jump through a few hoops to make the Windows service control panel to
localise service ObjectNames if they are set to LocalSystem,
LocalService or NetworkService.

Those aren't the canonical names of the accounts as returned by
LsaLookupSids() so attempting to call ChangeServiceConfig() with a
username of, eg, NetworkService will fail, even though writing
NetworkService directly to the registry would work.  Calling
ChangeServiceConfig() with a username of "NT Authority\Network Service"
will work, since that's the canonical name returned by LsaLookupSids()
but it won't be localised in the control panel.

The solution is to write "NT Authority\NetworkService" to the registry
and it will appear correctly.

At this point I'm not sure whether to label it a bug or a feature that
"nssm get <service> ObjectName" will return the actual value in the
registry and not the localised name.

Thanks Marco Certelli.
Iain Patterson 8 years ago
parent
commit
f5ce09a04c
6 changed files with 56 additions and 207 deletions
  1. 2 2
      README.txt
  2. 21 157
      account.cpp
  3. 5 4
      account.h
  4. 9 17
      gui.cpp
  5. 2 1
      service.cpp
  6. 17 26
      settings.cpp

+ 2 - 2
README.txt

@@ -520,8 +520,8 @@ The following well-known usernames do not need a password.  The password
 parameter can be omitted when using them:
 
   "LocalSystem" aka "System" aka "NT Authority\System"
-  "Local Service" aka "NT Authority\Local Service"
-  "Network Service" aka "NT Authority\Network Service"
+  "LocalService" aka "Local Service" aka "NT Authority\Local Service"
+  "NetworkService" aka "Network Service" aka "NT Authority\Network Service"
 
 
 The Start parameter is used to query or set the startup type of the service.

+ 21 - 157
account.cpp

@@ -103,7 +103,7 @@ int username_sid(const TCHAR *username, SID **sid, LSA_HANDLE *policy) {
   }
 
   int ret = 0;
-  if (translated_sid->Use == SidTypeWellKnownGroup && requires_password(*sid)) {
+  if (translated_sid->Use == SidTypeWellKnownGroup && ! well_known_sid(*sid)) {
     print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
     ret = 8;
   }
@@ -154,175 +154,31 @@ int is_localsystem(const TCHAR *username) {
 }
 
 /*
-  Find the canonical name for a well-known account name.
-  MUST ONLY BE USED for well-known account names.
-  Must call LocalFree() on result.
+  Get well-known alias for LocalSystem and friends.
+  Returns a pointer to a static string.  DO NOT try to free it.
 */
-TCHAR *canonical_username(const TCHAR *username) {
-  SID *user_sid;
-  TCHAR *canon;
-  size_t len;
-
-  if (is_localsystem(username)) {
-    len = (_tcslen(NSSM_LOCALSYSTEM_ACCOUNT) + 1) * sizeof(TCHAR);
-    canon = (TCHAR *) LocalAlloc(LPTR, len);
-    if (! canon) {
-      print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, NSSM_LOCALSYSTEM_ACCOUNT, _T("canonical_username"));
-      return 0;
-    }
-    memmove(canon, NSSM_LOCALSYSTEM_ACCOUNT, len);
-    _tprintf(_T("it's localsystem = %s!\n"), canon);
-    return canon;
-  }
-
-  if (! imports.CreateWellKnownSid) return 0;
-
-  if (username_sid(username, &user_sid)) return 0;
-
-  /*
-    LsaLookupSids will return the canonical username but the result won't
-    include the NT Authority part.  Thus we must look that up as well.
-  */
-  unsigned long ntsidsize = SECURITY_MAX_SID_SIZE;
-  SID *ntauth_sid = (SID *) HeapAlloc(GetProcessHeap(), 0, ntsidsize);
-  if (! ntauth_sid) {
-    HeapFree(GetProcessHeap(), 0, user_sid);
-    print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("NT Authority"), _T("canonical_username"));
-    return 0;
-  }
-
-  if (! imports.CreateWellKnownSid(WinNtAuthoritySid, NULL, ntauth_sid, &ntsidsize)) {
-    HeapFree(GetProcessHeap(), 0, ntauth_sid);
-    print_message(stderr, NSSM_MESSAGE_CREATEWELLKNOWNSID_FAILED, _T("WinNtAuthoritySid"));
-    return 0;
-  }
-
-  LSA_HANDLE policy;
-  if (open_lsa_policy(&policy)) return 0;
-
-  LSA_REFERENCED_DOMAIN_LIST *translated_domains;
-  LSA_TRANSLATED_NAME *translated_names;
-
-  unsigned long n = 2;
-  PSID *sids = (PSID *) HeapAlloc(GetProcessHeap(), 0, n * sizeof(PSID));
-  sids[0] = user_sid;
-  sids[1] = ntauth_sid;
-
-  NTSTATUS status = LsaLookupSids(policy, n, (PSID *) sids, &translated_domains, &translated_names);
-  HeapFree(GetProcessHeap(), 0, user_sid);
-  HeapFree(GetProcessHeap(), 0, ntauth_sid);
-  HeapFree(GetProcessHeap(), 0, sids);
-  LsaClose(policy);
-  if (status) {
-    print_message(stderr, NSSM_MESSAGE_LSALOOKUPSIDS_FAILED);
-    return 0;
-  }
-
-  /* Look up the account name. */
-  LSA_TRANSLATED_NAME *translated_name = &(translated_names[0]);
-  if (translated_name->Use != SidTypeWellKnownGroup) {
-    print_message(stderr, NSSM_GUI_INVALID_USERNAME);
-    LsaFreeMemory(translated_domains);
-    LsaFreeMemory(translated_names);
-    return 0;
-  }
-
-  LSA_UNICODE_STRING *lsa_group = &translated_name->Name;
-
-  /* Look up NT Authority. */
-  translated_name = &(translated_names[1]);
-  if (translated_name->Use != SidTypeDomain) {
-    print_message(stderr, NSSM_GUI_INVALID_USERNAME);
-    LsaFreeMemory(translated_domains);
-    LsaFreeMemory(translated_names);
-    return 0;
-  }
-
-  /* In theory these pointers should all be valid if we got this far... */
-  LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_name->DomainIndex];
-  LSA_UNICODE_STRING *lsa_domain = &trust->Name;
-
-  TCHAR *domain, *group;
-  unsigned long lsa_domain_len = lsa_domain->Length;
-  unsigned long lsa_group_len = lsa_group->Length;
-  len = lsa_domain_len + lsa_group_len + 2;
-
-#ifdef UNICODE
-  domain = lsa_domain->Buffer;
-  group = lsa_group->Buffer;
-#else
-  size_t buflen;
-
-  wcstombs_s(&buflen, NULL, 0, lsa_domain->Buffer, _TRUNCATE);
-  domain = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);
-  if (! domain) {
-    print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("domain"), _T("canonical_username"));
-    LsaFreeMemory(translated_domains);
-    LsaFreeMemory(translated_names);
-    return 0;
-  }
-  wcstombs_s(&buflen, (char *) domain, buflen, lsa_domain->Buffer, _TRUNCATE);
-
-  wcstombs_s(&buflen, NULL, 0, lsa_group->Buffer, _TRUNCATE);
-  group = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);
-  if (! group) {
-    print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("group"), _T("canonical_username"));
-    LsaFreeMemory(translated_domains);
-    LsaFreeMemory(translated_names);
-    return 0;
-  }
-  wcstombs_s(&buflen, (char *) group, buflen, lsa_group->Buffer, _TRUNCATE);
-#endif
-
-  canon = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
-  if (! canon) {
-    LsaFreeMemory(translated_domains);
-    LsaFreeMemory(translated_names);
-    print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("canonical_username"));
-    return 0;
-  }
-
-  _sntprintf_s(canon, len, _TRUNCATE, _T("%s\\%s"), domain, group);
-
-#ifndef UNICODE
-  HeapFree(GetProcessHeap(), 0, domain);
-  HeapFree(GetProcessHeap(), 0, group);
-#endif
-
-  LsaFreeMemory(translated_domains);
-  LsaFreeMemory(translated_names);
-
-  return canon;
-}
-
-/* Does the SID type require a password? */
-int requires_password(SID *sid) {
-  if (! imports.IsWellKnownSid) return -1;
-  if (imports.IsWellKnownSid(sid, WinLocalSystemSid)) return 0;
-  if (imports.IsWellKnownSid(sid, WinLocalServiceSid)) return 0;
-  if (imports.IsWellKnownSid(sid, WinNetworkServiceSid)) return 0;;
-  return 1;
+const TCHAR *well_known_sid(SID *sid) {
+  if (! imports.IsWellKnownSid) return 0;
+  if (imports.IsWellKnownSid(sid, WinLocalSystemSid)) return NSSM_LOCALSYSTEM_ACCOUNT;
+  if (imports.IsWellKnownSid(sid, WinLocalServiceSid)) return NSSM_LOCALSERVICE_ACCOUNT;
+  if (imports.IsWellKnownSid(sid, WinNetworkServiceSid)) return NSSM_NETWORKSERVICE_ACCOUNT;
+  return 0;
 }
 
-/* Does the username require a password? */
-int requires_password(const TCHAR *username) {
-  if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;
-
+const TCHAR *well_known_username(const TCHAR *username) {
+  if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return NSSM_LOCALSYSTEM_ACCOUNT;
   SID *sid;
   int r = username_sid(username, &sid);
   if (username_sid(username, &sid)) return 0;
 
-  int ret = 0;
-  ret = requires_password(sid);
-
+  const TCHAR *well_known = well_known_sid(sid);
   FreeSid(sid);
 
-  return ret;
+  return well_known;
 }
 
 int grant_logon_as_service(const TCHAR *username) {
   if (! username) return 0;
-  if (! requires_password(username)) return 0;
 
   /* Open Policy object. */
   LSA_OBJECT_ATTRIBUTES attributes;
@@ -340,6 +196,14 @@ int grant_logon_as_service(const TCHAR *username) {
     return 2;
   }
 
+  /*
+    Shouldn't happen because it should have been checked before callling this function.
+  */
+  if (well_known_sid(sid)) {
+    LsaClose(policy);
+    return 3;
+  }
+
   /* Check if the SID has the "Log on as a service" right. */
   LSA_UNICODE_STRING lsa_right;
   lsa_right.Buffer = NSSM_LOGON_AS_SERVICE_RIGHT;

+ 5 - 4
account.h

@@ -5,18 +5,19 @@
 
 /* Not really an account.  The canonical name is NT Authority\System. */
 #define NSSM_LOCALSYSTEM_ACCOUNT _T("LocalSystem")
+/* Other well-known accounts which can start a service without a password. */
+#define NSSM_LOCALSERVICE_ACCOUNT _T("NT Authority\\LocalService")
+#define NSSM_NETWORKSERVICE_ACCOUNT _T("NT Authority\\NetworkService")
 /* This is explicitly a wide string. */
 #define NSSM_LOGON_AS_SERVICE_RIGHT L"SeServiceLogonRight"
 
-
 int open_lsa_policy(LSA_HANDLE *);
 int username_sid(const TCHAR *, SID **, LSA_HANDLE *);
 int username_sid(const TCHAR *, SID **);
 int username_equiv(const TCHAR *, const TCHAR *);
 int is_localsystem(const TCHAR *);
-TCHAR *canonical_username(const TCHAR *);
-int requires_password(SID *);
-int requires_password(const TCHAR *);
+const TCHAR *well_known_sid(SID *);
+const TCHAR *well_known_username(const TCHAR *);
 int grant_logon_as_service(const TCHAR *);
 
 #endif

+ 9 - 17
gui.cpp

@@ -370,29 +370,21 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
       Special case for well-known accounts.
       Ignore the password if we're editing and the username hasn't changed.
     */
-    if (! requires_password(service->username)) {
-      if (is_localsystem(service->username)) {
+    const TCHAR *well_known = well_known_username(service->username);
+    if (well_known) {
+      if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) {
         HeapFree(GetProcessHeap(), 0, service->username);
         service->username = 0;
         service->usernamelen = 0;
       }
       else {
-        TCHAR *canon = canonical_username(service->username);
-        HeapFree(GetProcessHeap(), 0, service->username);
-        service->username = 0;
-        service->usernamelen = 0;
-        if (canon) {
-          service->usernamelen = _tcslen(canon) + 1;
-          service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->usernamelen * sizeof(TCHAR));
-          if (! service->username) {
-            LocalFree(canon);
-            print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("install()"));
-            return 6;
-          }
-          memmove(service->username, canon, service->usernamelen * sizeof(TCHAR));
-          LocalFree(canon);
+        service->usernamelen = _tcslen(well_known) + 1;
+        service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->usernamelen * sizeof(TCHAR));
+        if (! service->username) {
+          print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("install()"));
+          return 6;
         }
-        else return 6;
+        memmove(service->username, well_known, service->usernamelen * sizeof(TCHAR));
       }
     }
     else {

+ 2 - 1
service.cpp

@@ -934,7 +934,8 @@ int edit_service(nssm_service_t *service, bool editing) {
   }
   else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;
 
-  if (requires_password(username)) {
+  if (well_known_username(username)) password = _T("");
+  else {
     if (grant_logon_as_service(username)) {
       print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
       return 5;

+ 17 - 26
settings.cpp

@@ -530,31 +530,25 @@ int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *n
   bool localsystem = false;
   TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
   TCHAR *password = 0;
-  TCHAR *canon = 0;
   if (additional) {
     username = (TCHAR *) additional;
     if (value && value->string) password = value->string;
   }
   else if (value && value->string) username = value->string;
 
-  if (requires_password(username)) {
-    if (! password) {
-      /* We need a password if the account requires it. */
-      print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
-      return -1;
-    }
+  const TCHAR *well_known = well_known_username(username);
+  size_t passwordsize = 0;
+  if (well_known) {
+    if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;
+    username = (TCHAR *) well_known;
+    password = _T("");
   }
-  else {
-    password = 0;
-    if (is_localsystem(username)) {
-      localsystem = true;
-      username = NSSM_LOCALSYSTEM_ACCOUNT;
-    }
-    else {
-      canon = canonical_username(username);
-      if (canon) username = canon;
-    }
+  else if (! password) {
+    /* We need a password if the account requires it. */
+    print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
+    return -1;
   }
+  else passwordsize = _tcslen(password) * sizeof(TCHAR);
 
   /*
     ChangeServiceConfig() will fail to set the username if the service is set
@@ -564,8 +558,7 @@ int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *n
   if (! localsystem) {
     QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
     if (! qsc) {
-      if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
-      if (canon) LocalFree(canon);
+      if (passwordsize) SecureZeroMemory(password, passwordsize);
       return -1;
     }
 
@@ -573,24 +566,22 @@ int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *n
     HeapFree(GetProcessHeap(), 0, qsc);
   }
 
-  if (password) {
+  if (! well_known) {
     if (grant_logon_as_service(username)) {
-      if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
-      if (canon) LocalFree(canon);
+      if (passwordsize) SecureZeroMemory(password, passwordsize);
       print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
       return -1;
     }
   }
 
   if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
-    if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
-    if (canon) LocalFree(canon);
+    if (passwordsize) SecureZeroMemory(password, passwordsize);
     print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
     return -1;
   }
-  if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
 
-  if (canon) LocalFree(canon);
+  if (passwordsize) SecureZeroMemory(password, passwordsize);
+
   if (localsystem) return 0;
 
   return 1;