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