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