2 * Portions Copyright (C) 2004, 2007, 2009, 2013 Internet Systems Consortium, Inc. ("ISC")
3 * Portions Copyright (C) 2001, 2002 Internet Software Consortium.
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.
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.
18 /* Id: AccountInfo.cpp,v 1.10 2009/09/29 23:48:04 tbox Exp */
20 /* Compiled with UNICODE */
28 #include <isc/ntgroups.h>
29 #include <isc/result.h>
30 #include "AccountInfo.h"
32 #define MAX_NAME_LENGTH 256
36 LPWSTR ServerName
, /* machine to open policy on (Unicode) */
37 DWORD DesiredAccess
, /* desired access to policy */
38 PLSA_HANDLE PolicyHandle
/* resultant policy handle */
43 LPTSTR SystemName
, /* where to lookup account */
44 LPTSTR AccountName
, /* account of interest */
45 PSID
*Sid
/* resultant buffer containing SID */
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 */
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 */
65 AddPrivilegeToAcccount(
66 LPTSTR AccountName
, /* Name of the account */
67 LPWSTR PrivilegeName
/* Privilege to Add */
72 PLSA_UNICODE_STRING LsaString
, /* destination */
73 LPWSTR String
/* source (Unicode) */
78 LPSTR szAPI
, /* pointer to function name (ANSI) */
79 NTSTATUS Status
/* NTSTATUS error value */
84 LPSTR szAPI
, /* pointer to function name (ANSI) */
85 DWORD WinError
/* DWORD WinError */
88 #ifndef STATUS_SUCCESS
89 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
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
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.
107 GetAccountPrivileges(char *name
, wchar_t **PrivList
, unsigned int *PrivCount
,
108 char **Accounts
, unsigned int *totalAccounts
,
111 LSA_HANDLE PolicyHandle
;
112 TCHAR AccountName
[256]; /* static account name buffer */
116 isc_result_t istatus
;
117 int iRetVal
= RTN_ERROR
; /* assume error from main */
120 * Open the policy on the target machine.
122 if ((Status
= OpenPolicy(NULL
,
124 &PolicyHandle
)) != STATUS_SUCCESS
)
128 * Let's see if the account exists. Return if not
130 wsprintf(AccountName
, TEXT("%hS"), name
);
131 if (!GetAccountSid(NULL
, AccountName
, &pSid
))
132 return (RTN_NOACCOUNT
);
134 * Find out what groups the account belongs to
136 istatus
= isc_ntsecurity_getaccountgroups(name
, Accounts
, maxAccounts
,
138 if (istatus
== ISC_R_NOMEMORY
)
139 return (RTN_NOMEMORY
);
140 else if (istatus
!= ISC_R_SUCCESS
)
143 Accounts
[*totalAccounts
] = name
; /* Add the account to the list */
147 * Loop through each Account to get the list of privileges
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
)
160 HeapFree(GetProcessHeap(), 0, pSid
);
163 HeapFree(GetProcessHeap(), 0, pSid
);
164 continue; /* Try the next one */
168 * Close the policy handle.
170 LsaClose(PolicyHandle
);
172 (*totalAccounts
)--; /* Correct for the number of groups */
177 CreateServiceAccount(char *name
, char *password
) {
182 NET_API_STATUS nStatus
;
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
];
189 mbstowcs(AccountName
, name
, namelen
+ 1);
190 mbstowcs(AccountPassword
, password
, passwdlen
+ 1);
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.
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
|
205 ui
.usri1_script_path
= NULL
;
207 * Call the NetUserAdd function, specifying level 1.
209 nStatus
= NetUserAdd(NULL
, dwLevel
, (LPBYTE
)&ui
, &dwError
);
211 if (nStatus
!= NERR_Success
)
214 retstat
= AddPrivilegeToAcccount(name
, SE_SERVICE_LOGON_PRIV
);
219 AddPrivilegeToAcccount(LPTSTR name
, LPWSTR PrivilegeName
) {
220 LSA_HANDLE PolicyHandle
;
221 TCHAR AccountName
[256]; /* static account name buffer */
227 * Open the policy on the target machine.
229 if ((Status
= OpenPolicy(NULL
, POLICY_ALL_ACCESS
, &PolicyHandle
))
234 * Let's see if the account exists. Return if not
236 wsprintf(AccountName
, TEXT("%hS"), name
);
237 if (!GetAccountSid(NULL
, AccountName
, &pSid
))
238 return (RTN_NOACCOUNT
);
240 err
= LsaNtStatusToWinError(SetPrivilegeOnAccount(PolicyHandle
,
241 pSid
, PrivilegeName
, TRUE
));
243 LsaClose(PolicyHandle
);
244 if (err
== ERROR_SUCCESS
)
251 InitLsaString(PLSA_UNICODE_STRING LsaString
, LPWSTR String
){
254 if (String
== NULL
) {
255 LsaString
->Buffer
= NULL
;
256 LsaString
->Length
= 0;
257 LsaString
->MaximumLength
= 0;
261 StringLength
= wcslen(String
);
262 LsaString
->Buffer
= String
;
263 LsaString
->Length
= (USHORT
) StringLength
* sizeof(WCHAR
);
264 LsaString
->MaximumLength
= (USHORT
)(StringLength
+1) * sizeof(WCHAR
);
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
;
274 * Always initialize the object attributes to all zeroes.
276 ZeroMemory(&ObjectAttributes
, sizeof(ObjectAttributes
));
278 if (ServerName
!= NULL
) {
280 * Make a LSA_UNICODE_STRING out of the LPWSTR passed in
282 InitLsaString(&ServerString
, ServerName
);
283 Server
= &ServerString
;
287 * Attempt to open the policy.
289 return (LsaOpenPolicy(Server
, &ObjectAttributes
, DesiredAccess
,
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 */
299 BOOL bSuccess
= FALSE
; /* assume this function will fail */
303 * initial memory allocations
305 if ((*Sid
= HeapAlloc(GetProcessHeap(), 0, cbSid
)) == NULL
)
308 if ((ReferencedDomain
= (LPTSTR
) HeapAlloc(GetProcessHeap(), 0,
309 cbReferencedDomain
)) == NULL
) __leave
;
312 * Obtain the SID of the specified account on the specified system.
314 while (!LookupAccountName(SystemName
, AccountName
, *Sid
, &cbSid
,
315 ReferencedDomain
, &cbReferencedDomain
,
318 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
) {
319 /* reallocate memory */
320 if ((*Sid
= HeapReAlloc(GetProcessHeap(), 0,
321 *Sid
, cbSid
)) == NULL
) __leave
;
323 if ((ReferencedDomain
= (LPTSTR
) HeapReAlloc(
324 GetProcessHeap(), 0, ReferencedDomain
,
325 cbReferencedDomain
)) == NULL
)
335 /* Cleanup and indicate failure, if appropriate. */
337 HeapFree(GetProcessHeap(), 0, ReferencedDomain
);
341 HeapFree(GetProcessHeap(), 0, *Sid
);
352 SetPrivilegeOnAccount(LSA_HANDLE PolicyHandle
, PSID AccountSid
,
353 LPWSTR PrivilegeName
, BOOL bEnable
)
355 LSA_UNICODE_STRING PrivilegeString
;
357 /* Create a LSA_UNICODE_STRING for the privilege name. */
358 InitLsaString(&PrivilegeString
, PrivilegeName
);
360 /* grant or revoke the privilege, accordingly */
362 return (LsaAddAccountRights(PolicyHandle
, AccountSid
,
363 &PrivilegeString
, 1));
365 return (LsaRemoveAccountRights(PolicyHandle
, AccountSid
,
366 FALSE
, &PrivilegeString
, 1));
370 GetPrivilegesOnAccount(LSA_HANDLE PolicyHandle
, PSID AccountSid
,
371 wchar_t **PrivList
, unsigned int *PrivCount
)
374 LSA_UNICODE_STRING
*UserRights
;
376 unsigned int retlen
= 0;
380 Status
= LsaEnumerateAccountRights(PolicyHandle
, AccountSid
,
381 &UserRights
, &CountOfRights
);
382 /* Only continue if there is something */
383 if (UserRights
== NULL
|| Status
!= STATUS_SUCCESS
)
386 for (i
= 0; i
< CountOfRights
; i
++) {
388 retlen
= UserRights
[i
].Length
/sizeof(wchar_t);
389 for (j
= 0; j
< *PrivCount
; j
++) {
390 found
= wcsncmp(PrivList
[j
], UserRights
[i
].Buffer
,
396 PrivList
[*PrivCount
] =
397 (wchar_t *)malloc(UserRights
[i
].MaximumLength
);
398 if (PrivList
[*PrivCount
] == NULL
)
399 return (RTN_NOMEMORY
);
401 wcsncpy(PrivList
[*PrivCount
], UserRights
[i
].Buffer
,
403 PrivList
[*PrivCount
][retlen
] = L
'\0';
413 DisplayNtStatus(LPSTR szAPI
, NTSTATUS Status
) {
414 /* Convert the NTSTATUS to Winerror. Then call DisplayWinError(). */
415 DisplayWinError(szAPI
, LsaNtStatusToWinError(Status
));
419 DisplayWinError(LPSTR szAPI
, DWORD WinError
) {
421 DWORD dwBufferLength
;
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 */
429 /* Output message string on stderr. */
430 WriteFile(GetStdHandle(STD_ERROR_HANDLE
), MessageBuffer
,
431 dwBufferLength
, &dwBytesWritten
, NULL
);
433 /* Free the buffer allocated by the system. */
434 LocalFree(MessageBuffer
);