530 lines
13 KiB
C++
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
|