if (open_lsa_policy(policy)) return 1;
}
+ /*
+ LsaLookupNames() can't look up .\username but can look up
+ %COMPUTERNAME%\username. ChangeServiceConfig() writes .\username to the
+ registry when %COMPUTERNAME%\username is a passed as a parameter. We
+ need to preserve .\username when calling ChangeServiceConfig() without
+ changing the username, but expand to %COMPUTERNAME%\username when calling
+ LsaLookupNames().
+ */
+ TCHAR *expanded;
+ unsigned long expandedlen;
+ if (_tcsnicmp(_T(".\\"), username, 2)) {
+ expandedlen = (unsigned long) (_tcslen(username) + 1) * sizeof(TCHAR);
+ expanded = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, expandedlen);
+ if (! expanded) {
+ print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("expanded"), _T("username_sid"));
+ if (policy == &handle) LsaClose(handle);
+ return 2;
+ }
+ memmove(expanded, username, expandedlen);
+ }
+ else {
+ TCHAR computername[MAX_COMPUTERNAME_LENGTH + 1];
+ expandedlen = _countof(computername);
+ GetComputerName(computername, &expandedlen);
+ expandedlen += (unsigned long) _tcslen(username);
+
+ expanded = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, expandedlen * sizeof(TCHAR));
+ if (! expanded) {
+ print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("expanded"), _T("username_sid"));
+ if (policy == &handle) LsaClose(handle);
+ return 2;
+ }
+ _sntprintf_s(expanded, expandedlen, _TRUNCATE, _T("%s\\%s"), computername, username + 2);
+ }
+
LSA_UNICODE_STRING lsa_username;
#ifdef UNICODE
- lsa_username.Buffer = (wchar_t *) username;
- lsa_username.Length = (unsigned short) _tcslen(username) * sizeof(TCHAR);
+ lsa_username.Buffer = (wchar_t *) expanded;
+ lsa_username.Length = (unsigned short) _tcslen(expanded) * sizeof(TCHAR);
lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR);
#else
size_t buflen;
- mbstowcs_s(&buflen, NULL, 0, username, _TRUNCATE);
+ mbstowcs_s(&buflen, NULL, 0, expanded, _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);
+ if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, expanded, _TRUNCATE);
else {
if (policy == &handle) LsaClose(handle);
+ HeapFree(GetProcessHeap(), 0, expanded);
print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("username_sid()"));
- return 2;
+ return 4;
}
#endif
#ifndef UNICODE
HeapFree(GetProcessHeap(), 0, lsa_username.Buffer);
#endif
+ HeapFree(GetProcessHeap(), 0, expanded);
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;
+ return 5;
}
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;
+ return 6;
}
LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex];
LsaFreeMemory(translated_domains);
LsaFreeMemory(translated_sid);
print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
- return 5;
+ return 7;
}
/* GetSidSubAuthority*() return pointers! */
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;
+ print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("username_sid"));
+ return 8;
}
unsigned long error;
LsaFreeMemory(translated_domains);
LsaFreeMemory(translated_sid);
print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error));
- return 7;
+ return 9;
}
for (unsigned char i = 0; i <= *n; i++) {
}
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;
+ ret = 10;
}
LsaFreeMemory(translated_domains);
return username_sid(username, sid, 0);
}
+int canonicalise_username(const TCHAR *username, TCHAR **canon) {
+ LSA_HANDLE policy;
+ if (open_lsa_policy(&policy)) return 1;
+
+ SID *sid;
+ if (username_sid(username, &sid, &policy)) return 2;
+ PSID sids = { sid };
+
+ LSA_REFERENCED_DOMAIN_LIST *translated_domains;
+ LSA_TRANSLATED_NAME *translated_name;
+ NTSTATUS status = LsaLookupSids(policy, 1, &sids, &translated_domains, &translated_name);
+ if (status) {
+ LsaFreeMemory(translated_domains);
+ LsaFreeMemory(translated_name);
+ print_message(stderr, NSSM_MESSAGE_LSALOOKUPSIDS_FAILED, error_string(LsaNtStatusToWinError(status)));
+ return 3;
+ }
+
+ LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_name->DomainIndex];
+ LSA_UNICODE_STRING lsa_canon;
+ lsa_canon.Length = translated_name->Name.Length + trust->Name.Length + sizeof(wchar_t);
+ lsa_canon.MaximumLength = lsa_canon.Length + sizeof(wchar_t);
+ lsa_canon.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lsa_canon.MaximumLength);
+ if (! lsa_canon.Buffer) {
+ LsaFreeMemory(translated_domains);
+ LsaFreeMemory(translated_name);
+ print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lsa_canon"), _T("username_sid"));
+ return 9;
+ }
+
+ /* Buffer is wchar_t but Length is in bytes. */
+ memmove((char *) lsa_canon.Buffer, trust->Name.Buffer, trust->Name.Length);
+ memmove((char *) lsa_canon.Buffer + trust->Name.Length, L"\\", sizeof(wchar_t));
+ memmove((char *) lsa_canon.Buffer + trust->Name.Length + sizeof(wchar_t), translated_name->Name.Buffer, translated_name->Name.Length);
+
+#ifdef UNICODE
+ *canon = lsa_canon.Buffer;
+#else
+ size_t buflen;
+ wcstombs_s(&buflen, NULL, 0, lsa_canon.Buffer, _TRUNCATE);
+ *canon = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);
+ if (! *canon) {
+ LsaFreeMemory(translated_domains);
+ LsaFreeMemory(translated_name);
+ print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("username_sid"));
+ return 10;
+ }
+ wcstombs_s(&buflen, *canon, buflen, lsa_canon.Buffer, _TRUNCATE);
+ HeapFree(GetProcessHeap(), 0, lsa_canon.Buffer);
+#endif
+
+ LsaFreeMemory(translated_domains);
+ LsaFreeMemory(translated_name);
+
+ return 0;
+}
+
/* Do two usernames map to the same SID? */
int username_equiv(const TCHAR *a, const TCHAR *b) {
SID *sid_a, *sid_b;
}
/*
- 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 (! username) return NSSM_LOCALSYSTEM_ACCOUNT;
+ 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;
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;