1 /*
2 * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7 *
8 * See the COPYRIGHT file distributed with this work for additional
9 * information regarding copyright ownership.
10 */
11
12 /* Compiled with UNICODE */
13
14 #include "stdafx.h"
15
16 #include <windows.h>
17 #include <lm.h>
18 #include <ntsecapi.h>
19
20 #include <isc/ntgroups.h>
21 #include <isc/result.h>
22 #include "AccountInfo.h"
23
24 #define MAX_NAME_LENGTH 256
25
26 NTSTATUS
27 OpenPolicy(
28 LPWSTR ServerName, /* machine to open policy on (Unicode) */
29 DWORD DesiredAccess, /* desired access to policy */
30 PLSA_HANDLE PolicyHandle /* resultant policy handle */
31 );
32
33 BOOL
34 GetAccountSid(
35 LPTSTR SystemName, /* where to lookup account */
36 LPTSTR AccountName, /* account of interest */
37 PSID *Sid /* resultant buffer containing SID */
38 );
39
40 NTSTATUS
41 SetPrivilegeOnAccount(
42 LSA_HANDLE PolicyHandle, /* open policy handle */
43 PSID AccountSid, /* SID to grant privilege to */
44 LPWSTR PrivilegeName, /* privilege to grant (Unicode) */
45 BOOL bEnable /* enable or disable */
46 );
47
48 NTSTATUS
49 GetPrivilegesOnAccount(
50 LSA_HANDLE PolicyHandle, /* open policy handle */
51 PSID AccountSid, /* SID to grant privilege to */
52 wchar_t **PrivList, /* Ptr to List of Privileges found */
53 unsigned int *PrivCount /* total number of Privileges in list */
54 );
55
56 NTSTATUS
57 AddPrivilegeToAcccount(
58 LPTSTR AccountName, /* Name of the account */
59 LPWSTR PrivilegeName /* Privilege to Add */
60 );
61
62 void
63 InitLsaString(
64 PLSA_UNICODE_STRING LsaString, /* destination */
65 LPWSTR String /* source (Unicode) */
66 );
67
68 void
69 DisplayNtStatus(
70 LPSTR szAPI, /* pointer to function name (ANSI) */
71 NTSTATUS Status /* NTSTATUS error value */
72 );
73
74 void
75 DisplayWinError(
76 LPSTR szAPI, /* pointer to function name (ANSI) */
77 DWORD WinError /* DWORD WinError */
78 );
79
80 #ifndef STATUS_SUCCESS
81 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
82 #endif
83
84 /*
85 * Note that this code only retrieves the list of privileges of the
86 * requested account or group. However, all accounts belong to the
87 * Everyone group even though that group is not returned by the
88 * calls to get the groups to which that account belongs.
89 * The Everyone group has two privileges associated with it:
90 * SeChangeNotifyPrivilege and SeNetworkLogonRight
91 * It is not advisable to disable or remove these privileges
92 * from the group nor can the account be removed from the Everyone
93 * group
94 * The None group has no privileges associated with it and is the group
95 * to which an account belongs if it is associated with no group.
96 */
97
98 int
GetAccountPrivileges(char * name,wchar_t ** PrivList,unsigned int * PrivCount,char ** Accounts,unsigned int * totalAccounts,int maxAccounts)99 GetAccountPrivileges(char *name, wchar_t **PrivList, unsigned int *PrivCount,
100 char **Accounts, unsigned int *totalAccounts,
101 int maxAccounts)
102 {
103 LSA_HANDLE PolicyHandle;
104 TCHAR AccountName[256]; /* static account name buffer */
105 PSID pSid;
106 unsigned int i;
107 NTSTATUS Status;
108 isc_result_t istatus;
109 int iRetVal = RTN_ERROR; /* assume error from main */
110 int n;
111
112 /*
113 * Open the policy on the target machine.
114 */
115 if ((Status = OpenPolicy(NULL,
116 POLICY_LOOKUP_NAMES,
117 &PolicyHandle)) != STATUS_SUCCESS)
118 return (RTN_ERROR);
119
120 /*
121 * Let's see if the account exists. Return if not
122 */
123 n = wnsprintf(AccountName, sizeof(AccountName), TEXT("%hS"), name);
124 if (n < 0 || (size_t)n >= sizeof(AccountName)) {
125 LsaClose(PolicyHandle);
126 return (RTN_ERROR);
127 }
128
129 if (!GetAccountSid(NULL, AccountName, &pSid)) {
130 LsaClose(PolicyHandle);
131 return (RTN_NOACCOUNT);
132 }
133
134 /*
135 * Find out what groups the account belongs to
136 */
137 istatus = isc_ntsecurity_getaccountgroups(name, Accounts, maxAccounts,
138 totalAccounts);
139 if (istatus == ISC_R_NOMEMORY) {
140 LsaClose(PolicyHandle);
141 return (RTN_NOMEMORY);
142 } else if (istatus != ISC_R_SUCCESS) {
143 LsaClose(PolicyHandle);
144 return (RTN_ERROR);
145 }
146
147 Accounts[*totalAccounts] = name; /* Add the account to the list */
148 (*totalAccounts)++;
149
150 /*
151 * Loop through each Account to get the list of privileges
152 */
153 for (i = 0; i < *totalAccounts; i++) {
154 n = wnsprintf(AccountName, sizeof(AccountName), TEXT("%hS"),
155 Accounts[i]);
156 if (n < 0 || (size_t)n >= sizeof(AccountName)) {
157 continue;
158 }
159
160 /* Obtain the SID of the user/group. */
161 if (!GetAccountSid(NULL, AccountName, &pSid)) {
162 continue; /* Try the next one */
163 }
164
165 /* Get the Privileges allocated to this SID */
166 if ((Status = GetPrivilegesOnAccount(PolicyHandle, pSid,
167 PrivList, PrivCount)) == STATUS_SUCCESS)
168 {
169 iRetVal=RTN_OK;
170 if (pSid != NULL)
171 HeapFree(GetProcessHeap(), 0, pSid);
172 } else {
173 if (pSid != NULL)
174 HeapFree(GetProcessHeap(), 0, pSid);
175 continue; /* Try the next one */
176 }
177 }
178
179 /*
180 * Close the policy handle.
181 */
182 LsaClose(PolicyHandle);
183
184 (*totalAccounts)--; /* Correct for the number of groups */
185 return iRetVal;
186 }
187
188 BOOL
CreateServiceAccount(char * name,char * password)189 CreateServiceAccount(char *name, char *password) {
190 NTSTATUS retstat;
191 USER_INFO_1 ui;
192 DWORD dwLevel = 1;
193 DWORD dwError = 0;
194 NET_API_STATUS nStatus;
195
196 size_t namelen = strlen(name);
197 size_t passwdlen = strlen(password);
198 wchar_t AccountName[MAX_NAME_LENGTH];
199 wchar_t AccountPassword[MAX_NAME_LENGTH];
200
201 mbstowcs(AccountName, name, namelen + 1);
202 mbstowcs(AccountPassword, password, passwdlen + 1);
203
204 /*
205 * Set up the USER_INFO_1 structure.
206 * USER_PRIV_USER: name is required here when creating an account
207 * rather than an administrator or a guest.
208 */
209
210 ui.usri1_name = (LPWSTR) &AccountName;
211 ui.usri1_password = (LPWSTR) &AccountPassword;
212 ui.usri1_priv = USER_PRIV_USER;
213 ui.usri1_home_dir = NULL;
214 ui.usri1_comment = L"ISC BIND Service Account";
215 ui.usri1_flags = UF_PASSWD_CANT_CHANGE | UF_DONT_EXPIRE_PASSWD |
216 UF_SCRIPT;
217 ui.usri1_script_path = NULL;
218
219 /*
220 * Call the NetUserAdd function, specifying level 1.
221 */
222 nStatus = NetUserAdd(NULL, dwLevel, (LPBYTE)&ui, &dwError);
223 if (nStatus != NERR_Success) {
224 return (FALSE);
225 }
226
227 retstat = AddPrivilegeToAcccount(name, SE_SERVICE_LOGON_PRIV);
228 if (retstat != RTN_OK) {
229 return (FALSE);
230 }
231
232 return (TRUE);
233 }
234
235 NTSTATUS
AddPrivilegeToAcccount(LPTSTR name,LPWSTR PrivilegeName)236 AddPrivilegeToAcccount(LPTSTR name, LPWSTR PrivilegeName) {
237 LSA_HANDLE PolicyHandle;
238 TCHAR AccountName[256]; /* static account name buffer */
239 PSID pSid;
240 NTSTATUS Status;
241 unsigned long err;
242 int n;
243
244 /*
245 * Open the policy on the target machine.
246 */
247 Status = OpenPolicy(NULL, POLICY_ALL_ACCESS, &PolicyHandle);
248 if (Status != STATUS_SUCCESS) {
249 return (RTN_ERROR);
250 }
251
252 /*
253 * Let's see if the account exists. Return if not
254 */
255 n = wnsprintf(AccountName, sizeof(AccountName), TEXT("%hS"), name);
256 if (n < 0 || (size_t)n >= sizeof(AccountName)) {
257 LsaClose(PolicyHandle);
258 return (RTN_ERROR);
259 }
260
261 if (!GetAccountSid(NULL, AccountName, &pSid)) {
262 LsaClose(PolicyHandle);
263 return (RTN_NOACCOUNT);
264 }
265
266 err = LsaNtStatusToWinError(SetPrivilegeOnAccount(PolicyHandle,
267 pSid, PrivilegeName, TRUE));
268
269 LsaClose(PolicyHandle);
270 if (err == ERROR_SUCCESS) {
271 return (RTN_OK);
272 } else {
273 return (err);
274 }
275 }
276
277 void
InitLsaString(PLSA_UNICODE_STRING LsaString,LPWSTR String)278 InitLsaString(PLSA_UNICODE_STRING LsaString, LPWSTR String){
279 size_t StringLength;
280
281 if (String == NULL) {
282 LsaString->Buffer = NULL;
283 LsaString->Length = 0;
284 LsaString->MaximumLength = 0;
285 return;
286 }
287
288 StringLength = wcslen(String);
289 LsaString->Buffer = String;
290 LsaString->Length = (USHORT) StringLength * sizeof(WCHAR);
291 LsaString->MaximumLength = (USHORT)(StringLength+1) * sizeof(WCHAR);
292 }
293
294 NTSTATUS
OpenPolicy(LPWSTR ServerName,DWORD DesiredAccess,PLSA_HANDLE PolicyHandle)295 OpenPolicy(LPWSTR ServerName, DWORD DesiredAccess, PLSA_HANDLE PolicyHandle){
296 LSA_OBJECT_ATTRIBUTES ObjectAttributes;
297 LSA_UNICODE_STRING ServerString;
298 PLSA_UNICODE_STRING Server = NULL;
299
300 /*
301 * Always initialize the object attributes to all zeroes.
302 */
303 ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
304
305 if (ServerName != NULL) {
306 /*
307 * Make a LSA_UNICODE_STRING out of the LPWSTR passed in
308 */
309 InitLsaString(&ServerString, ServerName);
310 Server = &ServerString;
311 }
312
313 /*
314 * Attempt to open the policy.
315 */
316 return (LsaOpenPolicy(Server, &ObjectAttributes, DesiredAccess,
317 PolicyHandle));
318 }
319
320 BOOL
GetAccountSid(LPTSTR SystemName,LPTSTR AccountName,PSID * Sid)321 GetAccountSid(LPTSTR SystemName, LPTSTR AccountName, PSID *Sid) {
322 LPTSTR ReferencedDomain = NULL;
323 DWORD cbSid = 128; /* initial allocation attempt */
324 DWORD cbReferencedDomain = 16; /* initial allocation size */
325 SID_NAME_USE peUse;
326 BOOL bSuccess = FALSE; /* assume this function will fail */
327
328 __try {
329 /*
330 * initial memory allocations
331 */
332 if ((*Sid = HeapAlloc(GetProcessHeap(), 0, cbSid)) == NULL)
333 __leave;
334
335 if ((ReferencedDomain = (LPTSTR) HeapAlloc(GetProcessHeap(), 0,
336 cbReferencedDomain)) == NULL) __leave;
337
338 /*
339 * Obtain the SID of the specified account on the specified system.
340 */
341 while (!LookupAccountName(SystemName, AccountName, *Sid, &cbSid,
342 ReferencedDomain, &cbReferencedDomain,
343 &peUse))
344 {
345 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
346 /* reallocate memory */
347 if ((*Sid = HeapReAlloc(GetProcessHeap(), 0,
348 *Sid, cbSid)) == NULL) __leave;
349
350 if ((ReferencedDomain= (LPTSTR) HeapReAlloc(
351 GetProcessHeap(), 0, ReferencedDomain,
352 cbReferencedDomain)) == NULL)
353 __leave;
354 }
355 else
356 __leave;
357 }
358 bSuccess = TRUE;
359 } /* finally */
360 __finally {
361
362 /* Cleanup and indicate failure, if appropriate. */
363
364 HeapFree(GetProcessHeap(), 0, ReferencedDomain);
365
366 if (!bSuccess) {
367 if (*Sid != NULL) {
368 HeapFree(GetProcessHeap(), 0, *Sid);
369 *Sid = NULL;
370 }
371 }
372
373 }
374
375 return (bSuccess);
376 }
377
378 NTSTATUS
SetPrivilegeOnAccount(LSA_HANDLE PolicyHandle,PSID AccountSid,LPWSTR PrivilegeName,BOOL bEnable)379 SetPrivilegeOnAccount(LSA_HANDLE PolicyHandle, PSID AccountSid,
380 LPWSTR PrivilegeName, BOOL bEnable)
381 {
382 LSA_UNICODE_STRING PrivilegeString;
383
384 /* Create a LSA_UNICODE_STRING for the privilege name. */
385 InitLsaString(&PrivilegeString, PrivilegeName);
386
387 /* grant or revoke the privilege, accordingly */
388 if (bEnable)
389 return (LsaAddAccountRights(PolicyHandle, AccountSid,
390 &PrivilegeString, 1));
391 else
392 return (LsaRemoveAccountRights(PolicyHandle, AccountSid,
393 FALSE, &PrivilegeString, 1));
394 }
395
396 NTSTATUS
GetPrivilegesOnAccount(LSA_HANDLE PolicyHandle,PSID AccountSid,wchar_t ** PrivList,unsigned int * PrivCount)397 GetPrivilegesOnAccount(LSA_HANDLE PolicyHandle, PSID AccountSid,
398 wchar_t **PrivList, unsigned int *PrivCount)
399 {
400 NTSTATUS Status;
401 LSA_UNICODE_STRING *UserRights;
402 ULONG CountOfRights;
403 DWORD i, j;
404 int found;
405
406 Status = LsaEnumerateAccountRights(PolicyHandle, AccountSid,
407 &UserRights, &CountOfRights);
408 /* Only continue if there is something */
409 if (UserRights == NULL || Status != STATUS_SUCCESS)
410 return (Status);
411
412 for (i = 0; i < CountOfRights; i++) {
413 unsigned int retlen;
414 found = -1;
415 retlen = UserRights[i].Length/sizeof(wchar_t);
416 for (j = 0; j < *PrivCount; j++) {
417 found = wcsncmp(PrivList[j], UserRights[i].Buffer,
418 retlen);
419 if (found == 0)
420 break;
421 }
422 if (found != 0) {
423 PrivList[*PrivCount] =
424 (wchar_t *)malloc(UserRights[i].MaximumLength);
425 if (PrivList[*PrivCount] == NULL)
426 return (RTN_NOMEMORY);
427
428 wcsncpy(PrivList[*PrivCount], UserRights[i].Buffer,
429 retlen);
430 PrivList[*PrivCount][retlen] = L'\0';
431 (*PrivCount)++;
432 }
433
434 }
435
436 return (Status);
437 }
438
439 void
DisplayNtStatus(LPSTR szAPI,NTSTATUS Status)440 DisplayNtStatus(LPSTR szAPI, NTSTATUS Status) {
441 /* Convert the NTSTATUS to Winerror. Then call DisplayWinError(). */
442 DisplayWinError(szAPI, LsaNtStatusToWinError(Status));
443 }
444
445 void
DisplayWinError(LPSTR szAPI,DWORD WinError)446 DisplayWinError(LPSTR szAPI, DWORD WinError) {
447 LPSTR MessageBuffer;
448 DWORD dwBufferLength;
449
450 if (dwBufferLength=FormatMessageA(
451 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
452 NULL, WinError, GetUserDefaultLangID(),
453 (LPSTR) &MessageBuffer, 0, NULL)){
454 DWORD dwBytesWritten; /* unused */
455
456 /* Output message string on stderr. */
457 WriteFile(GetStdHandle(STD_ERROR_HANDLE), MessageBuffer,
458 dwBufferLength, &dwBytesWritten, NULL);
459
460 /* Free the buffer allocated by the system. */
461 LocalFree(MessageBuffer);
462 }
463 }
464