account.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. #include "nssm.h"
  2. #include <sddl.h>
  3. extern imports_t imports;
  4. /* Open Policy object. */
  5. int open_lsa_policy(LSA_HANDLE *policy) {
  6. LSA_OBJECT_ATTRIBUTES attributes;
  7. ZeroMemory(&attributes, sizeof(attributes));
  8. NTSTATUS status = LsaOpenPolicy(0, &attributes, POLICY_ALL_ACCESS, policy);
  9. if (status) {
  10. print_message(stderr, NSSM_MESSAGE_LSAOPENPOLICY_FAILED, error_string(LsaNtStatusToWinError(status)));
  11. return 1;
  12. }
  13. return 0;
  14. }
  15. /* Look up SID for an account. */
  16. int username_sid(const TCHAR *username, SID **sid, LSA_HANDLE *policy) {
  17. LSA_HANDLE handle;
  18. if (! policy) {
  19. policy = &handle;
  20. if (open_lsa_policy(policy)) return 1;
  21. }
  22. /*
  23. LsaLookupNames() can't look up .\username but can look up
  24. %COMPUTERNAME%\username. ChangeServiceConfig() writes .\username to the
  25. registry when %COMPUTERNAME%\username is a passed as a parameter. We
  26. need to preserve .\username when calling ChangeServiceConfig() without
  27. changing the username, but expand to %COMPUTERNAME%\username when calling
  28. LsaLookupNames().
  29. */
  30. TCHAR *expanded;
  31. unsigned long expandedlen;
  32. if (_tcsnicmp(_T(".\\"), username, 2)) {
  33. expandedlen = (unsigned long) (_tcslen(username) + 1) * sizeof(TCHAR);
  34. expanded = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, expandedlen);
  35. if (! expanded) {
  36. print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("expanded"), _T("username_sid"));
  37. if (policy == &handle) LsaClose(handle);
  38. return 2;
  39. }
  40. memmove(expanded, username, expandedlen);
  41. }
  42. else {
  43. TCHAR computername[MAX_COMPUTERNAME_LENGTH + 1];
  44. expandedlen = _countof(computername);
  45. GetComputerName(computername, &expandedlen);
  46. expandedlen += (unsigned long) _tcslen(username);
  47. expanded = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, expandedlen * sizeof(TCHAR));
  48. if (! expanded) {
  49. print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("expanded"), _T("username_sid"));
  50. if (policy == &handle) LsaClose(handle);
  51. return 2;
  52. }
  53. _sntprintf_s(expanded, expandedlen, _TRUNCATE, _T("%s\\%s"), computername, username + 2);
  54. }
  55. LSA_UNICODE_STRING lsa_username;
  56. #ifdef UNICODE
  57. lsa_username.Buffer = (wchar_t *) expanded;
  58. lsa_username.Length = (unsigned short) _tcslen(expanded) * sizeof(TCHAR);
  59. lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR);
  60. #else
  61. size_t buflen;
  62. mbstowcs_s(&buflen, NULL, 0, expanded, _TRUNCATE);
  63. lsa_username.MaximumLength = (unsigned short) buflen * sizeof(wchar_t);
  64. lsa_username.Length = lsa_username.MaximumLength - sizeof(wchar_t);
  65. lsa_username.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, lsa_username.MaximumLength);
  66. if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, expanded, _TRUNCATE);
  67. else {
  68. if (policy == &handle) LsaClose(handle);
  69. HeapFree(GetProcessHeap(), 0, expanded);
  70. print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("username_sid()"));
  71. return 4;
  72. }
  73. #endif
  74. LSA_REFERENCED_DOMAIN_LIST *translated_domains;
  75. LSA_TRANSLATED_SID *translated_sid;
  76. NTSTATUS status = LsaLookupNames(*policy, 1, &lsa_username, &translated_domains, &translated_sid);
  77. #ifndef UNICODE
  78. HeapFree(GetProcessHeap(), 0, lsa_username.Buffer);
  79. #endif
  80. HeapFree(GetProcessHeap(), 0, expanded);
  81. if (policy == &handle) LsaClose(handle);
  82. if (status) {
  83. LsaFreeMemory(translated_domains);
  84. LsaFreeMemory(translated_sid);
  85. print_message(stderr, NSSM_MESSAGE_LSALOOKUPNAMES_FAILED, username, error_string(LsaNtStatusToWinError(status)));
  86. return 5;
  87. }
  88. if (translated_sid->Use != SidTypeUser && translated_sid->Use != SidTypeWellKnownGroup) {
  89. LsaFreeMemory(translated_domains);
  90. LsaFreeMemory(translated_sid);
  91. print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
  92. return 6;
  93. }
  94. LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex];
  95. if (! trust || ! IsValidSid(trust->Sid)) {
  96. LsaFreeMemory(translated_domains);
  97. LsaFreeMemory(translated_sid);
  98. print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
  99. return 7;
  100. }
  101. /* GetSidSubAuthority*() return pointers! */
  102. unsigned char *n = GetSidSubAuthorityCount(trust->Sid);
  103. /* Convert translated SID to SID. */
  104. *sid = (SID *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetSidLengthRequired(*n + 1));
  105. if (! *sid) {
  106. LsaFreeMemory(translated_domains);
  107. LsaFreeMemory(translated_sid);
  108. print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("username_sid"));
  109. return 8;
  110. }
  111. unsigned long error;
  112. if (! InitializeSid(*sid, GetSidIdentifierAuthority(trust->Sid), *n + 1)) {
  113. error = GetLastError();
  114. HeapFree(GetProcessHeap(), 0, *sid);
  115. LsaFreeMemory(translated_domains);
  116. LsaFreeMemory(translated_sid);
  117. print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error));
  118. return 9;
  119. }
  120. for (unsigned char i = 0; i <= *n; i++) {
  121. unsigned long *sub = GetSidSubAuthority(*sid, i);
  122. if (i < *n) *sub = *GetSidSubAuthority(trust->Sid, i);
  123. else *sub = translated_sid->RelativeId;
  124. }
  125. int ret = 0;
  126. if (translated_sid->Use == SidTypeWellKnownGroup && ! well_known_sid(*sid)) {
  127. print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
  128. ret = 10;
  129. }
  130. LsaFreeMemory(translated_domains);
  131. LsaFreeMemory(translated_sid);
  132. return ret;
  133. }
  134. int username_sid(const TCHAR *username, SID **sid) {
  135. return username_sid(username, sid, 0);
  136. }
  137. int canonicalise_username(const TCHAR *username, TCHAR **canon) {
  138. LSA_HANDLE policy;
  139. if (open_lsa_policy(&policy)) return 1;
  140. SID *sid;
  141. if (username_sid(username, &sid, &policy)) return 2;
  142. PSID sids = { sid };
  143. LSA_REFERENCED_DOMAIN_LIST *translated_domains;
  144. LSA_TRANSLATED_NAME *translated_name;
  145. NTSTATUS status = LsaLookupSids(policy, 1, &sids, &translated_domains, &translated_name);
  146. if (status) {
  147. LsaFreeMemory(translated_domains);
  148. LsaFreeMemory(translated_name);
  149. print_message(stderr, NSSM_MESSAGE_LSALOOKUPSIDS_FAILED, error_string(LsaNtStatusToWinError(status)));
  150. return 3;
  151. }
  152. LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_name->DomainIndex];
  153. LSA_UNICODE_STRING lsa_canon;
  154. lsa_canon.Length = translated_name->Name.Length + trust->Name.Length + sizeof(wchar_t);
  155. lsa_canon.MaximumLength = lsa_canon.Length + sizeof(wchar_t);
  156. lsa_canon.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lsa_canon.MaximumLength);
  157. if (! lsa_canon.Buffer) {
  158. LsaFreeMemory(translated_domains);
  159. LsaFreeMemory(translated_name);
  160. print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lsa_canon"), _T("username_sid"));
  161. return 9;
  162. }
  163. /* Buffer is wchar_t but Length is in bytes. */
  164. memmove((char *) lsa_canon.Buffer, trust->Name.Buffer, trust->Name.Length);
  165. memmove((char *) lsa_canon.Buffer + trust->Name.Length, L"\\", sizeof(wchar_t));
  166. memmove((char *) lsa_canon.Buffer + trust->Name.Length + sizeof(wchar_t), translated_name->Name.Buffer, translated_name->Name.Length);
  167. #ifdef UNICODE
  168. *canon = lsa_canon.Buffer;
  169. #else
  170. size_t buflen;
  171. wcstombs_s(&buflen, NULL, 0, lsa_canon.Buffer, _TRUNCATE);
  172. *canon = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);
  173. if (! *canon) {
  174. LsaFreeMemory(translated_domains);
  175. LsaFreeMemory(translated_name);
  176. print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("username_sid"));
  177. return 10;
  178. }
  179. wcstombs_s(&buflen, *canon, buflen, lsa_canon.Buffer, _TRUNCATE);
  180. HeapFree(GetProcessHeap(), 0, lsa_canon.Buffer);
  181. #endif
  182. LsaFreeMemory(translated_domains);
  183. LsaFreeMemory(translated_name);
  184. return 0;
  185. }
  186. /* Do two usernames map to the same SID? */
  187. int username_equiv(const TCHAR *a, const TCHAR *b) {
  188. SID *sid_a, *sid_b;
  189. if (username_sid(a, &sid_a)) return 0;
  190. if (username_sid(b, &sid_b)) {
  191. FreeSid(sid_a);
  192. return 0;
  193. }
  194. int ret = 0;
  195. if (EqualSid(sid_a, sid_b)) ret = 1;
  196. FreeSid(sid_a);
  197. FreeSid(sid_b);
  198. return ret;
  199. }
  200. /* Does the username represent the LocalSystem account? */
  201. int is_localsystem(const TCHAR *username) {
  202. if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 1;
  203. if (! imports.IsWellKnownSid) return 0;
  204. SID *sid;
  205. if (username_sid(username, &sid)) return 0;
  206. int ret = 0;
  207. if (imports.IsWellKnownSid(sid, WinLocalSystemSid)) ret = 1;
  208. FreeSid(sid);
  209. return ret;
  210. }
  211. /*
  212. Get well-known alias for LocalSystem and friends.
  213. Returns a pointer to a static string. DO NOT try to free it.
  214. */
  215. const TCHAR *well_known_sid(SID *sid) {
  216. if (! imports.IsWellKnownSid) return 0;
  217. if (imports.IsWellKnownSid(sid, WinLocalSystemSid)) return NSSM_LOCALSYSTEM_ACCOUNT;
  218. if (imports.IsWellKnownSid(sid, WinLocalServiceSid)) return NSSM_LOCALSERVICE_ACCOUNT;
  219. if (imports.IsWellKnownSid(sid, WinNetworkServiceSid)) return NSSM_NETWORKSERVICE_ACCOUNT;
  220. return 0;
  221. }
  222. const TCHAR *well_known_username(const TCHAR *username) {
  223. if (! username) return NSSM_LOCALSYSTEM_ACCOUNT;
  224. if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return NSSM_LOCALSYSTEM_ACCOUNT;
  225. SID *sid;
  226. if (username_sid(username, &sid)) return 0;
  227. const TCHAR *well_known = well_known_sid(sid);
  228. FreeSid(sid);
  229. return well_known;
  230. }
  231. int grant_logon_as_service(const TCHAR *username) {
  232. if (! username) return 0;
  233. /* Open Policy object. */
  234. LSA_OBJECT_ATTRIBUTES attributes;
  235. ZeroMemory(&attributes, sizeof(attributes));
  236. LSA_HANDLE policy;
  237. NTSTATUS status;
  238. if (open_lsa_policy(&policy)) return 1;
  239. /* Look up SID for the account. */
  240. SID *sid;
  241. if (username_sid(username, &sid, &policy)) {
  242. LsaClose(policy);
  243. return 2;
  244. }
  245. /*
  246. Shouldn't happen because it should have been checked before callling this function.
  247. */
  248. if (well_known_sid(sid)) {
  249. LsaClose(policy);
  250. return 3;
  251. }
  252. /* Check if the SID has the "Log on as a service" right. */
  253. LSA_UNICODE_STRING lsa_right;
  254. lsa_right.Buffer = NSSM_LOGON_AS_SERVICE_RIGHT;
  255. lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t);
  256. lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t);
  257. LSA_UNICODE_STRING *rights;
  258. unsigned long count = ~0;
  259. status = LsaEnumerateAccountRights(policy, sid, &rights, &count);
  260. if (status) {
  261. /*
  262. If the account has no rights set LsaEnumerateAccountRights() will return
  263. STATUS_OBJECT_NAME_NOT_FOUND and set count to 0.
  264. */
  265. unsigned long error = LsaNtStatusToWinError(status);
  266. if (error != ERROR_FILE_NOT_FOUND) {
  267. FreeSid(sid);
  268. LsaClose(policy);
  269. print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error));
  270. return 4;
  271. }
  272. }
  273. for (unsigned long i = 0; i < count; i++) {
  274. if (rights[i].Length != lsa_right.Length) continue;
  275. if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue;
  276. /* The SID has the right. */
  277. FreeSid(sid);
  278. LsaFreeMemory(rights);
  279. LsaClose(policy);
  280. return 0;
  281. }
  282. LsaFreeMemory(rights);
  283. /* Add the right. */
  284. status = LsaAddAccountRights(policy, sid, &lsa_right, 1);
  285. FreeSid(sid);
  286. LsaClose(policy);
  287. if (status) {
  288. print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status)));
  289. return 5;
  290. }
  291. print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username);
  292. return 0;
  293. }