AccountInfo.cpp revision 1.1.1.3.6.1
1/* 2 * Portions Copyright (C) 2004, 2007, 2009 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#ifndef UNICODE 21#define UNICODE 22#endif /* UNICODE */ 23 24#include "stdafx.h" 25 26#include <windows.h> 27#include <lm.h> 28#include <ntsecapi.h> 29 30#include <isc/ntgroups.h> 31#include <isc/result.h> 32#include "AccountInfo.h" 33 34#define MAX_NAME_LENGTH 256 35 36NTSTATUS 37OpenPolicy( 38 LPWSTR ServerName, /* machine to open policy on (Unicode) */ 39 DWORD DesiredAccess, /* desired access to policy */ 40 PLSA_HANDLE PolicyHandle /* resultant policy handle */ 41 ); 42 43BOOL 44GetAccountSid( 45 LPTSTR SystemName, /* where to lookup account */ 46 LPTSTR AccountName, /* account of interest */ 47 PSID *Sid /* resultant buffer containing SID */ 48 ); 49 50NTSTATUS 51SetPrivilegeOnAccount( 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 */ 56 ); 57 58NTSTATUS 59GetPrivilegesOnAccount( 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 */ 64 ); 65 66NTSTATUS 67AddPrivilegeToAcccount( 68 LPTSTR AccountName, /* Name of the account */ 69 LPWSTR PrivilegeName /* Privilege to Add */ 70 ); 71 72void 73InitLsaString( 74 PLSA_UNICODE_STRING LsaString, /* destination */ 75 LPWSTR String /* source (Unicode) */ 76 ); 77 78void 79DisplayNtStatus( 80 LPSTR szAPI, /* pointer to function name (ANSI) */ 81 NTSTATUS Status /* NTSTATUS error value */ 82 ); 83 84void 85DisplayWinError( 86 LPSTR szAPI, /* pointer to function name (ANSI) */ 87 DWORD WinError /* DWORD WinError */ 88 ); 89 90#ifndef STATUS_SUCCESS 91#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) 92#endif 93 94/* 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 103 * group 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. 106 */ 107 108int 109GetAccountPrivileges(char *name, wchar_t **PrivList, unsigned int *PrivCount, 110 char **Accounts, unsigned int *totalAccounts, 111 int maxAccounts) 112{ 113 LSA_HANDLE PolicyHandle; 114 TCHAR AccountName[256]; /* static account name buffer */ 115 PSID pSid; 116 unsigned int i; 117 NTSTATUS Status; 118 isc_result_t istatus; 119 int iRetVal = RTN_ERROR; /* assume error from main */ 120 121 /* 122 * Open the policy on the target machine. 123 */ 124 if ((Status = OpenPolicy(NULL, 125 POLICY_LOOKUP_NAMES, 126 &PolicyHandle)) != STATUS_SUCCESS) 127 return (RTN_ERROR); 128 129 /* 130 * Let's see if the account exists. Return if not 131 */ 132 wsprintf(AccountName, TEXT("%hS"), name); 133 if (!GetAccountSid(NULL, AccountName, &pSid)) 134 return (RTN_NOACCOUNT); 135 /* 136 * Find out what groups the account belongs to 137 */ 138 istatus = isc_ntsecurity_getaccountgroups(name, Accounts, maxAccounts, 139 totalAccounts); 140 if (istatus == ISC_R_NOMEMORY) 141 return (RTN_NOMEMORY); 142 else if (istatus != ISC_R_SUCCESS) 143 return (RTN_ERROR); 144 145 Accounts[*totalAccounts] = name; /* Add the account to the list */ 146 (*totalAccounts)++; 147 148 /* 149 * Loop through each Account to get the list of privileges 150 */ 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) 159 { 160 iRetVal=RTN_OK; 161 if (pSid != NULL) 162 HeapFree(GetProcessHeap(), 0, pSid); 163 } else { 164 if (pSid != NULL) 165 HeapFree(GetProcessHeap(), 0, pSid); 166 continue; /* Try the next one */ 167 } 168 } 169 /* 170 * Close the policy handle. 171 */ 172 LsaClose(PolicyHandle); 173 174 (*totalAccounts)--; /* Correct for the number of groups */ 175 return iRetVal; 176} 177 178BOOL 179CreateServiceAccount(char *name, char *password) { 180 NTSTATUS retstat; 181 USER_INFO_1 ui; 182 DWORD dwLevel = 1; 183 DWORD dwError = 0; 184 NET_API_STATUS nStatus; 185 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]; 190 191 mbstowcs(AccountName, name, namelen + 1); 192 mbstowcs(AccountPassword, password, passwdlen + 1); 193 194 /* 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. 198 */ 199 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 | 206 UF_SCRIPT; 207 ui.usri1_script_path = NULL; 208 /* 209 * Call the NetUserAdd function, specifying level 1. 210 */ 211 nStatus = NetUserAdd(NULL, dwLevel, (LPBYTE)&ui, &dwError); 212 213 if (nStatus != NERR_Success) 214 return (FALSE); 215 216 retstat = AddPrivilegeToAcccount(name, SE_SERVICE_LOGON_PRIV); 217 return (TRUE); 218} 219 220NTSTATUS 221AddPrivilegeToAcccount(LPTSTR name, LPWSTR PrivilegeName) { 222 LSA_HANDLE PolicyHandle; 223 TCHAR AccountName[256]; /* static account name buffer */ 224 PSID pSid; 225 NTSTATUS Status; 226 unsigned long err; 227 228 /* 229 * Open the policy on the target machine. 230 */ 231 if ((Status = OpenPolicy(NULL, POLICY_ALL_ACCESS, &PolicyHandle)) 232 != STATUS_SUCCESS) 233 return (RTN_ERROR); 234 235 /* 236 * Let's see if the account exists. Return if not 237 */ 238 wsprintf(AccountName, TEXT("%hS"), name); 239 if (!GetAccountSid(NULL, AccountName, &pSid)) 240 return (RTN_NOACCOUNT); 241 242 err = LsaNtStatusToWinError(SetPrivilegeOnAccount(PolicyHandle, 243 pSid, PrivilegeName, TRUE)); 244 245 LsaClose(PolicyHandle); 246 if (err == ERROR_SUCCESS) 247 return (RTN_OK); 248 else 249 return (err); 250} 251 252void 253InitLsaString(PLSA_UNICODE_STRING LsaString, LPWSTR String){ 254 size_t StringLength; 255 256 if (String == NULL) { 257 LsaString->Buffer = NULL; 258 LsaString->Length = 0; 259 LsaString->MaximumLength = 0; 260 return; 261 } 262 263 StringLength = wcslen(String); 264 LsaString->Buffer = String; 265 LsaString->Length = (USHORT) StringLength * sizeof(WCHAR); 266 LsaString->MaximumLength = (USHORT)(StringLength+1) * sizeof(WCHAR); 267} 268 269NTSTATUS 270OpenPolicy(LPWSTR ServerName, DWORD DesiredAccess, PLSA_HANDLE PolicyHandle){ 271 LSA_OBJECT_ATTRIBUTES ObjectAttributes; 272 LSA_UNICODE_STRING ServerString; 273 PLSA_UNICODE_STRING Server = NULL; 274 275 /* 276 * Always initialize the object attributes to all zeroes. 277 */ 278 ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes)); 279 280 if (ServerName != NULL) { 281 /* 282 * Make a LSA_UNICODE_STRING out of the LPWSTR passed in 283 */ 284 InitLsaString(&ServerString, ServerName); 285 Server = &ServerString; 286 } 287 288 /* 289 * Attempt to open the policy. 290 */ 291 return (LsaOpenPolicy(Server, &ObjectAttributes, DesiredAccess, 292 PolicyHandle)); 293} 294 295BOOL 296GetAccountSid(LPTSTR SystemName, LPTSTR AccountName, PSID *Sid) { 297 LPTSTR ReferencedDomain = NULL; 298 DWORD cbSid = 128; /* initial allocation attempt */ 299 DWORD cbReferencedDomain = 16; /* initial allocation size */ 300 SID_NAME_USE peUse; 301 BOOL bSuccess = FALSE; /* assume this function will fail */ 302 303 __try { 304 /* 305 * initial memory allocations 306 */ 307 if ((*Sid = HeapAlloc(GetProcessHeap(), 0, cbSid)) == NULL) 308 __leave; 309 310 if ((ReferencedDomain = (LPTSTR) HeapAlloc(GetProcessHeap(), 0, 311 cbReferencedDomain)) == NULL) __leave; 312 313 /* 314 * Obtain the SID of the specified account on the specified system. 315 */ 316 while (!LookupAccountName(SystemName, AccountName, *Sid, &cbSid, 317 ReferencedDomain, &cbReferencedDomain, 318 &peUse)) 319 { 320 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { 321 /* reallocate memory */ 322 if ((*Sid = HeapReAlloc(GetProcessHeap(), 0, 323 *Sid, cbSid)) == NULL) __leave; 324 325 if ((ReferencedDomain= (LPTSTR) HeapReAlloc( 326 GetProcessHeap(), 0, ReferencedDomain, 327 cbReferencedDomain)) == NULL) 328 __leave; 329 } 330 else 331 __leave; 332 } 333 bSuccess = TRUE; 334 } /* finally */ 335 __finally { 336 337 /* Cleanup and indicate failure, if appropriate. */ 338 339 HeapFree(GetProcessHeap(), 0, ReferencedDomain); 340 341 if (!bSuccess) { 342 if (*Sid != NULL) { 343 HeapFree(GetProcessHeap(), 0, *Sid); 344 *Sid = NULL; 345 } 346 } 347 348 } 349 350 return (bSuccess); 351} 352 353NTSTATUS 354SetPrivilegeOnAccount(LSA_HANDLE PolicyHandle, PSID AccountSid, 355 LPWSTR PrivilegeName, BOOL bEnable) 356{ 357 LSA_UNICODE_STRING PrivilegeString; 358 359 /* Create a LSA_UNICODE_STRING for the privilege name. */ 360 InitLsaString(&PrivilegeString, PrivilegeName); 361 362 /* grant or revoke the privilege, accordingly */ 363 if (bEnable) 364 return (LsaAddAccountRights(PolicyHandle, AccountSid, 365 &PrivilegeString, 1)); 366 else 367 return (LsaRemoveAccountRights(PolicyHandle, AccountSid, 368 FALSE, &PrivilegeString, 1)); 369} 370 371NTSTATUS 372GetPrivilegesOnAccount(LSA_HANDLE PolicyHandle, PSID AccountSid, 373 wchar_t **PrivList, unsigned int *PrivCount) 374{ 375 NTSTATUS Status; 376 LSA_UNICODE_STRING *UserRights; 377 ULONG CountOfRights; 378 unsigned int retlen = 0; 379 DWORD i, j; 380 int found; 381 382 Status = LsaEnumerateAccountRights(PolicyHandle, AccountSid, 383 &UserRights, &CountOfRights); 384 /* Only continue if there is something */ 385 if (UserRights == NULL || Status != STATUS_SUCCESS) 386 return (Status); 387 388 for (i = 0; i < CountOfRights; i++) { 389 found = -1; 390 retlen = UserRights[i].Length/sizeof(wchar_t); 391 for (j = 0; j < *PrivCount; j++) { 392 found = wcsncmp(PrivList[j], UserRights[i].Buffer, 393 retlen); 394 if (found == 0) 395 break; 396 } 397 if (found != 0) { 398 PrivList[*PrivCount] = 399 (wchar_t *)malloc(UserRights[i].MaximumLength); 400 if (PrivList[*PrivCount] == NULL) 401 return (RTN_NOMEMORY); 402 403 wcsncpy(PrivList[*PrivCount], UserRights[i].Buffer, 404 retlen); 405 PrivList[*PrivCount][retlen] = L'\0'; 406 (*PrivCount)++; 407 } 408 409 } 410 411 return (Status); 412} 413 414void 415DisplayNtStatus(LPSTR szAPI, NTSTATUS Status) { 416 /* Convert the NTSTATUS to Winerror. Then call DisplayWinError(). */ 417 DisplayWinError(szAPI, LsaNtStatusToWinError(Status)); 418} 419 420void 421DisplayWinError(LPSTR szAPI, DWORD WinError) { 422 LPSTR MessageBuffer; 423 DWORD dwBufferLength; 424 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 */ 430 431 /* Output message string on stderr. */ 432 WriteFile(GetStdHandle(STD_ERROR_HANDLE), MessageBuffer, 433 dwBufferLength, &dwBytesWritten, NULL); 434 435 /* Free the buffer allocated by the system. */ 436 LocalFree(MessageBuffer); 437 } 438} 439