/////////////////////////////////////////////////////////////////////////// // // Copyright(C) 1997-1998 Microsoft Corporation all rights reserved. // // Module: sdomachine.cpp // // Project: Everest // // Description: SDO Machine Implementation // // Author: TLP 9/1/98 // /////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include #include #include #include "sdomachine.h" #include "sdofactory.h" #include "sdo.h" #include "dspath.h" #include "sdodictionary.h" #include "sdoschema.h" #include "activeds.h" HRESULT WINAPI IASGetLDAPPathForUser( PCWSTR computerName, PCWSTR userName, BSTR* path ) throw () { HRESULT hr; // Check the pointers. if (!computerName || !userName || !path) { return E_POINTER; } // Check the string lengths, so we don't have to worry about overflow. if (wcslen(computerName) > MAX_PATH || wcslen(userName) > MAX_PATH) { return E_INVALIDARG; } // Initialize the out parameter. *path = NULL; // Form the LDAP path for the target computer. WCHAR root[8 + MAX_PATH]; wcscat(wcscpy(root, L"LDAP://"), computerName); // Get the IDirectorySearch interface. CComPtr search; // tperraut 453050 hr = ADsOpenObject( root, NULL, NULL, ADS_SECURE_AUTHENTICATION | ADS_USE_SIGNING | ADS_USE_SEALING, __uuidof(IDirectorySearch), (PVOID*)&search ); if (FAILED(hr)) { return hr; } // Form the search filter. WCHAR filter[18 + MAX_PATH]; wcscat(wcscat(wcscpy(filter, L"(sAMAccountName="), userName), L")"); // Execute the search. PWSTR attrs[] = { L"distinguishedName" }; ADS_SEARCH_HANDLE result; hr = search->ExecuteSearch( filter, attrs, 1, &result ); if (FAILED(hr)) { return hr; } // Get the first row. hr = search->GetFirstRow(result); if (SUCCEEDED(hr)) { // Get the column containing the distinguishedName. ADS_SEARCH_COLUMN column; hr = search->GetColumn(result, attrs[0], &column); if (SUCCEEDED(hr)) { // Sanity check the struct. if (column.dwADsType == ADSTYPE_DN_STRING && column.dwNumValues) { // Extract the DN. PCWSTR dn = column.pADsValues[0].DNString; // Get the Pathname object. IADsPathname* pathname; hr = CoCreateInstance( __uuidof(Pathname), NULL, CLSCTX_INPROC_SERVER, __uuidof(IADsPathname), (PVOID*)&pathname ); if (SUCCEEDED(hr)) { do { ///////// // Build the ADSI path. ///////// hr = pathname->Set(L"LDAP", ADS_SETTYPE_PROVIDER); if (FAILED(hr)) { break; } hr = pathname->Set((PWSTR)computerName, ADS_SETTYPE_SERVER); if (FAILED(hr)) { break; } hr = pathname->Set((PWSTR)dn, ADS_SETTYPE_DN); if (FAILED(hr)) { break; } hr = pathname->Retrieve(ADS_FORMAT_WINDOWS, path); } while (FALSE); pathname->Release(); } } else { // We got back a bogus ADS_SEARCH_COLUMN struct. hr = E_FAIL; } // Free the column data. search->FreeColumn(&column); } } // Close the search handle. search->CloseSearchHandle(result); return hr; } ////////////////////////////////////////////////////////////////////////////// CSdoMachine::CSdoMachine() : m_fAttached(false), m_fSchemaInitialized(false), m_pSdoSchema(NULL), m_pSdoDictionary(NULL), dsType(Directory_Unknown) { memset(&m_Limits, 0, sizeof(m_Limits)); } ////////////////////////////////////////////////////////////////////////////// CSdoMachine::~CSdoMachine() { if ( m_fAttached ) { m_dsIAS.Disconnect(); m_dsAD.Disconnect(); m_pSdoSchema->Release(); if ( m_pSdoDictionary ) m_pSdoDictionary->Release(); IASUninitialize(); } } ////////////////////// // ISdoMachine Methods ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CSdoMachine::Attach( /*[in]*/ BSTR computerName ) { CSdoLock theLock(*this); _ASSERT( ! m_fAttached ); if ( m_fAttached ) return E_FAIL; HRESULT hr = E_FAIL; try { if ( computerName ) IASTracePrintf("Machine SDO is attempting to attach to computer: '%ls'...", computerName); else IASTracePrintf("Machine SDO is attempting to attach to the local computer..."); IASTracePrintf("Machine SDO is initializing the IAS support services..."); if ( IASInitialize() ) { hr = m_dsIAS.Connect(computerName, NULL, NULL); if ( SUCCEEDED(hr) ) { DWORD error = IASGetProductLimits(computerName, &m_Limits); if (error == NO_ERROR) { IASTracePrintf("Machine SDO is creating the SDO schema..."); hr = CreateSDOSchema(); if ( SUCCEEDED(hr) ) { IASTracePrintf("Machine SDO has successfully attached to computer: '%ls'...",m_dsIAS.GetServerName()); m_fAttached = true; } else { m_dsIAS.Disconnect(); IASUninitialize(); } } else { hr = HRESULT_FROM_WIN32(error); m_dsIAS.Disconnect(); IASUninitialize(); } } else { IASTracePrintf("Error in Machine SDO - Attach() - Could not connect to IAS data store..."); } } else { IASTracePrintf("Error in Machine SDO - Attach() - Could not initialize IAS support services..."); } } catch(...) { IASTracePrintf("Error in Machine SDO - Attach() - Caught unknown exception..."); hr = E_UNEXPECTED; } return hr; } ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CSdoMachine::GetDictionarySDO( /*[out]*/ IUnknown** ppDictionarySdo ) { CSdoLock theLock(*this); IASTracePrintf("Machine SDO is retrieving the Dictionary SDO..."); HRESULT hr = S_OK; try { do { // Check preconditions // _ASSERT( m_fAttached ); if ( ! m_fAttached ) { hr = E_FAIL; break; } _ASSERT( NULL != ppDictionarySdo ); if ( NULL == ppDictionarySdo ) { hr = E_POINTER; break; } // The dictionary is a singleton... // if (NULL == m_pSdoDictionary) { hr = InitializeSDOSchema(); if ( FAILED(hr) ) { IASTracePrintf("Error in Machine SDO - GetDictionarySDO() - Could not initialize the SDO schema..."); break; } // Create the Dictionary SDO CComPtr pSdoDictionary; hr = SdoDictionary::createInstance( m_dsIAS.GetConfigPath(), !m_dsIAS.IsRemoteServer(), (SdoDictionary**)&m_pSdoDictionary ); if (FAILED(hr)) { IASTraceFailure("SdoDictionary::createInstance", hr); break; } } // Return the dictionary interface to the caller // (*ppDictionarySdo = m_pSdoDictionary)->AddRef(); } while ( FALSE ); } catch(...) { IASTracePrintf("Error in Machine SDO - GetDictionarySDO() - Caught unknown exception..."); hr = E_UNEXPECTED; } return hr; } const wchar_t g_IASService[] = L"IAS"; const wchar_t g_RASService[] = L"RemoteAccess"; const wchar_t g_Sentinel[] = L"Sentinel"; LPCWSTR CSdoMachine::m_SupportedServices[MACHINE_MAX_SERVICES] = { g_IASService, g_RASService, g_Sentinel }; ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CSdoMachine::GetServiceSDO( /*[in]*/ IASDATASTORE dataStore, /*[in]*/ BSTR serviceName, /*[out]*/ IUnknown** ppServiceSdo ) { CSdoLock theLock(*this); _ASSERT( m_fAttached ); if ( ! m_fAttached ) return E_FAIL; _ASSERT( ( DATA_STORE_LOCAL == dataStore || DATA_STORE_DIRECTORY == dataStore ) && NULL != serviceName && NULL != ppServiceSdo ); if ( ( DATA_STORE_LOCAL != dataStore && DATA_STORE_DIRECTORY != dataStore ) || NULL == serviceName || NULL == ppServiceSdo ) { return E_INVALIDARG; } IASTracePrintf("Machine SDO is attempting to retrieve the Service SDO for service: %ls...", serviceName); int i; for ( i = 0; i < MACHINE_MAX_SERVICES; i++ ) { if ( ! lstrcmp(serviceName, m_SupportedServices[i]) ) { break; } else { if ( ! lstrcmp(m_SupportedServices[i], g_Sentinel ) ) return E_INVALIDARG; } } HRESULT hr = E_INVALIDARG; try { do { hr = InitializeSDOSchema(); if ( FAILED(hr) ) { IASTracePrintf("Error in Machine SDO - GetServiceSDO() - Could not initialize the SDO schema..."); break; } CComBSTR bstrServiceName(DS_OBJECT_SERVICE); if (!bstrServiceName) { hr = E_OUTOFMEMORY; break; } CComPtr pDSObject; hr = (m_dsIAS.GetDSRootContainer())->Item(bstrServiceName, &pDSObject); if ( FAILED(hr) ) { IASTracePrintf("Error in Machine SDO - GetServiceSDO() - Could not locate IAS service data store..."); break; } CComPtr pSdoService; pSdoService.p = ::MakeSDO( serviceName, SDO_PROG_ID_SERVICE, static_cast(this), pDSObject, NULL, false ); if ( NULL == pSdoService.p ) { IASTracePrintf("Error in Machine SDO - GetServiceSDO() - MakeSDO() failed..."); hr = E_FAIL; break; } hr = pSdoService->QueryInterface(IID_IDispatch, (void**)ppServiceSdo); if ( FAILED(hr) ) { IASTracePrintf("Error in Machine SDO - GetServiceSDO() - QueryInterface(IDispatch) failed..."); break; } } while ( FALSE ); } catch(...) { IASTracePrintf("Error in Machine SDO - GetServiceSDO() - Caught unknown exception..."); hr = E_UNEXPECTED; } return hr; } ////////////////////////////////////////////////////////////////////////////// const wchar_t DOWNLEVEL_NAME[] = L"downlevel"; STDMETHODIMP CSdoMachine::GetUserSDO( /*[in]*/ IASDATASTORE eDataStore, /*[in]*/ BSTR bstrUserName, /*[out]*/ IUnknown** ppUserSdo ) { CSdoLock theLock(*this); _ASSERT( m_fAttached ); if ( ! m_fAttached ) return E_FAIL; _ASSERT( ( DATA_STORE_LOCAL == eDataStore || DATA_STORE_DIRECTORY == eDataStore ) && NULL != bstrUserName && NULL != ppUserSdo ); if ( ( DATA_STORE_LOCAL != eDataStore && DATA_STORE_DIRECTORY != eDataStore ) || NULL == bstrUserName || NULL == ppUserSdo ) return E_INVALIDARG; HRESULT hr = E_FAIL; // Map local users to the DS if we're attached to a machine with // a directory. if (eDataStore == DATA_STORE_LOCAL && hasDirectory()) { eDataStore = DATA_STORE_DIRECTORY; } // If we're connecting to a directory and the username doesn't begin with // "LDAP://", then we'll assume it's a SAM account name. BSTR ldapPath = NULL; if (eDataStore == DATA_STORE_DIRECTORY && wcsncmp(bstrUserName, L"LDAP://", 7)) { hr = IASGetLDAPPathForUser( m_dsIAS.GetServerName(), bstrUserName, &ldapPath ); if (FAILED(hr)) { return hr; } bstrUserName = ldapPath; } IASTracePrintf("Machine SDO is attempting to retrieve the RAS User SDO for user: %ls...", bstrUserName); ISdo* pSdoUser = NULL; try { do { // Get the IDataStoreObject interface for the new User SDO. // We'll use the IDataStoreObject interface to read/write User SDO properties. // bool fUseDownLevelAPI = false; bool fUseNetAPI = true; CComPtr pDSObject; _variant_t vtSAMAccountName; if ( DATA_STORE_DIRECTORY == eDataStore ) { // Make sure we're connected to the directory // if ( ! m_dsAD.IsConnected() ) { hr = m_dsAD.Connect(m_dsIAS.GetServerName(), NULL, NULL); if ( FAILED(hr) ) { IASTracePrintf("Error in Machine SDO - GetUserSDO() - Could not connect to the directory data store..."); break; } } // Make sure it's initialized. hr = m_dsAD.InitializeDS(); if ( FAILED(hr) ) { IASTracePrintf("Error in Machine SDO - GetUserSDO() - Could not initialize the directory data store..."); break; } // Get the user object from the directory // hr = (m_dsAD.GetDSRoot())->OpenObject(bstrUserName, &pDSObject); if ( FAILED(hr) ) { IASTracePrintf("Error in Machine SDO - GetUserSDO() - Could not retrieve user object from DS..."); break; } // If we're connected to a dc in a mixed domain we'll need to first get the users // SAM account name from the user object in the active directory and then treat the // GetUserSDO() call as if the caller specified DATA_STORE_LOCAL. We also use // downlevel APIs (SAM) because its a mixed domain. // if ( m_dsAD.IsMixedMode() ) { IASTracePrintf("Machine SDO - GetUserSDO() - Current DC (Server) %ls is in a mixed mode domain...", m_dsAD.GetServerName()); hr = pDSObject->GetValue(IAS_NTDS_SAM_ACCOUNT_NAME, &vtSAMAccountName); if ( FAILED(hr) ) { IASTracePrintf("Error in Server SDO - GetUserSDO() - Could not retrieve users SAM account name..."); break; } bstrUserName = V_BSTR(&vtSAMAccountName); fUseDownLevelAPI = true; pDSObject.Release(); IASTracePrintf("Server SDO - GetUserSDO() - User's SAM account name is: %ls...", (LPWSTR)bstrUserName); } else { // Use the directory data store object for all subsequent property read/write // operations on this User SDO. // IASTracePrintf("Server SDO - GetUserSDO() - Using active directory for user properties..."); fUseNetAPI = false; } } if ( fUseNetAPI ) { // Create the net data store and aquire the data store object interfaces // we'll use to complete the GetUserSDO() operation // CComPtr pDSNet; hr = CoCreateInstance( __uuidof(NetDataStore), NULL, CLSCTX_INPROC_SERVER, IID_IDataStore2, (void**)&pDSNet ); if ( FAILED(hr) ) { IASTracePrintf("Error in Machine SDO - GetUserSDO() - CoCreateInstance(NetDataStore) failed..."); break; } hr = pDSNet->Initialize(NULL, NULL, NULL); if ( FAILED(hr) ) { IASTracePrintf("Error in Machine SDO - GetUserSDO() - Could not initialize net data store..."); break; } CComPtr pDSRootObject; hr = pDSNet->get_Root(&pDSRootObject); if ( FAILED(hr) ) { IASTracePrintf("Error in Machine SDO - GetUserSDO() - Could not get Root object from net data store..."); break; } CComPtr pDSContainer; hr = pDSRootObject->QueryInterface(IID_IDataStoreContainer, (void**)&pDSContainer); if ( FAILED(hr) ) { IASTracePrintf("Error in Machine SDO - GetUserSDO() - QueryInterface(IID_IDataStoreContainer) failed..."); break; } // Get the name of the attached computer and use it to aquire a "Server" // (machine) object from the data store. // CComPtr pDSObjectMachine; if ( fUseDownLevelAPI ) { _bstr_t bstrServerName = m_dsAD.GetServerName(); IASTracePrintf("Machine SDO - GetUserSDO() - Using server %ls with downlevel APIs...", (LPWSTR)bstrServerName); hr = pDSContainer->Item(bstrServerName, &pDSObjectMachine); if ( FAILED(hr) ) { IASTracePrintf("Error in Machine SDO - GetUserSDO() - Could not obtain server object from net data store..."); break; } _bstr_t bstrDownLevel = DOWNLEVEL_NAME; _variant_t vtDownLevel; V_BOOL(&vtDownLevel) = VARIANT_TRUE; V_VT(&vtDownLevel) = VT_BOOL; hr = pDSObjectMachine->PutValue(bstrDownLevel, &vtDownLevel); if ( FAILED(hr) ) { IASTracePrintf("Error in Machine SDO - GetUserSDO() - Could not set downlevel data store mode..."); break; } } else { _bstr_t bstrServerName = m_dsIAS.GetServerName(); IASTracePrintf("Machine SDO - GetUserSDO() - Using server %ls with Net APIs...", (LPWSTR)bstrServerName); hr = pDSContainer->Item(bstrServerName, &pDSObjectMachine); if ( FAILED(hr) ) { IASTracePrintf("Error in Machine SDO - GetUserSDO() - Could not obtain server object from data store..."); break; } } pDSContainer.Release(); // Get "User" object from the "Server" object. We'll use the "User" object in // all subsequent read/write operations on the User SDO. // hr = pDSObjectMachine->QueryInterface(IID_IDataStoreContainer, (void**)&pDSContainer); if ( FAILED(hr) ) { IASTracePrintf("Error in Machine SDO - GetUserSDO() - QueryInterface(IID_IDataStoreContainer) failed..."); break; } hr = pDSContainer->Item(bstrUserName, &pDSObject); if ( FAILED(hr) ) { IASTracePrintf("Error in Machine SDO - GetUserSDO() - Could not obtain user object from data store..."); break; } } // Create and initialize the User SDO // pSdoUser = ::MakeSDO( NULL, SDO_PROG_ID_USER, static_cast(this), pDSObject, NULL, false ); if ( NULL == pSdoUser ) { IASTracePrintf("Error in Machine SDO - GetUserSDO() - MakeSDO() failed..."); hr = E_FAIL; } else { CComPtr pSdoDispatch; hr = pSdoUser->QueryInterface(IID_IDispatch, (void**)&pSdoDispatch); if ( FAILED(hr) ) IASTracePrintf("Error in Machine SDO - GetUserSDO() - QueryInterface(IDispatch) failed..."); else (*ppUserSdo = pSdoDispatch)->AddRef(); } } while ( FALSE); } catch(...) { IASTracePrintf("Error in Server SDO - GetUserSDO() - Caught unknown exception..."); hr = E_FAIL; } if ( pSdoUser ) pSdoUser->Release(); if (ldapPath) { SysFreeString(ldapPath); } return hr; } ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CSdoMachine::GetOSType( /*[out]*/ IASOSTYPE* eOSType ) { CSdoLock theLock(*this); _ASSERT( m_fAttached ); if ( ! m_fAttached ) return E_FAIL; _ASSERT( NULL != eOSType ); if ( NULL == eOSType ) return E_INVALIDARG; // Get the OS info now // HRESULT hr = m_objServerInfo.GetOSInfo ( (LPWSTR)m_dsIAS.GetServerName(), eOSType ); if ( FAILED (hr) ) IASTracePrintf("Error in Machine SDO - GetOSType() failed with error: %lx...", hr); return hr; } ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CSdoMachine::GetDomainType( /*[out]*/ IASDOMAINTYPE* eDomainType ) { CSdoLock theLock(*this); _ASSERT( m_fAttached ); if ( ! m_fAttached ) return E_FAIL; _ASSERT( NULL != eDomainType ); if ( NULL == eDomainType ) return E_INVALIDARG; HRESULT hr = m_objServerInfo.GetDomainInfo ( OBJECT_TYPE_COMPUTER, (LPWSTR)m_dsIAS.GetServerName(), eDomainType ); if (FAILED (hr)) IASTracePrintf("Error in Machine SDO - GetDomainType() - failed with error: %lx...", hr); return hr; } ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CSdoMachine::IsDirectoryAvailable( /*[out]*/ VARIANT_BOOL* boolDirectoryAvailable ) { CSdoLock theLock(*this); _ASSERT( m_fAttached ); if ( ! m_fAttached ) return E_FAIL; _ASSERT( NULL != boolDirectoryAvailable ); if ( NULL == boolDirectoryAvailable ) return E_INVALIDARG; *boolDirectoryAvailable = VARIANT_FALSE; return S_OK; } ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CSdoMachine::GetAttachedComputer( /*[out]*/ BSTR* bstrComputerName ) { CSdoLock theLock(*this); _ASSERT( m_fAttached ); if ( ! m_fAttached ) return E_FAIL; _ASSERT( NULL != bstrComputerName ); if ( NULL == bstrComputerName ) return E_INVALIDARG; *bstrComputerName = SysAllocString(m_dsIAS.GetServerName()); if ( NULL != *bstrComputerName ) return S_OK; else return E_OUTOFMEMORY; } ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CSdoMachine::GetSDOSchema( /*[out]*/ IUnknown** ppSDOSchema ) { CSdoLock theLock(*this); _ASSERT( m_fAttached ); if ( ! m_fAttached ) return E_FAIL; _ASSERT( NULL != ppSDOSchema ); if ( NULL == ppSDOSchema ) return E_INVALIDARG; (*ppSDOSchema = m_pSdoSchema)->AddRef(); return S_OK; } STDMETHODIMP CSdoMachine::get_Limits(IAS_PRODUCT_LIMITS* pVal) { if (pVal == 0) { return E_POINTER; } *pVal = m_Limits; return S_OK; } /////////////////////////////////////////////////////////////////////////////// // Private member functions /////////////////////////////////////////////////////////////////////////////// HRESULT CSdoMachine::CreateSDOSchema() { auto_ptr pSchema (new SDO_SCHEMA_OBJ); HRESULT hr = pSchema->Initialize(NULL); if ( SUCCEEDED(hr) ) m_pSdoSchema = dynamic_cast(pSchema.release()); return hr; } /////////////////////////////////////////////////////////////////////////////// HRESULT CSdoMachine::InitializeSDOSchema() { HRESULT hr = S_OK; if ( ! m_fSchemaInitialized ) { // First initialize the IAS data store // hr = m_dsIAS.InitializeDS(); if ( SUCCEEDED(hr) ) { // Get the root data store object for the SDO schema // CComPtr pDSRootContainer = m_dsIAS.GetDSRootContainer(); _bstr_t bstrSchemaName = SDO_SCHEMA_ROOT_OBJECT; CComPtr pSchemaDataStore; hr = pDSRootContainer->Item(bstrSchemaName, &pSchemaDataStore); if ( SUCCEEDED(hr) ) { // Initialize the SDO schema from the SDO schema data store // PSDO_SCHEMA_OBJ pSchema = dynamic_cast(m_pSdoSchema); hr = pSchema->Initialize(pSchemaDataStore); if ( SUCCEEDED(hr) ) m_fSchemaInitialized = true; } else { IASTracePrintf("Error in Machine SDO - InitializeSDOSchema() - Could not locate schema data store..."); } } else { IASTracePrintf("Error in Machine SDO - InitializeSDOSchema() - Could not initialize the IAS data store..."); } } return hr; } // Returns TRUE if the attached machine has a DS. BOOL CSdoMachine::hasDirectory() throw () { if (dsType == Directory_Unknown) { PDSROLE_PRIMARY_DOMAIN_INFO_BASIC info; DWORD error = DsRoleGetPrimaryDomainInformation( m_dsIAS.GetServerName(), DsRolePrimaryDomainInfoBasic, (PBYTE*)&info ); if (error == NO_ERROR) { if (info->Flags & DSROLE_PRIMARY_DS_RUNNING) { dsType = Directory_Available; } else { dsType = Directory_None; } NetApiBufferFree(info); } } return dsType == Directory_Available; }