From cca8d28295ce27b7c996b47badc6a1e3a6a34e65 Mon Sep 17 00:00:00 2001 From: Iain Patterson Date: Wed, 29 Jan 2014 20:30:55 +0000 Subject: [PATCH] Handle well-known account names. Certain well-known accounts can be used to start a service without entering a password. "LocalSystem" is the default value of ObjectName in the registry. It is not a real account but is an alias for the "NT Authority\System" account. "NT Authority\Local Service" has minimal local and no network privileges. "NT Authority\Network Service" has minimal local and network privileges. NSSM now recognises these well-known accounts and will accept them as ObjectName parameters without complaining that no password was enter. With the exception of LocalSystem, each can be specified without the "NT Authority" pseudo-domain prefix. --- README.txt | 7 ++ account.cpp | 388 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ account.h | 22 ++++ gui.cpp | 30 ++++- messages.mc | Bin 144888 -> 146016 bytes nssm.cpp | 6 +- nssm.h | 1 + nssm.vcproj | 8 ++ service.cpp | 160 +----------------------- service.h | 4 - settings.cpp | 47 +++++--- 11 files changed, 493 insertions(+), 180 deletions(-) create mode 100644 account.cpp create mode 100644 account.h diff --git a/README.txt b/README.txt index de01aa4..df918bd 100644 --- a/README.txt +++ b/README.txt @@ -516,6 +516,13 @@ invocation is valid and will have the expected effect. nssm set ObjectName correct horse battery staple +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" + The Start parameter is used to query or set the startup type of the service. Valid service startup types are as follows: diff --git a/account.cpp b/account.cpp new file mode 100644 index 0000000..9c2ff70 --- /dev/null +++ b/account.cpp @@ -0,0 +1,388 @@ +#include "nssm.h" + +#include + +extern imports_t imports; + +/* Open Policy object. */ +int open_lsa_policy(LSA_HANDLE *policy) { + LSA_OBJECT_ATTRIBUTES attributes; + ZeroMemory(&attributes, sizeof(attributes)); + + NTSTATUS status = LsaOpenPolicy(0, &attributes, POLICY_ALL_ACCESS, policy); + if (status) { + print_message(stderr, NSSM_MESSAGE_LSAOPENPOLICY_FAILED, error_string(LsaNtStatusToWinError(status))); + return 1; + } + + return 0; +} + +/* Look up SID for an account. */ +int username_sid(const TCHAR *username, SID **sid, LSA_HANDLE *policy) { + LSA_HANDLE handle; + if (! policy) { + policy = &handle; + if (open_lsa_policy(policy)) return 1; + } + + LSA_UNICODE_STRING lsa_username; +#ifdef UNICODE + lsa_username.Buffer = (wchar_t *) username; + lsa_username.Length = (unsigned short) _tcslen(username) * sizeof(TCHAR); + lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR); +#else + size_t buflen; + mbstowcs_s(&buflen, NULL, 0, username, _TRUNCATE); + lsa_username.MaximumLength = (unsigned short) buflen * sizeof(wchar_t); + lsa_username.Length = lsa_username.MaximumLength - sizeof(wchar_t); + lsa_username.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, lsa_username.MaximumLength); + if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, username, _TRUNCATE); + else { + if (policy == &handle) LsaClose(handle); + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("username_sid()")); + return 2; + } +#endif + + LSA_REFERENCED_DOMAIN_LIST *translated_domains; + LSA_TRANSLATED_SID *translated_sid; + NTSTATUS status = LsaLookupNames(*policy, 1, &lsa_username, &translated_domains, &translated_sid); +#ifndef UNICODE + HeapFree(GetProcessHeap(), 0, lsa_username.Buffer); +#endif + if (policy == &handle) LsaClose(handle); + if (status) { + LsaFreeMemory(translated_domains); + LsaFreeMemory(translated_sid); + print_message(stderr, NSSM_MESSAGE_LSALOOKUPNAMES_FAILED, username, error_string(LsaNtStatusToWinError(status))); + return 3; + } + + if (translated_sid->Use != SidTypeUser && translated_sid->Use != SidTypeWellKnownGroup) { + LsaFreeMemory(translated_domains); + LsaFreeMemory(translated_sid); + print_message(stderr, NSSM_GUI_INVALID_USERNAME, username); + return 4; + } + + LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex]; + if (! trust || ! IsValidSid(trust->Sid)) { + LsaFreeMemory(translated_domains); + LsaFreeMemory(translated_sid); + print_message(stderr, NSSM_GUI_INVALID_USERNAME, username); + return 5; + } + + /* GetSidSubAuthority*() return pointers! */ + unsigned char *n = GetSidSubAuthorityCount(trust->Sid); + + /* Convert translated SID to SID. */ + *sid = (SID *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetSidLengthRequired(*n + 1)); + if (! *sid) { + LsaFreeMemory(translated_domains); + LsaFreeMemory(translated_sid); + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("grant_logon_as_service")); + return 6; + } + + unsigned long error; + if (! InitializeSid(*sid, GetSidIdentifierAuthority(trust->Sid), *n + 1)) { + error = GetLastError(); + HeapFree(GetProcessHeap(), 0, *sid); + LsaFreeMemory(translated_domains); + LsaFreeMemory(translated_sid); + print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error)); + return 7; + } + + for (unsigned char i = 0; i <= *n; i++) { + unsigned long *sub = GetSidSubAuthority(*sid, i); + if (i < *n) *sub = *GetSidSubAuthority(trust->Sid, i); + else *sub = translated_sid->RelativeId; + } + + int ret = 0; + if (translated_sid->Use == SidTypeWellKnownGroup && requires_password(*sid)) { + print_message(stderr, NSSM_GUI_INVALID_USERNAME, username); + ret = 8; + } + + LsaFreeMemory(translated_domains); + LsaFreeMemory(translated_sid); + + return ret; +} + +int username_sid(const TCHAR *username, SID **sid) { + return username_sid(username, sid, 0); +} + +/* Do two usernames map to the same SID? */ +int username_equiv(const TCHAR *a, const TCHAR *b) { + SID *sid_a, *sid_b; + if (username_sid(a, &sid_a)) return 0; + + if (username_sid(b, &sid_b)) { + FreeSid(sid_a); + return 0; + } + + int ret = 0; + if (EqualSid(sid_a, sid_b)) ret = 1; + + FreeSid(sid_a); + FreeSid(sid_b); + + return ret; +} + +/* Does the username represent the LocalSystem account? */ +int is_localsystem(const TCHAR *username) { + if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 1; + if (! imports.IsWellKnownSid) return 0; + + SID *sid; + if (username_sid(username, &sid)) return 0; + + int ret = 0; + if (imports.IsWellKnownSid(sid, WinLocalSystemSid)) ret = 1; + + FreeSid(sid); + + return ret; +} + +/* + Find the canonical name for a well-known account name. + MUST ONLY BE USED for well-known account names. + Must call LocalFree() on result. +*/ +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; +} + +/* Does the username require a password? */ +int requires_password(const TCHAR *username) { + if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 0; + + SID *sid; + int r = username_sid(username, &sid); + if (username_sid(username, &sid)) return 0; + + int ret = 0; + ret = requires_password(sid); + + FreeSid(sid); + + return ret; +} + +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; + ZeroMemory(&attributes, sizeof(attributes)); + + LSA_HANDLE policy; + NTSTATUS status; + + if (open_lsa_policy(&policy)) return 1; + + /* Look up SID for the account. */ + SID *sid; + if (username_sid(username, &sid, &policy)) { + LsaClose(policy); + return 2; + } + + /* 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; + lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t); + lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t); + + LSA_UNICODE_STRING *rights; + unsigned long count = ~0; + status = LsaEnumerateAccountRights(policy, sid, &rights, &count); + if (status) { + /* + If the account has no rights set LsaEnumerateAccountRights() will return + STATUS_OBJECT_NAME_NOT_FOUND and set count to 0. + */ + unsigned long error = LsaNtStatusToWinError(status); + if (error != ERROR_FILE_NOT_FOUND) { + FreeSid(sid); + LsaClose(policy); + print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error)); + return 4; + } + } + + for (unsigned long i = 0; i < count; i++) { + if (rights[i].Length != lsa_right.Length) continue; + if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue; + /* The SID has the right. */ + FreeSid(sid); + LsaFreeMemory(rights); + LsaClose(policy); + return 0; + } + LsaFreeMemory(rights); + + /* Add the right. */ + status = LsaAddAccountRights(policy, sid, &lsa_right, 1); + FreeSid(sid); + LsaClose(policy); + if (status) { + print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status))); + return 5; + } + + print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username); + return 0; +} diff --git a/account.h b/account.h new file mode 100644 index 0000000..f16a899 --- /dev/null +++ b/account.h @@ -0,0 +1,22 @@ +#ifndef ACCOUNT_H +#define ACCOUNT_H + +#include + +/* Not really an account. The canonical name is NT Authority\System. */ +#define NSSM_LOCALSYSTEM_ACCOUNT _T("LocalSystem") +/* 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 *); +int grant_logon_as_service(const TCHAR *); + +#endif diff --git a/gui.cpp b/gui.cpp index 888ef02..9f6f9ba 100644 --- a/gui.cpp +++ b/gui.cpp @@ -367,13 +367,33 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service } /* - Special case LOCALSYSTEM. + Special case for well-known accounts. Ignore the password if we're editing and the username hasn't changed. */ - if (str_equiv(service->username, NSSM_LOCALSYSTEM_ACCOUNT)) { - HeapFree(GetProcessHeap(), 0, service->username); - service->username = 0; - service->usernamelen = 0; + if (! requires_password(service->username)) { + if (is_localsystem(service->username)) { + 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); + } + else return 6; + } } else { /* Password. */ diff --git a/messages.mc b/messages.mc index 79234c47e934eeb63e3febd13d74bc2da3bb3bfa..3931273a7ba7fc369a56da1123c7bdd4c420703e 100644 GIT binary patch delta 309 zcmezIn&ZJ2jtw3&CMgO`mU9$h4QB9UaGCxvh*5KLf@9v~DK0{qX$<)cMGOiIU?~PY z1}+94hGK?9Aej$@*$kx&1wgq>hLp*IPN^j8(4Oq@Ol0x`JAuh@P7);QPV~;7TvN#< z9?swjbd3*#H-jI8KSMZ>1iN*zqekX*gM3CMZdIUzp&pnlI9GS_KNped0l|!XC=!9{ b`jZ2mv4GryDQUPWvN>SJ_JA3TE9L?KSTIae delta 18 acmaFxh2zI-jtw3&nm5eZzF`jIhPeP}Aqsc^ diff --git a/nssm.cpp b/nssm.cpp index 35001d3..e29ead6 100644 --- a/nssm.cpp +++ b/nssm.cpp @@ -77,6 +77,9 @@ int _tmain(int argc, TCHAR **argv) { /* Remember if we are admin */ check_admin(); + /* Set up function pointers. */ + if (get_imports()) exit(111); + /* Elevate */ if (argc > 1) { /* @@ -137,9 +140,6 @@ int _tmain(int argc, TCHAR **argv) { This will save time when running with no arguments from a command prompt. */ if (! GetStdHandle(STD_INPUT_HANDLE)) { - /* Set up function pointers. */ - if (get_imports()) exit(111); - /* Start service magic */ SERVICE_TABLE_ENTRY table[] = { { NSSM, service_main }, { 0, 0 } }; if (! StartServiceCtrlDispatcher(table)) { diff --git a/nssm.h b/nssm.h index 1219df2..f34346b 100644 --- a/nssm.h +++ b/nssm.h @@ -41,6 +41,7 @@ #include #include #include "service.h" +#include "account.h" #include "console.h" #include "env.h" #include "event.h" diff --git a/nssm.vcproj b/nssm.vcproj index 88d3056..8923120 100755 --- a/nssm.vcproj +++ b/nssm.vcproj @@ -403,6 +403,10 @@ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" > + + @@ -644,6 +648,10 @@ Filter="h;hpp;hxx;hm;inl" > + + diff --git a/service.cpp b/service.cpp index 91e726f..d01ece0 100644 --- a/service.cpp +++ b/service.cpp @@ -1,8 +1,5 @@ #include "nssm.h" -/* This is explicitly a wide string. */ -#define NSSM_LOGON_AS_SERVICE_RIGHT L"SeServiceLogonRight" - bool is_admin; bool use_critical_section; @@ -475,7 +472,7 @@ int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG * if (! qsc) return 1; - if (str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) return 0; + if (is_localsystem(qsc->lpServiceStartName)) return 0; size_t len = _tcslen(qsc->lpServiceStartName); *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR)); @@ -490,153 +487,6 @@ int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG * return 0; } -int grant_logon_as_service(const TCHAR *username) { - if (! username) return 0; - if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 0; - - /* Open Policy object. */ - LSA_OBJECT_ATTRIBUTES attributes; - ZeroMemory(&attributes, sizeof(attributes)); - - LSA_HANDLE policy; - - NTSTATUS status = LsaOpenPolicy(0, &attributes, POLICY_ALL_ACCESS, &policy); - if (status) { - print_message(stderr, NSSM_MESSAGE_LSAOPENPOLICY_FAILED, error_string(LsaNtStatusToWinError(status))); - return 1; - } - - /* Look up SID for the account. */ - LSA_UNICODE_STRING lsa_username; -#ifdef UNICODE - lsa_username.Buffer = (wchar_t *) username; - lsa_username.Length = (unsigned short) _tcslen(username) * sizeof(TCHAR); - lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR); -#else - size_t buflen; - mbstowcs_s(&buflen, NULL, 0, username, _TRUNCATE); - lsa_username.MaximumLength = (unsigned short) buflen * sizeof(wchar_t); - lsa_username.Length = lsa_username.MaximumLength - sizeof(wchar_t); - lsa_username.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, lsa_username.MaximumLength); - if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, username, _TRUNCATE); - else { - LsaClose(policy); - print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("grant_logon_as_service()")); - return 2; - } -#endif - - LSA_REFERENCED_DOMAIN_LIST *translated_domains; - LSA_TRANSLATED_SID *translated_sid; - status = LsaLookupNames(policy, 1, &lsa_username, &translated_domains, &translated_sid); -#ifndef UNICODE - HeapFree(GetProcessHeap(), 0, lsa_username.Buffer); -#endif - if (status) { - LsaFreeMemory(translated_domains); - LsaFreeMemory(translated_sid); - LsaClose(policy); - print_message(stderr, NSSM_MESSAGE_LSALOOKUPNAMES_FAILED, username, error_string(LsaNtStatusToWinError(status))); - return 3; - } - - if (translated_sid->Use != SidTypeUser) { - LsaFreeMemory(translated_domains); - LsaFreeMemory(translated_sid); - LsaClose(policy); - print_message(stderr, NSSM_GUI_INVALID_USERNAME, username); - return 4; - } - - LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex]; - if (! trust || ! IsValidSid(trust->Sid)) { - LsaFreeMemory(translated_domains); - LsaFreeMemory(translated_sid); - LsaClose(policy); - print_message(stderr, NSSM_GUI_INVALID_USERNAME, username); - return 4; - } - - /* GetSidSubAuthority*() return pointers! */ - unsigned char *n = GetSidSubAuthorityCount(trust->Sid); - - /* Convert translated SID to SID. */ - SID *sid = (SID *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetSidLengthRequired(*n + 1)); - if (! sid) { - LsaFreeMemory(translated_domains); - LsaFreeMemory(translated_sid); - LsaClose(policy); - print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("grant_logon_as_service")); - return 4; - } - - unsigned long error; - if (! InitializeSid(sid, GetSidIdentifierAuthority(trust->Sid), *n + 1)) { - error = GetLastError(); - HeapFree(GetProcessHeap(), 0, sid); - LsaFreeMemory(translated_domains); - LsaFreeMemory(translated_sid); - LsaClose(policy); - print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error)); - return 5; - } - - for (unsigned char i = 0; i <= *n; i++) { - unsigned long *sub = GetSidSubAuthority(sid, i); - if (i < *n) *sub = *GetSidSubAuthority(trust->Sid, i); - else *sub = translated_sid->RelativeId; - } - - LsaFreeMemory(translated_domains); - LsaFreeMemory(translated_sid); - - /* 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; - lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t); - lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t); - - LSA_UNICODE_STRING *rights; - unsigned long count = ~0; - status = LsaEnumerateAccountRights(policy, sid, &rights, &count); - if (status) { - /* - If the account has no rights set LsaEnumerateAccountRights() will return - STATUS_OBJECT_NAME_NOT_FOUND and set count to 0. - */ - error = LsaNtStatusToWinError(status); - if (error != ERROR_FILE_NOT_FOUND) { - HeapFree(GetProcessHeap(), 0, sid); - LsaClose(policy); - print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error)); - return 4; - } - } - - for (unsigned long i = 0; i < count; i++) { - if (rights[i].Length != lsa_right.Length) continue; - if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue; - /* The SID has the right. */ - HeapFree(GetProcessHeap(), 0, sid); - LsaFreeMemory(rights); - LsaClose(policy); - return 0; - } - LsaFreeMemory(rights); - - /* Add the right. */ - status = LsaAddAccountRights(policy, sid, &lsa_right, 1); - HeapFree(GetProcessHeap(), 0, sid); - LsaClose(policy); - if (status) { - print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status))); - return 5; - } - - print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username); - return 0; -} - /* Set default values which aren't zero. */ void set_nssm_service_defaults(nssm_service_t *service) { if (! service) return; @@ -1084,9 +934,11 @@ int edit_service(nssm_service_t *service, bool editing) { } else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT; - if (grant_logon_as_service(username)) { - print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username); - return 5; + if (requires_password(username)) { + if (grant_logon_as_service(username)) { + print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username); + return 5; + } } if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) { diff --git a/service.h b/service.h index 496b54a..68c0147 100644 --- a/service.h +++ b/service.h @@ -1,8 +1,6 @@ #ifndef SERVICE_H #define SERVICE_H -#include - /* MSDN says the commandline in CreateProcess() is limited to 32768 characters and the application name to MAX_PATH. @@ -19,7 +17,6 @@ #define ACTION_LEN 16 -#define NSSM_LOCALSYSTEM_ACCOUNT _T("LocalSystem") #define NSSM_KERNEL_DRIVER _T("SERVICE_KERNEL_DRIVER") #define NSSM_FILE_SYSTEM_DRIVER _T("SERVICE_FILE_SYSTEM_DRIVER") #define NSSM_WIN32_OWN_PROCESS _T("SERVICE_WIN32_OWN_PROCESS") @@ -126,7 +123,6 @@ int set_service_description(const TCHAR *, SC_HANDLE, TCHAR *); int get_service_description(const TCHAR *, SC_HANDLE, unsigned long, TCHAR *); int get_service_startup(const TCHAR *, SC_HANDLE, const QUERY_SERVICE_CONFIG *, unsigned long *); int get_service_username(const TCHAR *, const QUERY_SERVICE_CONFIG *, TCHAR **, size_t *); -int grant_logon_as_service(const TCHAR *); int pre_install_service(int, TCHAR **); int pre_remove_service(int, TCHAR **); int pre_edit_service(int, TCHAR **); diff --git a/settings.cpp b/settings.cpp index e7ec830..907524d 100644 --- a/settings.cpp +++ b/settings.cpp @@ -527,19 +527,32 @@ int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *n Logical syntax is: nssm set ObjectName That means the username is actually passed in the additional parameter. */ - bool localsystem = true; + bool localsystem = false; TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT; TCHAR *password = 0; + TCHAR *canon = 0; if (additional) { - if (! str_equiv(additional, NSSM_LOCALSYSTEM_ACCOUNT)) { - localsystem = false; - username = (TCHAR *) additional; - if (value && value->string) password = value->string; - else { - /* We need a password if the account is not LOCALSYSTEM. */ - print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name); - return -1; - } + 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; + } + } + else { + password = 0; + if (is_localsystem(username)) { + localsystem = true; + username = NSSM_LOCALSYSTEM_ACCOUNT; + } + else { + canon = canonical_username(username); + if (canon) username = canon; } } @@ -552,6 +565,7 @@ int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *n 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); return -1; } @@ -559,19 +573,24 @@ int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *n HeapFree(GetProcessHeap(), 0, qsc); } - if (grant_logon_as_service(username)) { - if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR)); - print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username); - return -1; + if (password) { + if (grant_logon_as_service(username)) { + if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR)); + if (canon) LocalFree(canon); + 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); 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 (localsystem) return 0; return 1; -- 2.7.4