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