mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-22 01:59:26 +00:00
459 lines
12 KiB
C++
459 lines
12 KiB
C++
/*
|
|
* Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
* information regarding copyright ownership.
|
|
*/
|
|
|
|
/* $Id: AccountInfo.cpp,v 1.10 2009/09/29 23:48:04 tbox Exp $ */
|
|
|
|
/* Compiled with UNICODE */
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include <windows.h>
|
|
#include <lm.h>
|
|
#include <ntsecapi.h>
|
|
|
|
#include <isc/ntgroups.h>
|
|
#include <isc/result.h>
|
|
#include "AccountInfo.h"
|
|
|
|
#define MAX_NAME_LENGTH 256
|
|
|
|
NTSTATUS
|
|
OpenPolicy(
|
|
LPWSTR ServerName, /* machine to open policy on (Unicode) */
|
|
DWORD DesiredAccess, /* desired access to policy */
|
|
PLSA_HANDLE PolicyHandle /* resultant policy handle */
|
|
);
|
|
|
|
BOOL
|
|
GetAccountSid(
|
|
LPTSTR SystemName, /* where to lookup account */
|
|
LPTSTR AccountName, /* account of interest */
|
|
PSID *Sid /* resultant buffer containing SID */
|
|
);
|
|
|
|
NTSTATUS
|
|
SetPrivilegeOnAccount(
|
|
LSA_HANDLE PolicyHandle, /* open policy handle */
|
|
PSID AccountSid, /* SID to grant privilege to */
|
|
LPWSTR PrivilegeName, /* privilege to grant (Unicode) */
|
|
BOOL bEnable /* enable or disable */
|
|
);
|
|
|
|
NTSTATUS
|
|
GetPrivilegesOnAccount(
|
|
LSA_HANDLE PolicyHandle, /* open policy handle */
|
|
PSID AccountSid, /* SID to grant privilege to */
|
|
wchar_t **PrivList, /* Ptr to List of Privileges found */
|
|
unsigned int *PrivCount /* total number of Privileges in list */
|
|
);
|
|
|
|
NTSTATUS
|
|
AddPrivilegeToAcccount(
|
|
LPTSTR AccountName, /* Name of the account */
|
|
LPWSTR PrivilegeName /* Privilege to Add */
|
|
);
|
|
|
|
void
|
|
InitLsaString(
|
|
PLSA_UNICODE_STRING LsaString, /* destination */
|
|
LPWSTR String /* source (Unicode) */
|
|
);
|
|
|
|
void
|
|
DisplayNtStatus(
|
|
LPSTR szAPI, /* pointer to function name (ANSI) */
|
|
NTSTATUS Status /* NTSTATUS error value */
|
|
);
|
|
|
|
void
|
|
DisplayWinError(
|
|
LPSTR szAPI, /* pointer to function name (ANSI) */
|
|
DWORD WinError /* DWORD WinError */
|
|
);
|
|
|
|
#ifndef STATUS_SUCCESS
|
|
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
|
|
#endif
|
|
|
|
/*
|
|
* Note that this code only retrieves the list of privileges of the
|
|
* requested account or group. However, all accounts belong to the
|
|
* Everyone group even though that group is not returned by the
|
|
* calls to get the groups to which that account belongs.
|
|
* The Everyone group has two privileges associated with it:
|
|
* SeChangeNotifyPrivilege and SeNetworkLogonRight
|
|
* It is not advisable to disable or remove these privileges
|
|
* from the group nor can the account be removed from the Everyone
|
|
* group
|
|
* The None group has no privileges associated with it and is the group
|
|
* to which an account belongs if it is associated with no group.
|
|
*/
|
|
|
|
int
|
|
GetAccountPrivileges(char *name, wchar_t **PrivList, unsigned int *PrivCount,
|
|
char **Accounts, unsigned int *totalAccounts,
|
|
int maxAccounts)
|
|
{
|
|
LSA_HANDLE PolicyHandle;
|
|
TCHAR AccountName[256]; /* static account name buffer */
|
|
PSID pSid;
|
|
unsigned int i;
|
|
NTSTATUS Status;
|
|
isc_result_t istatus;
|
|
int iRetVal = RTN_ERROR; /* assume error from main */
|
|
int n;
|
|
|
|
/*
|
|
* Open the policy on the target machine.
|
|
*/
|
|
if ((Status = OpenPolicy(NULL,
|
|
POLICY_LOOKUP_NAMES,
|
|
&PolicyHandle)) != STATUS_SUCCESS)
|
|
return (RTN_ERROR);
|
|
|
|
/*
|
|
* Let's see if the account exists. Return if not
|
|
*/
|
|
n = wnsprintf(AccountName, sizeof(AccountName), TEXT("%hS"), name);
|
|
if (n < 0 || (size_t)n >= sizeof(AccountName)) {
|
|
LsaClose(PolicyHandle);
|
|
return (RTN_ERROR);
|
|
}
|
|
|
|
if (!GetAccountSid(NULL, AccountName, &pSid)) {
|
|
LsaClose(PolicyHandle);
|
|
return (RTN_NOACCOUNT);
|
|
}
|
|
|
|
/*
|
|
* Find out what groups the account belongs to
|
|
*/
|
|
istatus = isc_ntsecurity_getaccountgroups(name, Accounts, maxAccounts,
|
|
totalAccounts);
|
|
if (istatus == ISC_R_NOMEMORY) {
|
|
LsaClose(PolicyHandle);
|
|
return (RTN_NOMEMORY);
|
|
} else if (istatus != ISC_R_SUCCESS) {
|
|
LsaClose(PolicyHandle);
|
|
return (RTN_ERROR);
|
|
}
|
|
|
|
Accounts[*totalAccounts] = name; /* Add the account to the list */
|
|
(*totalAccounts)++;
|
|
|
|
/*
|
|
* Loop through each Account to get the list of privileges
|
|
*/
|
|
for (i = 0; i < *totalAccounts; i++) {
|
|
n = wnsprintf(AccountName, sizeof(AccountName), TEXT("%hS"),
|
|
Accounts[i]);
|
|
if (n < 0 || (size_t)n >= sizeof(AccountName)) {
|
|
continue;
|
|
}
|
|
|
|
/* Obtain the SID of the user/group. */
|
|
if (!GetAccountSid(NULL, AccountName, &pSid)) {
|
|
continue; /* Try the next one */
|
|
}
|
|
|
|
/* Get the Privileges allocated to this SID */
|
|
if ((Status = GetPrivilegesOnAccount(PolicyHandle, pSid,
|
|
PrivList, PrivCount)) == STATUS_SUCCESS)
|
|
{
|
|
iRetVal=RTN_OK;
|
|
if (pSid != NULL)
|
|
HeapFree(GetProcessHeap(), 0, pSid);
|
|
} else {
|
|
if (pSid != NULL)
|
|
HeapFree(GetProcessHeap(), 0, pSid);
|
|
continue; /* Try the next one */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Close the policy handle.
|
|
*/
|
|
LsaClose(PolicyHandle);
|
|
|
|
(*totalAccounts)--; /* Correct for the number of groups */
|
|
return iRetVal;
|
|
}
|
|
|
|
BOOL
|
|
CreateServiceAccount(char *name, char *password) {
|
|
NTSTATUS retstat;
|
|
USER_INFO_1 ui;
|
|
DWORD dwLevel = 1;
|
|
DWORD dwError = 0;
|
|
NET_API_STATUS nStatus;
|
|
|
|
size_t namelen = strlen(name);
|
|
size_t passwdlen = strlen(password);
|
|
wchar_t AccountName[MAX_NAME_LENGTH];
|
|
wchar_t AccountPassword[MAX_NAME_LENGTH];
|
|
|
|
mbstowcs(AccountName, name, namelen + 1);
|
|
mbstowcs(AccountPassword, password, passwdlen + 1);
|
|
|
|
/*
|
|
* Set up the USER_INFO_1 structure.
|
|
* USER_PRIV_USER: name is required here when creating an account
|
|
* rather than an administrator or a guest.
|
|
*/
|
|
|
|
ui.usri1_name = (LPWSTR) &AccountName;
|
|
ui.usri1_password = (LPWSTR) &AccountPassword;
|
|
ui.usri1_priv = USER_PRIV_USER;
|
|
ui.usri1_home_dir = NULL;
|
|
ui.usri1_comment = L"ISC BIND Service Account";
|
|
ui.usri1_flags = UF_PASSWD_CANT_CHANGE | UF_DONT_EXPIRE_PASSWD |
|
|
UF_SCRIPT;
|
|
ui.usri1_script_path = NULL;
|
|
/*
|
|
* Call the NetUserAdd function, specifying level 1.
|
|
*/
|
|
nStatus = NetUserAdd(NULL, dwLevel, (LPBYTE)&ui, &dwError);
|
|
|
|
if (nStatus != NERR_Success)
|
|
return (FALSE);
|
|
|
|
retstat = AddPrivilegeToAcccount(name, SE_SERVICE_LOGON_PRIV);
|
|
return (TRUE);
|
|
}
|
|
|
|
NTSTATUS
|
|
AddPrivilegeToAcccount(LPTSTR name, LPWSTR PrivilegeName) {
|
|
LSA_HANDLE PolicyHandle;
|
|
TCHAR AccountName[256]; /* static account name buffer */
|
|
PSID pSid;
|
|
NTSTATUS Status;
|
|
unsigned long err;
|
|
int n;
|
|
|
|
/*
|
|
* Open the policy on the target machine.
|
|
*/
|
|
if ((Status = OpenPolicy(NULL, POLICY_ALL_ACCESS, &PolicyHandle))
|
|
!= STATUS_SUCCESS)
|
|
return (RTN_ERROR);
|
|
|
|
/*
|
|
* Let's see if the account exists. Return if not
|
|
*/
|
|
n = wnsprintf(AccountName, sizeof(AccountName), TEXT("%hS"), name);
|
|
if (n < 0 || (size_t)n >= sizeof(AccountName)) {
|
|
LsaClose(PolicyHandle);
|
|
return (RTN_ERROR);
|
|
}
|
|
|
|
if (!GetAccountSid(NULL, AccountName, &pSid)) {
|
|
LsaClose(PolicyHandle);
|
|
return (RTN_NOACCOUNT);
|
|
}
|
|
|
|
err = LsaNtStatusToWinError(SetPrivilegeOnAccount(PolicyHandle,
|
|
pSid, PrivilegeName, TRUE));
|
|
|
|
LsaClose(PolicyHandle);
|
|
if (err == ERROR_SUCCESS)
|
|
return (RTN_OK);
|
|
else
|
|
return (err);
|
|
}
|
|
|
|
void
|
|
InitLsaString(PLSA_UNICODE_STRING LsaString, LPWSTR String){
|
|
size_t StringLength;
|
|
|
|
if (String == NULL) {
|
|
LsaString->Buffer = NULL;
|
|
LsaString->Length = 0;
|
|
LsaString->MaximumLength = 0;
|
|
return;
|
|
}
|
|
|
|
StringLength = wcslen(String);
|
|
LsaString->Buffer = String;
|
|
LsaString->Length = (USHORT) StringLength * sizeof(WCHAR);
|
|
LsaString->MaximumLength = (USHORT)(StringLength+1) * sizeof(WCHAR);
|
|
}
|
|
|
|
NTSTATUS
|
|
OpenPolicy(LPWSTR ServerName, DWORD DesiredAccess, PLSA_HANDLE PolicyHandle){
|
|
LSA_OBJECT_ATTRIBUTES ObjectAttributes;
|
|
LSA_UNICODE_STRING ServerString;
|
|
PLSA_UNICODE_STRING Server = NULL;
|
|
|
|
/*
|
|
* Always initialize the object attributes to all zeroes.
|
|
*/
|
|
ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
|
|
|
|
if (ServerName != NULL) {
|
|
/*
|
|
* Make a LSA_UNICODE_STRING out of the LPWSTR passed in
|
|
*/
|
|
InitLsaString(&ServerString, ServerName);
|
|
Server = &ServerString;
|
|
}
|
|
|
|
/*
|
|
* Attempt to open the policy.
|
|
*/
|
|
return (LsaOpenPolicy(Server, &ObjectAttributes, DesiredAccess,
|
|
PolicyHandle));
|
|
}
|
|
|
|
BOOL
|
|
GetAccountSid(LPTSTR SystemName, LPTSTR AccountName, PSID *Sid) {
|
|
LPTSTR ReferencedDomain = NULL;
|
|
DWORD cbSid = 128; /* initial allocation attempt */
|
|
DWORD cbReferencedDomain = 16; /* initial allocation size */
|
|
SID_NAME_USE peUse;
|
|
BOOL bSuccess = FALSE; /* assume this function will fail */
|
|
|
|
__try {
|
|
/*
|
|
* initial memory allocations
|
|
*/
|
|
if ((*Sid = HeapAlloc(GetProcessHeap(), 0, cbSid)) == NULL)
|
|
__leave;
|
|
|
|
if ((ReferencedDomain = (LPTSTR) HeapAlloc(GetProcessHeap(), 0,
|
|
cbReferencedDomain)) == NULL) __leave;
|
|
|
|
/*
|
|
* Obtain the SID of the specified account on the specified system.
|
|
*/
|
|
while (!LookupAccountName(SystemName, AccountName, *Sid, &cbSid,
|
|
ReferencedDomain, &cbReferencedDomain,
|
|
&peUse))
|
|
{
|
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
|
/* reallocate memory */
|
|
if ((*Sid = HeapReAlloc(GetProcessHeap(), 0,
|
|
*Sid, cbSid)) == NULL) __leave;
|
|
|
|
if ((ReferencedDomain= (LPTSTR) HeapReAlloc(
|
|
GetProcessHeap(), 0, ReferencedDomain,
|
|
cbReferencedDomain)) == NULL)
|
|
__leave;
|
|
}
|
|
else
|
|
__leave;
|
|
}
|
|
bSuccess = TRUE;
|
|
} /* finally */
|
|
__finally {
|
|
|
|
/* Cleanup and indicate failure, if appropriate. */
|
|
|
|
HeapFree(GetProcessHeap(), 0, ReferencedDomain);
|
|
|
|
if (!bSuccess) {
|
|
if (*Sid != NULL) {
|
|
HeapFree(GetProcessHeap(), 0, *Sid);
|
|
*Sid = NULL;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return (bSuccess);
|
|
}
|
|
|
|
NTSTATUS
|
|
SetPrivilegeOnAccount(LSA_HANDLE PolicyHandle, PSID AccountSid,
|
|
LPWSTR PrivilegeName, BOOL bEnable)
|
|
{
|
|
LSA_UNICODE_STRING PrivilegeString;
|
|
|
|
/* Create a LSA_UNICODE_STRING for the privilege name. */
|
|
InitLsaString(&PrivilegeString, PrivilegeName);
|
|
|
|
/* grant or revoke the privilege, accordingly */
|
|
if (bEnable)
|
|
return (LsaAddAccountRights(PolicyHandle, AccountSid,
|
|
&PrivilegeString, 1));
|
|
else
|
|
return (LsaRemoveAccountRights(PolicyHandle, AccountSid,
|
|
FALSE, &PrivilegeString, 1));
|
|
}
|
|
|
|
NTSTATUS
|
|
GetPrivilegesOnAccount(LSA_HANDLE PolicyHandle, PSID AccountSid,
|
|
wchar_t **PrivList, unsigned int *PrivCount)
|
|
{
|
|
NTSTATUS Status;
|
|
LSA_UNICODE_STRING *UserRights;
|
|
ULONG CountOfRights;
|
|
unsigned int retlen = 0;
|
|
DWORD i, j;
|
|
int found;
|
|
|
|
Status = LsaEnumerateAccountRights(PolicyHandle, AccountSid,
|
|
&UserRights, &CountOfRights);
|
|
/* Only continue if there is something */
|
|
if (UserRights == NULL || Status != STATUS_SUCCESS)
|
|
return (Status);
|
|
|
|
for (i = 0; i < CountOfRights; i++) {
|
|
found = -1;
|
|
retlen = UserRights[i].Length/sizeof(wchar_t);
|
|
for (j = 0; j < *PrivCount; j++) {
|
|
found = wcsncmp(PrivList[j], UserRights[i].Buffer,
|
|
retlen);
|
|
if (found == 0)
|
|
break;
|
|
}
|
|
if (found != 0) {
|
|
PrivList[*PrivCount] =
|
|
(wchar_t *)malloc(UserRights[i].MaximumLength);
|
|
if (PrivList[*PrivCount] == NULL)
|
|
return (RTN_NOMEMORY);
|
|
|
|
wcsncpy(PrivList[*PrivCount], UserRights[i].Buffer,
|
|
retlen);
|
|
PrivList[*PrivCount][retlen] = L'\0';
|
|
(*PrivCount)++;
|
|
}
|
|
|
|
}
|
|
|
|
return (Status);
|
|
}
|
|
|
|
void
|
|
DisplayNtStatus(LPSTR szAPI, NTSTATUS Status) {
|
|
/* Convert the NTSTATUS to Winerror. Then call DisplayWinError(). */
|
|
DisplayWinError(szAPI, LsaNtStatusToWinError(Status));
|
|
}
|
|
|
|
void
|
|
DisplayWinError(LPSTR szAPI, DWORD WinError) {
|
|
LPSTR MessageBuffer;
|
|
DWORD dwBufferLength;
|
|
|
|
if (dwBufferLength=FormatMessageA(
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL, WinError, GetUserDefaultLangID(),
|
|
(LPSTR) &MessageBuffer, 0, NULL)){
|
|
DWORD dwBytesWritten; /* unused */
|
|
|
|
/* Output message string on stderr. */
|
|
WriteFile(GetStdHandle(STD_ERROR_HANDLE), MessageBuffer,
|
|
dwBufferLength, &dwBytesWritten, NULL);
|
|
|
|
/* Free the buffer allocated by the system. */
|
|
LocalFree(MessageBuffer);
|
|
}
|
|
}
|