windows-nt/Source/XPSP1/NT/admin/admt/disp/admtaccount.cpp
2020-09-26 16:20:57 +08:00

530 lines
13 KiB
C++

#include "StdAfx.h"
#include "AdmtAccount.h"
#include <LM.h>
#include <NtSecApi.h>
#include <ActiveDS.h>
#include <PWGen.hpp>
#include <AdsiHelpers.h>
#include <ResStr.h>
#pragma comment(lib, "NetApi32.lib")
#pragma comment(lib, "ActiveDS.lib")
#pragma comment(lib, "adsiid.lib")
//#pragma comment(lib, "ntdsapi.lib")
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#endif
using namespace _com_util;
namespace nsAdmtAccount
{
bool __stdcall IsComputerDomainController();
CADsContainer __stdcall GetAccountContainer(bool bLocal);
_bstr_t __stdcall GetAccountName();
_bstr_t __stdcall CreateAccountPassword();
CADsUser __stdcall GetAccount(bool bLocal, CADsContainer& adsContainer, _bstr_t strName);
CADsUser __stdcall CreateAccount(bool bLocal, CADsContainer& adsContainer, _bstr_t strName, _bstr_t strPassword);
_bstr_t __stdcall LoadPasswordFromPrivateData();
void __stdcall SavePasswordToPrivateData(LPCTSTR pszPassword);
inline _bstr_t __stdcall GetAccountPassword()
{
return LoadPasswordFromPrivateData();
}
void _cdecl Trace(LPCTSTR pszFormat, ...)
{
#ifdef _DEBUG
if (pszFormat)
{
_TCHAR szMessage[2048];
va_list args;
va_start(args, pszFormat);
_vsntprintf(szMessage, 2048, pszFormat, args);
va_end(args);
OutputDebugString(szMessage);
}
#endif
}
}
using namespace nsAdmtAccount;
//---------------------------------------------------------------------------
// GetOptionsCredentials Method
//
// Retrieves the options credentials.
//---------------------------------------------------------------------------
HRESULT __stdcall GetOptionsCredentials(_bstr_t& strDomain, _bstr_t& strUserName, _bstr_t& strPassword)
{
HRESULT hr = S_OK;
try
{
// use local account if computer is not a domain controller
bool bLocal = IsComputerDomainController() ? false : true;
// get account container
CADsContainer adsContainer = GetAccountContainer(bLocal);
// get account name
strUserName = GetAccountName();
// get account password
bool bNewPassword = false;
strPassword = GetAccountPassword();
if (!strPassword)
{
strPassword = CreateAccountPassword();
bNewPassword = true;
}
// get account
CADsUser adsUser = GetAccount(bLocal, adsContainer, strUserName);
// if account exists...
if (adsUser)
{
// if new password...
// this may occur if administrator or system deletes
// the private data key where the password is stored
if (bNewPassword)
{
// set new password
adsUser.SetPassword(strPassword);
}
}
else
{
// create account
CreateAccount(bLocal, adsContainer, strUserName, strPassword);
}
// get account domain (NetBIOS name)
if (bLocal)
{
_TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1];
DWORD cbComputerName = MAX_COMPUTERNAME_LENGTH + 1;
if (GetComputerName(szComputerName, &cbComputerName))
{
strDomain = szComputerName;
}
else
{
_com_issue_error(HRESULT_FROM_WIN32(GetLastError()));
}
}
else
{
strDomain = CADsADSystemInfo().GetDomainShortName();
}
}
catch (_com_error& ce)
{
hr = ce.Error();
}
catch (...)
{
hr = E_FAIL;
}
return hr;
}
namespace nsAdmtAccount
{
//---------------------------------------------------------------------------
// IsComputerDomainController Function
//
// Determines whether the local computer is a domain controller or not.
//---------------------------------------------------------------------------
bool __stdcall IsComputerDomainController()
{
Trace(_T("E IsComputerDomainController()\n"));
bool bIs = true;
PSERVER_INFO_101 psiInfo = NULL;
NET_API_STATUS nasStatus = NetServerGetInfo(NULL, 101, (LPBYTE*)&psiInfo);
if (nasStatus == ERROR_SUCCESS)
{
// if either the domain controller or backup domain controller type
// bit is set then the local computer is a domain controller
if (psiInfo->sv101_type & (SV_TYPE_DOMAIN_CTRL|SV_TYPE_DOMAIN_BAKCTRL))
{
bIs = true;
}
else
{
bIs = false;
}
NetApiBufferFree(psiInfo);
}
else
{
_com_issue_error(HRESULT_FROM_WIN32(nasStatus));
}
Trace(_T("L IsComputerDomainController() : %s\n"), bIs ? _T("true") : _T("false"));
return bIs;
}
//---------------------------------------------------------------------------
// GetAccountContainer Function
//
// Retrieves container interface either to local computer or the computer's
// domain Users container.
// The WinNT: provider is used for access to the local computer's local
// security authority.
// The LDAP: provider is used for access to the domain.
//---------------------------------------------------------------------------
CADsContainer __stdcall GetAccountContainer(bool bLocal)
{
Trace(_T("E GetAccountContainer(bLocal=%s)\n"), bLocal ? _T("true") : _T("false"));
CADsContainer adsContainer;
// if creating a local account...
if (bLocal)
{
// get container interface to computer
_TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1];
DWORD cbComputerName = MAX_COMPUTERNAME_LENGTH + 1;
if (GetComputerName(szComputerName, &cbComputerName))
{
// build path to local computer
_bstr_t strPath = _bstr_t(_T("WinNT://")) + szComputerName + _bstr_t(_T(",computer"));
// get container interface
adsContainer = CADsContainer(LPCTSTR(strPath));
}
else
{
_com_issue_error(HRESULT_FROM_WIN32(GetLastError()));
}
}
else
{
// retrieve default naming context for computer's domain
CADs adsRootDSE(_T("LDAP://rootDSE"));
_bstr_t strDefaultNamingContext = adsRootDSE.Get(_T("defaultNamingContext"));
// build path to Users container in the domain
CADsPathName adsPathName(_T("LDAP"), ADS_SETTYPE_PROVIDER);
adsPathName.Set(strDefaultNamingContext, ADS_SETTYPE_DN);
adsPathName.AddLeafElement(_T("CN=Users"));
// get container interface
adsContainer = CADsContainer(LPCTSTR(adsPathName.Retrieve(ADS_FORMAT_X500)));
}
Trace(_T("L GetAccountContainer() : %p\n"), adsContainer.operator IADsContainer*());
return adsContainer;
}
//---------------------------------------------------------------------------
// GetAccountName Function
//
// Generates account name based on the local computer's name.
//---------------------------------------------------------------------------
_bstr_t __stdcall GetAccountName()
{
Trace(_T("E GetAccountName()\n"));
_bstr_t strName;
_TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1];
DWORD cbComputerName = MAX_COMPUTERNAME_LENGTH + 1;
if (GetComputerName(szComputerName, &cbComputerName))
{
strName = _T("ADMT_");
strName += szComputerName;
}
else
{
_com_issue_error(HRESULT_FROM_WIN32(GetLastError()));
}
Trace(_T("L GetAccountName() : '%s'\n"), LPCTSTR(strName));
return strName;
}
//---------------------------------------------------------------------------
// CreateAccountPassword Function
//
// Creates complex password, stores it in the local security authority
// private data and returns the password.
//---------------------------------------------------------------------------
_bstr_t __stdcall CreateAccountPassword()
{
Trace(_T("E CreateAccountPassword()\n"));
// generate complex password
WCHAR szPassword[32];
EaPasswordGenerate(3, 3, 3, 3, 2, 12, szPassword, sizeof(szPassword) / sizeof(szPassword[0]));
SavePasswordToPrivateData(szPassword);
Trace(_T("L CreateAccountPassword() : '%s'\n"), szPassword);
return szPassword;
}
//---------------------------------------------------------------------------
// GetAccount Function
//
// Gets local or domain user account in the specified container.
//---------------------------------------------------------------------------
CADsUser __stdcall GetAccount(bool bLocal, CADsContainer& adsContainer, _bstr_t strName)
{
Trace(_T("E GetAccount(bLocal=%s, adsContainer=%p, strName='%s')\n"), bLocal ? _T("true") : _T("false"), adsContainer.operator IADsContainer*(), LPCTSTR(strName));
IDispatchPtr spDispatch;
try
{
spDispatch = adsContainer.GetObject(_T("user"), bLocal ? strName : _T("CN=") + strName);
}
catch (_com_error& ce)
{
switch (ce.Error())
{
case HRESULT_FROM_WIN32(ERROR_DS_NO_SUCH_OBJECT):
case HRESULT_FROM_WIN32(NERR_UserNotFound):
break;
default:
throw;
break;
}
}
Trace(_T("L GetAccount() : %p\n"), spDispatch.GetInterfacePtr());
return spDispatch;
}
//---------------------------------------------------------------------------
// CreateAccount Function
//
// Creates local or domain user account in the specified container.
// WinNT provider is used to create a local account whereas the LDAP provider
// is used to create a domain account.
//---------------------------------------------------------------------------
CADsUser __stdcall CreateAccount(bool bLocal, CADsContainer& adsContainer, _bstr_t strName, _bstr_t strPassword)
{
Trace(_T("E CreateAccount(bLocal=%s, adsContainer=%p, strName='%s', strPassword='%s')\n"), bLocal ? _T("true") : _T("false"), adsContainer.operator IADsContainer*(), LPCTSTR(strName), LPCTSTR(strPassword));
CADsUser adsUser;
// the relative name must be prefixed only for LDAP provider
adsUser = adsContainer.Create(_T("user"), bLocal ? strName : _T("CN=") + strName);
// if not local account...
if (bLocal == false)
{
// the SAM account name attribute is mandatory in active directory
adsUser.Put(_T("sAMAccountName"), strName);
}
adsUser.SetDescription(GET_BSTR(IDS_ADMT_ACCOUNT_DESCRIPTION));
// active directory account must exist first before password can be set
// whereas local accounts may be created with the password attribute
// this becomes important if password policies are in effect because
// the WinNT provider will fail to create the account if the password
// does not meet password policy
// the LDAP provider creates the account disabled by default and therefore
// the password does not need to meet password policy
if (bLocal == false)
{
adsUser.SetInfo();
}
// the password can only be set after the user account is created
adsUser.SetPassword(strPassword);
// enable account after setting the account password
// set password cannot be changed to remind the administrator not to change the password
// note that the administrator can still set the password even with this flag set
// set do not expire password as we do not want the password to expire
adsUser.Put(
bLocal ? _T("UserFlags") : _T("userAccountControl"),
long(ADS_UF_NORMAL_ACCOUNT|ADS_UF_PASSWD_CANT_CHANGE|ADS_UF_DONT_EXPIRE_PASSWD)
);
adsUser.SetInfo();
Trace(_T("L CreateAccount() : %p\n"), adsUser.operator IADs*());
return adsUser;
}
// LSA private data key name for password storage
const WCHAR c_wszKeyName[] = L"L$4480F273-39D1-4FFA-BEB4-44DF6C848364";
const USHORT c_usKeyNameLength = sizeof(c_wszKeyName) - sizeof(c_wszKeyName[0]);
//---------------------------------------------------------------------------
// SavePasswordToPrivateData Method
//
// Stores password in local security authority private data (LSA Secret).
//---------------------------------------------------------------------------
void __stdcall SavePasswordToPrivateData(LPCTSTR pszPassword)
{
LSA_HANDLE hPolicy = NULL;
LSA_OBJECT_ATTRIBUTES loa = { sizeof(LSA_OBJECT_ATTRIBUTES), NULL, NULL, 0, NULL, NULL };
NTSTATUS ntsStatus = LsaOpenPolicy(NULL, &loa, POLICY_CREATE_SECRET, &hPolicy);
if (ntsStatus == STATUS_SUCCESS)
{
USHORT usPasswordLength = pszPassword ? wcslen(pszPassword) * sizeof(WCHAR) : 0;
LSA_UNICODE_STRING lusKeyName = { c_usKeyNameLength, c_usKeyNameLength, const_cast<PWSTR>(c_wszKeyName) };
LSA_UNICODE_STRING lusPrivateData = { usPasswordLength, usPasswordLength, const_cast<PWSTR>(pszPassword) };
ntsStatus = LsaStorePrivateData(hPolicy, &lusKeyName, &lusPrivateData);
LsaClose(hPolicy);
if (ntsStatus != ERROR_SUCCESS)
{
_com_issue_error(HRESULT_FROM_WIN32(LsaNtStatusToWinError(ntsStatus)));
}
}
else
{
_com_issue_error(HRESULT_FROM_WIN32(LsaNtStatusToWinError(ntsStatus)));
}
}
//---------------------------------------------------------------------------
// LoadPasswordFromPrivateData Function
//
// Retrieves password from local security authority private data (LSA Secret).
//---------------------------------------------------------------------------
_bstr_t __stdcall LoadPasswordFromPrivateData()
{
_bstr_t strPassword;
LSA_HANDLE hPolicy = NULL;
try
{
// open local security authority policy
LSA_OBJECT_ATTRIBUTES loa = { sizeof(LSA_OBJECT_ATTRIBUTES), NULL, NULL, 0, NULL, NULL };
NTSTATUS ntsStatus = LsaOpenPolicy(NULL, &loa, POLICY_GET_PRIVATE_INFORMATION, &hPolicy);
if (ntsStatus == STATUS_SUCCESS)
{
// retrieve password from private data
LSA_UNICODE_STRING lusKeyName = { c_usKeyNameLength, c_usKeyNameLength, const_cast<PWSTR>(c_wszKeyName) };
PLSA_UNICODE_STRING plusPrivateData;
ntsStatus = LsaRetrievePrivateData(hPolicy, &lusKeyName, &plusPrivateData);
if (ntsStatus == STATUS_SUCCESS)
{
DWORD dwLength = plusPrivateData->Length / sizeof(WCHAR);
WCHAR szPassword[64];
wcsncpy(szPassword, plusPrivateData->Buffer, dwLength);
szPassword[dwLength] = L'\0';
strPassword = szPassword;
LsaFreeMemory(plusPrivateData);
}
else
{
DWORD dwError = LsaNtStatusToWinError(ntsStatus);
if (dwError != ERROR_FILE_NOT_FOUND)
{
_com_issue_error(HRESULT_FROM_WIN32(dwError));
}
}
// close local security authority policy
LsaClose(hPolicy);
}
else
{
_com_issue_error(HRESULT_FROM_WIN32(LsaNtStatusToWinError(ntsStatus)));
}
}
catch (...)
{
if (hPolicy)
{
LsaClose(hPolicy);
}
throw;
}
return strPassword;
}
} // namespace nsAdmtAccount