#include "StdAfx.h" #include "AdmtAccount.h" #include #include #include #include #include #include #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(c_wszKeyName) }; LSA_UNICODE_STRING lusPrivateData = { usPasswordLength, usPasswordLength, const_cast(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(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