From: Iain Patterson <me@iain.cx> Date: Mon, 1 Aug 2016 16:45:46 +0000 (+0100) Subject: Support virtual service accounts. X-Git-Url: http://git.iain.cx/?a=commitdiff_plain;h=71764fe41e37f36bcbc89ac62f664aeda4240790;p=nssm.git Support virtual service accounts. Specify the account name as "NT Service\<servicename>" or use the virtual account radio button in the GUI to run the service as a virtual service account. Thanks Christian Long. --- 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\<servicename>" 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 d9e4351..9f61384 100644 Binary files a/nssm.rc and b/nssm.rc differ diff --git a/resource.h b/resource.h index df5c6a3..3dcf6db 100644 --- a/resource.h +++ b/resource.h @@ -71,6 +71,7 @@ #define IDC_HOOK 1050 #define IDC_BROWSE_HOOK 1051 #define IDC_REDIRECT_HOOK 1052 +#define IDC_VIRTUAL_SERVICE 1053 // Next default values for new objects // @@ -78,7 +79,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 117 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1053 +#define _APS_NEXT_CONTROL_VALUE 1054 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/service.cpp b/service.cpp index 2d20ff3..c9f4700 100644 --- a/service.cpp +++ b/service.cpp @@ -1274,19 +1274,33 @@ int edit_service(nssm_service_t *service, bool editing) { TCHAR *username = 0; TCHAR *canon = 0; TCHAR *password = 0; + boolean virtual_account = false; if (service->usernamelen) { 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);