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