19dc66e82d5c75bc900f24c33d6c86383310a591
[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 (! username) return NSSM_LOCALSYSTEM_ACCOUNT;
170   if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return NSSM_LOCALSYSTEM_ACCOUNT;
171   SID *sid;
172   int r = username_sid(username, &sid);
173   if (username_sid(username, &sid)) return 0;
174
175   const TCHAR *well_known = well_known_sid(sid);
176   FreeSid(sid);
177
178   return well_known;
179 }
180
181 int grant_logon_as_service(const TCHAR *username) {
182   if (! username) return 0;
183
184   /* Open Policy object. */
185   LSA_OBJECT_ATTRIBUTES attributes;
186   ZeroMemory(&attributes, sizeof(attributes));
187
188   LSA_HANDLE policy;
189   NTSTATUS status;
190
191   if (open_lsa_policy(&policy)) return 1;
192
193   /* Look up SID for the account. */
194   SID *sid;
195   if (username_sid(username, &sid, &policy)) {
196     LsaClose(policy);
197     return 2;
198   }
199
200   /*
201     Shouldn't happen because it should have been checked before callling this function.
202   */
203   if (well_known_sid(sid)) {
204     LsaClose(policy);
205     return 3;
206   }
207
208   /* Check if the SID has the "Log on as a service" right. */
209   LSA_UNICODE_STRING lsa_right;
210   lsa_right.Buffer = NSSM_LOGON_AS_SERVICE_RIGHT;
211   lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t);
212   lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t);
213
214   LSA_UNICODE_STRING *rights;
215   unsigned long count = ~0;
216   status = LsaEnumerateAccountRights(policy, sid, &rights, &count);
217   if (status) {
218     /*
219       If the account has no rights set LsaEnumerateAccountRights() will return
220       STATUS_OBJECT_NAME_NOT_FOUND and set count to 0.
221     */
222     unsigned long error = LsaNtStatusToWinError(status);
223     if (error != ERROR_FILE_NOT_FOUND) {
224       FreeSid(sid);
225       LsaClose(policy);
226       print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error));
227       return 4;
228     }
229   }
230
231   for (unsigned long i = 0; i < count; i++) {
232     if (rights[i].Length != lsa_right.Length) continue;
233     if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue;
234     /* The SID has the right. */
235     FreeSid(sid);
236     LsaFreeMemory(rights);
237     LsaClose(policy);
238     return 0;
239   }
240   LsaFreeMemory(rights);
241
242   /* Add the right. */
243   status = LsaAddAccountRights(policy, sid, &lsa_right, 1);
244   FreeSid(sid);
245   LsaClose(policy);
246   if (status) {
247     print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status)));
248     return 5;
249   }
250
251   print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username);
252   return 0;
253 }