2 * Portions Copyright (C) 2004, 2007, 2009 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 */
30 #include <isc/ntgroups.h>
31 #include <isc/result.h>
32 #include "AccountInfo.h"
34 #define MAX_NAME_LENGTH 256
38 LPWSTR ServerName
, /* machine to open policy on (Unicode) */
39 DWORD DesiredAccess
, /* desired access to policy */
40 PLSA_HANDLE PolicyHandle
/* resultant policy handle */
45 LPTSTR SystemName
, /* where to lookup account */
46 LPTSTR AccountName
, /* account of interest */
47 PSID
*Sid
/* resultant buffer containing SID */
51 SetPrivilegeOnAccount(
52 LSA_HANDLE PolicyHandle
, /* open policy handle */
53 PSID AccountSid
, /* SID to grant privilege to */
54 LPWSTR PrivilegeName
, /* privilege to grant (Unicode) */
55 BOOL bEnable
/* enable or disable */
59 GetPrivilegesOnAccount(
60 LSA_HANDLE PolicyHandle
, /* open policy handle */
61 PSID AccountSid
, /* SID to grant privilege to */
62 wchar_t **PrivList
, /* Ptr to List of Privileges found */
63 unsigned int *PrivCount
/* total number of Privileges in list */
67 AddPrivilegeToAcccount(
68 LPTSTR AccountName
, /* Name of the account */
69 LPWSTR PrivilegeName
/* Privilege to Add */
74 PLSA_UNICODE_STRING LsaString
, /* destination */
75 LPWSTR String
/* source (Unicode) */
80 LPSTR szAPI
, /* pointer to function name (ANSI) */
81 NTSTATUS Status
/* NTSTATUS error value */
86 LPSTR szAPI
, /* pointer to function name (ANSI) */
87 DWORD WinError
/* DWORD WinError */
90 #ifndef STATUS_SUCCESS
91 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
95 * Note that this code only retrieves the list of privileges of the
96 * requested account or group. However, all accounts belong to the
97 * Everyone group even though that group is not returned by the
98 * calls to get the groups to which that account belongs.
99 * The Everyone group has two privileges associated with it:
100 * SeChangeNotifyPrivilege and SeNetworkLogonRight
101 * It is not advisable to disable or remove these privileges
102 * from the group nor can the account be removed from the Everyone
104 * The None group has no privileges associated with it and is the group
105 * to which an account belongs if it is associated with no group.
109 GetAccountPrivileges(char *name
, wchar_t **PrivList
, unsigned int *PrivCount
,
110 char **Accounts
, unsigned int *totalAccounts
,
113 LSA_HANDLE PolicyHandle
;
114 TCHAR AccountName
[256]; /* static account name buffer */
118 isc_result_t istatus
;
119 int iRetVal
= RTN_ERROR
; /* assume error from main */
122 * Open the policy on the target machine.
124 if ((Status
= OpenPolicy(NULL
,
126 &PolicyHandle
)) != STATUS_SUCCESS
)
130 * Let's see if the account exists. Return if not
132 wsprintf(AccountName
, TEXT("%hS"), name
);
133 if (!GetAccountSid(NULL
, AccountName
, &pSid
))
134 return (RTN_NOACCOUNT
);
136 * Find out what groups the account belongs to
138 istatus
= isc_ntsecurity_getaccountgroups(name
, Accounts
, maxAccounts
,
140 if (istatus
== ISC_R_NOMEMORY
)
141 return (RTN_NOMEMORY
);
142 else if (istatus
!= ISC_R_SUCCESS
)
145 Accounts
[*totalAccounts
] = name
; /* Add the account to the list */
149 * Loop through each Account to get the list of privileges
151 for (i
= 0; i
< *totalAccounts
; i
++) {
152 wsprintf(AccountName
, TEXT("%hS"), Accounts
[i
]);
153 /* Obtain the SID of the user/group. */
154 if (!GetAccountSid(NULL
, AccountName
, &pSid
))
155 continue; /* Try the next one */
156 /* Get the Privileges allocated to this SID */
157 if ((Status
= GetPrivilegesOnAccount(PolicyHandle
, pSid
,
158 PrivList
, PrivCount
)) == STATUS_SUCCESS
)
162 HeapFree(GetProcessHeap(), 0, pSid
);
165 HeapFree(GetProcessHeap(), 0, pSid
);
166 continue; /* Try the next one */
170 * Close the policy handle.
172 LsaClose(PolicyHandle
);
174 (*totalAccounts
)--; /* Correct for the number of groups */
179 CreateServiceAccount(char *name
, char *password
) {
184 NET_API_STATUS nStatus
;
186 size_t namelen
= strlen(name
);
187 size_t passwdlen
= strlen(password
);
188 wchar_t AccountName
[MAX_NAME_LENGTH
];
189 wchar_t AccountPassword
[MAX_NAME_LENGTH
];
191 mbstowcs(AccountName
, name
, namelen
+ 1);
192 mbstowcs(AccountPassword
, password
, passwdlen
+ 1);
195 * Set up the USER_INFO_1 structure.
196 * USER_PRIV_USER: name is required here when creating an account
197 * rather than an administrator or a guest.
200 ui
.usri1_name
= (LPWSTR
) &AccountName
;
201 ui
.usri1_password
= (LPWSTR
) &AccountPassword
;
202 ui
.usri1_priv
= USER_PRIV_USER
;
203 ui
.usri1_home_dir
= NULL
;
204 ui
.usri1_comment
= L
"ISC BIND Service Account";
205 ui
.usri1_flags
= UF_PASSWD_CANT_CHANGE
| UF_DONT_EXPIRE_PASSWD
|
207 ui
.usri1_script_path
= NULL
;
209 * Call the NetUserAdd function, specifying level 1.
211 nStatus
= NetUserAdd(NULL
, dwLevel
, (LPBYTE
)&ui
, &dwError
);
213 if (nStatus
!= NERR_Success
)
216 retstat
= AddPrivilegeToAcccount(name
, SE_SERVICE_LOGON_PRIV
);
221 AddPrivilegeToAcccount(LPTSTR name
, LPWSTR PrivilegeName
) {
222 LSA_HANDLE PolicyHandle
;
223 TCHAR AccountName
[256]; /* static account name buffer */
229 * Open the policy on the target machine.
231 if ((Status
= OpenPolicy(NULL
, POLICY_ALL_ACCESS
, &PolicyHandle
))
236 * Let's see if the account exists. Return if not
238 wsprintf(AccountName
, TEXT("%hS"), name
);
239 if (!GetAccountSid(NULL
, AccountName
, &pSid
))
240 return (RTN_NOACCOUNT
);
242 err
= LsaNtStatusToWinError(SetPrivilegeOnAccount(PolicyHandle
,
243 pSid
, PrivilegeName
, TRUE
));
245 LsaClose(PolicyHandle
);
246 if (err
== ERROR_SUCCESS
)
253 InitLsaString(PLSA_UNICODE_STRING LsaString
, LPWSTR String
){
256 if (String
== NULL
) {
257 LsaString
->Buffer
= NULL
;
258 LsaString
->Length
= 0;
259 LsaString
->MaximumLength
= 0;
263 StringLength
= wcslen(String
);
264 LsaString
->Buffer
= String
;
265 LsaString
->Length
= (USHORT
) StringLength
* sizeof(WCHAR
);
266 LsaString
->MaximumLength
= (USHORT
)(StringLength
+1) * sizeof(WCHAR
);
270 OpenPolicy(LPWSTR ServerName
, DWORD DesiredAccess
, PLSA_HANDLE PolicyHandle
){
271 LSA_OBJECT_ATTRIBUTES ObjectAttributes
;
272 LSA_UNICODE_STRING ServerString
;
273 PLSA_UNICODE_STRING Server
= NULL
;
276 * Always initialize the object attributes to all zeroes.
278 ZeroMemory(&ObjectAttributes
, sizeof(ObjectAttributes
));
280 if (ServerName
!= NULL
) {
282 * Make a LSA_UNICODE_STRING out of the LPWSTR passed in
284 InitLsaString(&ServerString
, ServerName
);
285 Server
= &ServerString
;
289 * Attempt to open the policy.
291 return (LsaOpenPolicy(Server
, &ObjectAttributes
, DesiredAccess
,
296 GetAccountSid(LPTSTR SystemName
, LPTSTR AccountName
, PSID
*Sid
) {
297 LPTSTR ReferencedDomain
= NULL
;
298 DWORD cbSid
= 128; /* initial allocation attempt */
299 DWORD cbReferencedDomain
= 16; /* initial allocation size */
301 BOOL bSuccess
= FALSE
; /* assume this function will fail */
305 * initial memory allocations
307 if ((*Sid
= HeapAlloc(GetProcessHeap(), 0, cbSid
)) == NULL
)
310 if ((ReferencedDomain
= (LPTSTR
) HeapAlloc(GetProcessHeap(), 0,
311 cbReferencedDomain
)) == NULL
) __leave
;
314 * Obtain the SID of the specified account on the specified system.
316 while (!LookupAccountName(SystemName
, AccountName
, *Sid
, &cbSid
,
317 ReferencedDomain
, &cbReferencedDomain
,
320 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
) {
321 /* reallocate memory */
322 if ((*Sid
= HeapReAlloc(GetProcessHeap(), 0,
323 *Sid
, cbSid
)) == NULL
) __leave
;
325 if ((ReferencedDomain
= (LPTSTR
) HeapReAlloc(
326 GetProcessHeap(), 0, ReferencedDomain
,
327 cbReferencedDomain
)) == NULL
)
337 /* Cleanup and indicate failure, if appropriate. */
339 HeapFree(GetProcessHeap(), 0, ReferencedDomain
);
343 HeapFree(GetProcessHeap(), 0, *Sid
);
354 SetPrivilegeOnAccount(LSA_HANDLE PolicyHandle
, PSID AccountSid
,
355 LPWSTR PrivilegeName
, BOOL bEnable
)
357 LSA_UNICODE_STRING PrivilegeString
;
359 /* Create a LSA_UNICODE_STRING for the privilege name. */
360 InitLsaString(&PrivilegeString
, PrivilegeName
);
362 /* grant or revoke the privilege, accordingly */
364 return (LsaAddAccountRights(PolicyHandle
, AccountSid
,
365 &PrivilegeString
, 1));
367 return (LsaRemoveAccountRights(PolicyHandle
, AccountSid
,
368 FALSE
, &PrivilegeString
, 1));
372 GetPrivilegesOnAccount(LSA_HANDLE PolicyHandle
, PSID AccountSid
,
373 wchar_t **PrivList
, unsigned int *PrivCount
)
376 LSA_UNICODE_STRING
*UserRights
;
378 unsigned int retlen
= 0;
382 Status
= LsaEnumerateAccountRights(PolicyHandle
, AccountSid
,
383 &UserRights
, &CountOfRights
);
384 /* Only continue if there is something */
385 if (UserRights
== NULL
|| Status
!= STATUS_SUCCESS
)
388 for (i
= 0; i
< CountOfRights
; i
++) {
390 retlen
= UserRights
[i
].Length
/sizeof(wchar_t);
391 for (j
= 0; j
< *PrivCount
; j
++) {
392 found
= wcsncmp(PrivList
[j
], UserRights
[i
].Buffer
,
398 PrivList
[*PrivCount
] =
399 (wchar_t *)malloc(UserRights
[i
].MaximumLength
);
400 if (PrivList
[*PrivCount
] == NULL
)
401 return (RTN_NOMEMORY
);
403 wcsncpy(PrivList
[*PrivCount
], UserRights
[i
].Buffer
,
405 PrivList
[*PrivCount
][retlen
] = L
'\0';
415 DisplayNtStatus(LPSTR szAPI
, NTSTATUS Status
) {
416 /* Convert the NTSTATUS to Winerror. Then call DisplayWinError(). */
417 DisplayWinError(szAPI
, LsaNtStatusToWinError(Status
));
421 DisplayWinError(LPSTR szAPI
, DWORD WinError
) {
423 DWORD dwBufferLength
;
425 if (dwBufferLength
=FormatMessageA(
426 FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
,
427 NULL
, WinError
, GetUserDefaultLangID(),
428 (LPSTR
) &MessageBuffer
, 0, NULL
)){
429 DWORD dwBytesWritten
; /* unused */
431 /* Output message string on stderr. */
432 WriteFile(GetStdHandle(STD_ERROR_HANDLE
), MessageBuffer
,
433 dwBufferLength
, &dwBytesWritten
, NULL
);
435 /* Free the buffer allocated by the system. */
436 LocalFree(MessageBuffer
);