From 71764fe41e37f36bcbc89ac62f664aeda4240790 Mon Sep 17 00:00:00 2001 From: Iain Patterson Date: Mon, 1 Aug 2016 17:45:46 +0100 Subject: [PATCH] Support virtual service accounts. Specify the account name as "NT Service\" or use the virtual account radio button in the GUI to run the service as a virtual service account. Thanks Christian Long. --- README.txt | 2 ++ account.cpp | 28 +++++++++++++++++++++++---- account.h | 3 +++ gui.cpp | 53 ++++++++++++++++++++++++++++++++++----------------- nssm.rc | Bin 67178 -> 67226 bytes resource.h | 3 ++- service.cpp | 30 +++++++++++++++++++++-------- settings.cpp | 4 +++- 8 files changed, 92 insertions(+), 31 deletions(-) diff --git a/README.txt b/README.txt index ae6ca33..35df5b4 100644 --- a/README.txt +++ b/README.txt @@ -790,6 +790,7 @@ parameter can be omitted when using them: "LocalSystem" aka "System" aka "NT Authority\System" "LocalService" aka "Local Service" aka "NT Authority\Local Service" "NetworkService" aka "Network Service" aka "NT Authority\Network Service" + Virtual service account "NT Service\" The Start parameter is used to query or set the startup type of the service. @@ -1003,6 +1004,7 @@ Thanks to Mathias Breiner for help with Visual Studio and some registry fixes. Thanks to David Bremner for general tidyups. Thanks to Nabil Redmann for suggesting redirecting hooks' output. Thanks to Bader Aldurai for suggesting the process tree. +Thanks to Christian Long for suggesting virtual accounts. Licence ------- diff --git a/account.cpp b/account.cpp index 6150301..d16d51d 100644 --- a/account.cpp +++ b/account.cpp @@ -89,10 +89,12 @@ int username_sid(const TCHAR *username, SID **sid, LSA_HANDLE *policy) { } if (translated_sid->Use != SidTypeUser && translated_sid->Use != SidTypeWellKnownGroup) { - LsaFreeMemory(translated_domains); - LsaFreeMemory(translated_sid); - print_message(stderr, NSSM_GUI_INVALID_USERNAME, username); - return 6; + if (translated_sid->Use != SidTypeUnknown || _tcsnicmp(NSSM_VIRTUAL_SERVICE_ACCOUNT_DOMAIN _T("\\"), username, _tcslen(NSSM_VIRTUAL_SERVICE_ACCOUNT_DOMAIN) + 1)) { + LsaFreeMemory(translated_domains); + LsaFreeMemory(translated_sid); + print_message(stderr, NSSM_GUI_INVALID_USERNAME, username); + return 6; + } } LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex]; @@ -232,6 +234,24 @@ int is_localsystem(const TCHAR *username) { return ret; } +/* Does the username represent a virtual account for the service? */ +int is_virtual_account(const TCHAR *service_name, const TCHAR *username) { + if (! imports.IsWellKnownSid) return 0; + if (! service_name) return 0; + if (! username) return 0; + + size_t len = _tcslen(NSSM_VIRTUAL_SERVICE_ACCOUNT_DOMAIN) + _tcslen(service_name) + 2; + TCHAR *canon = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR)); + if (! canon) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("is_virtual_account")); + return 0; + } + _sntprintf_s(canon, len, _TRUNCATE, _T("%s\\%s"), NSSM_VIRTUAL_SERVICE_ACCOUNT_DOMAIN, service_name); + int ret = str_equiv(canon, username); + HeapFree(GetProcessHeap(), 0, canon); + return ret; +} + /* Get well-known alias for LocalSystem and friends. Returns a pointer to a static string. DO NOT try to free it. diff --git a/account.h b/account.h index 232e1f9..a4377b9 100644 --- a/account.h +++ b/account.h @@ -8,6 +8,8 @@ /* 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") +/* Virtual service accounts. */ +#define NSSM_VIRTUAL_SERVICE_ACCOUNT_DOMAIN _T("NT Service") /* This is explicitly a wide string. */ #define NSSM_LOGON_AS_SERVICE_RIGHT L"SeServiceLogonRight" @@ -17,6 +19,7 @@ int username_sid(const TCHAR *, SID **); int username_equiv(const TCHAR *, const TCHAR *); int canonicalise_username(const TCHAR *, TCHAR **); int is_localsystem(const TCHAR *); +int is_virtual_account(const TCHAR *, const TCHAR *); const TCHAR *well_known_sid(SID *); const TCHAR *well_known_username(const TCHAR *); int grant_logon_as_service(const TCHAR *); diff --git a/gui.cpp b/gui.cpp index d07af68..5fe3384 100644 --- a/gui.cpp +++ b/gui.cpp @@ -26,6 +26,13 @@ static HWND dialog(const TCHAR *templ, HWND parent, DLGPROC function) { return dialog(templ, parent, function, 0); } +static inline void set_logon_enabled(unsigned char interact_enabled, unsigned char credentials_enabled) { + EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_INTERACT), interact_enabled); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), credentials_enabled); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), credentials_enabled); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), credentials_enabled); +} + int nssm_gui(int resource, nssm_service_t *service) { /* Create window */ HWND dlg = dialog(MAKEINTRESOURCE(resource), 0, nssm_dlg, (LPARAM) service); @@ -86,15 +93,18 @@ int nssm_gui(int resource, nssm_service_t *service) { /* Log on tab. */ if (service->username) { - CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_ACCOUNT); - SetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_USERNAME, service->username); - EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_INTERACT), 0); - EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), 1); - EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), 1); - EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), 1); + if (is_virtual_account(service->name, service->username)) { + CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_VIRTUAL_SERVICE, IDC_VIRTUAL_SERVICE); + set_logon_enabled(0, 0); + } + else { + CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_VIRTUAL_SERVICE, IDC_ACCOUNT); + SetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_USERNAME, service->username); + set_logon_enabled(0, 1); + } } else { - CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_LOCALSYSTEM); + CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_VIRTUAL_SERVICE, IDC_LOCALSYSTEM); if (service->type & SERVICE_INTERACTIVE_PROCESS) SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_INTERACT, BM_SETCHECK, BST_CHECKED, 0); } @@ -268,13 +278,6 @@ static inline void set_timeout_enabled(unsigned long control, unsigned long depe EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], dependent), enabled); } -static inline void set_logon_enabled(unsigned char enabled) { - EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_INTERACT), ! enabled); - EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), enabled); - EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), enabled); - EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), enabled); -} - static inline void set_affinity_enabled(unsigned char enabled) { EnableWindow(GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY), enabled); } @@ -460,6 +463,18 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service service->password = 0; service->passwordlen = 0; } + else if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_VIRTUAL_SERVICE, BM_GETCHECK, 0, 0) & BST_CHECKED) { + if (service->username) HeapFree(GetProcessHeap(), 0, service->username); + service->usernamelen = _tcslen(NSSM_VIRTUAL_SERVICE_ACCOUNT_DOMAIN) + _tcslen(service->name) + 2; + service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->usernamelen * sizeof(TCHAR)); + if (! service->username) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("account name"), _T("install()")); + return 6; + } + _sntprintf_s(service->username, service->usernamelen, _TRUNCATE, _T("%s\\%s"), NSSM_VIRTUAL_SERVICE_ACCOUNT_DOMAIN, service->name); + service->password = 0; + service->passwordlen = 0; + } else { /* Username. */ service->usernamelen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), WM_GETTEXTLENGTH, 0, 0); @@ -976,11 +991,15 @@ INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) { /* Log on. */ case IDC_LOCALSYSTEM: - set_logon_enabled(0); + set_logon_enabled(1, 0); + break; + + case IDC_VIRTUAL_SERVICE: + set_logon_enabled(0, 0); break; case IDC_ACCOUNT: - set_logon_enabled(1); + set_logon_enabled(0, 1); break; /* Affinity. */ @@ -1127,7 +1146,7 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) { /* Set defaults. */ CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_LOCALSYSTEM); - set_logon_enabled(0); + set_logon_enabled(1, 0); /* Dependencies tab. */ tab.pszText = message_string(NSSM_GUI_TAB_DEPENDENCIES); diff --git a/nssm.rc b/nssm.rc index d9e4351356e4236e84911ad59d3e11be50661002..9f6138413901e114afd095552c6b81612ff7c448 100644 GIT binary patch delta 472 zcmaFW!!oOvWy2M1-Y|wth9ZU%hEj$^hMdV4*@P$0(UOr4WAJ1MVhCXfWpHHhVTfl4 zW^e`aL88u+FPe!n8!{M7cKj74^40H~D=Sfa}q9p@#n$+YC^=2r}TX0%$azLX4$cc<5lNGD{ z!A>dAl_K4tKo1p8D!19})$HQOi|pRS$&FpYvUps`;5xbSf-%^A4Hqp?+(%0ADKUZq VwQ#cEFPqJ7%`%Le^{&|%0069jkum@P delta 304 zcmbQ$%krv+Wy2Ni$u&AolmD4%UX)v37;EKXzyDJ=%&xt7UIy2-musernamelen) { username = service->username; - if (canonicalise_username(username, &canon)) return 5; - if (service->passwordlen) password = service->password; + if (is_virtual_account(service->name, username)) { + virtual_account = true; + canon = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (service->usernamelen + 1) * sizeof(TCHAR)); + if (! canon) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("edit_service()")); + return 5; + } + memmove(canon, username, (service->usernamelen + 1) * sizeof(TCHAR)); + } + else { + if (canonicalise_username(username, &canon)) return 5; + if (service->passwordlen) password = service->password; + } } else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT; - if (well_known_username(canon)) password = _T(""); - else { - if (grant_logon_as_service(canon)) { - if (canon != username) HeapFree(GetProcessHeap(), 0, canon); - print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username); - return 5; + if (! virtual_account) { + if (well_known_username(canon)) password = _T(""); + else { + if (grant_logon_as_service(canon)) { + if (canon != username) HeapFree(GetProcessHeap(), 0, canon); + print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username); + return 5; + } } } diff --git a/settings.cpp b/settings.cpp index f7346cb..11984bc 100644 --- a/settings.cpp +++ b/settings.cpp @@ -1074,6 +1074,7 @@ int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *n That means the username is actually passed in the additional parameter. */ bool localsystem = false; + bool virtual_account = false; TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT; TCHAR *password = 0; if (additional) { @@ -1089,6 +1090,7 @@ int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *n username = (TCHAR *) well_known; password = _T(""); } + else if (is_virtual_account(service_name, username)) virtual_account = true; else if (! password) { /* We need a password if the account requires it. */ print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name); @@ -1112,7 +1114,7 @@ int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *n HeapFree(GetProcessHeap(), 0, qsc); } - if (! well_known) { + if (! well_known && ! virtual_account) { if (grant_logon_as_service(username)) { if (passwordsize) SecureZeroMemory(password, passwordsize); print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username); -- 2.20.1