//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 2000. // // File: session.cxx // // Contents: TSession interfaces. // // History: 3-30-97 MohamedN Created // //---------------------------------------------------------------------------- #include #pragma hdrstop #define DBINITCONSTANTS #include #undef DBINITCONSTANTS #include #include // CQuerySpec // Constants ----------------------------------------------------------------- // Session object interfaces that support ISupportErrorInfo static const GUID* apSessionErrInt[] = { &IID_IDBCreateCommand, &IID_IGetDataSource, &IID_IOpenRowset, &IID_ISessionProperties, }; static const ULONG cSessionErrInt = NUMELEM( apSessionErrInt ); //+--------------------------------------------------------------------------- // // Class: CDBSession::CDBSession // // Purpose: ctor // // History: 3-30-97 MohamedN Created // // Notes: // //---------------------------------------------------------------------------- CDBSession::CDBSession( CDataSrc & dataSrc, IUnknown * pUnkOuter, IUnknown ** ppUnkInner, IParserSession * pIPSession, HANDLE hToken ) : _dataSrc(dataSrc), #pragma warning(disable : 4355) // 'this' in ctor _impIUnknown(this), _ErrorInfo( * ((IUnknown *) (IOpenRowset *) this), _mtxSess ), #pragma warning(default : 4355) // 'this' in ctor. _xSessionToken( hToken ) { _pUnkOuter = pUnkOuter ? pUnkOuter : (IUnknown *) &_impIUnknown; _ErrorInfo.SetInterfaceArray( cSessionErrInt, apSessionErrInt ); _dataSrc.AddRef(); _dataSrc.IncSessionCount(); // // SQL Text Parser // Win4Assert( pIPSession ); _xIPSession.Set( pIPSession ); _xIPSession->AddRef(); *ppUnkInner = (IUnknown *) &_impIUnknown; (*ppUnkInner)->AddRef(); } //+--------------------------------------------------------------------------- // // Class: CDBSession::~CDBSession // // Purpose: dtor // // History: 3-30-97 MohamedN Created // // Notes: // //---------------------------------------------------------------------------- CDBSession::~CDBSession() { _dataSrc.DecSessionCount(); _dataSrc.Release(); } //+--------------------------------------------------------------------------- // // Member: CDBSession::RealQueryInterface // // Synopsis: Supports IID_IUnknown, // IID_IGetDataSource, // IID_IOpenRowset, // IID_ISessionProperties, // IID_IDBCreateCommand // IID_ISupportErrorInfo // // History: 03-30-97 mohamedn created // 10-18-97 danleg added ISupportErrorInfo Support // 01-29-98 danleg non delegating QI when not aggregated //---------------------------------------------------------------------------- STDMETHODIMP CDBSession::RealQueryInterface(REFIID riid, void **ppvObj ) { SCODE sc = S_OK; if ( !ppvObj ) return E_INVALIDARG; *ppvObj = 0; if ( riid == IID_IUnknown ) { *ppvObj = (void *) ( (IUnknown *) (IOpenRowset *) this ); } else if ( riid == IID_IGetDataSource ) { *ppvObj = (void *) (IGetDataSource *) this; } else if ( riid == IID_ISessionProperties ) { *ppvObj = (void *) (ISessionProperties *) this; } else if ( riid == IID_IOpenRowset ) { *ppvObj = (void *) (IOpenRowset *) this; } else if ( riid == IID_IDBCreateCommand ) { *ppvObj = (void *) (IDBCreateCommand *) this; } else if ( riid == IID_ISupportErrorInfo ) { *ppvObj = (void *) ((IUnknown *) (ISupportErrorInfo *) &_ErrorInfo); } else { *ppvObj = 0; sc = E_NOINTERFACE; } return sc; } //RealQueryInterface //+--------------------------------------------------------------------------- // // Method: CDBSession::GetDataSource // // Synopsis: obtains the owning data source object // // Arguments: [riid] - interface to bind // [ppDataSource] - interface returned here // // returns: S_OK, E_NOINTERFACE // // History: 3-30-97 mohamedn Created // 11-20-97 danleg QI on OuterUnk // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CDBSession::GetDataSource( REFIID riid, IUnknown ** ppDataSource ) { SCODE sc = S_OK; // Clear previous Error Object for this thread _ErrorInfo.ClearErrorInfo(); // Check Function Arguments if ( 0 == ppDataSource ) return _ErrorInfo.PostHResult( E_INVALIDARG, IID_IGetDataSource ); TRANSLATE_EXCEPTIONS; TRY { CLock lck( _mtxSess ); *ppDataSource = 0; sc = (_dataSrc.GetOuterUnk())->QueryInterface( riid, (void **)ppDataSource ); if ( FAILED(sc) ) THROW( CException(sc) ); } CATCH( CException, e ) { sc = _ErrorInfo.PostHResult( e, IID_IGetDataSource ); } END_CATCH; UNTRANSLATE_EXCEPTIONS; return sc; } //+------------------------------------------------------------------------- // // Function: CDBSession::GetProperties // // Synopsis: gets ISessionProperties // // Arguments: [cPropertyIDSets] - number of desired property set IDs or 0 // [pPropIDSets] - array of desired property set IDs or NULL // [pcPropertySets] - number of property sets returned // [prgPropertySets] - array of returned property sets // // Returns: SCODE - result code indicating error return status. One of // S_OK, DB_S_ERRORSOCCURRED, E_INVALIDARG. // // History: 03-30-97 mohamedn Created // 10-30-97 danleg Changed to use common property code //-------------------------------------------------------------------------- STDMETHODIMP CDBSession::GetProperties(ULONG cPropertySets, const DBPROPIDSET rgPropertySets[], ULONG* pcProperties, DBPROPSET** prgProperties) { SCODE sc = S_OK; // Clear previous Error Object for this thread _ErrorInfo.ClearErrorInfo(); TRANSLATE_EXCEPTIONS; TRY { CLock lck( _mtxSess ); _UtlProps.GetPropertiesArgChk( cPropertySets, rgPropertySets, pcProperties, prgProperties ); sc = _UtlProps.GetProperties( cPropertySets, rgPropertySets, pcProperties, prgProperties ); if ( FAILED(sc) ) THROW( CException(sc) ); } CATCH( CException, e ) { sc = _ErrorInfo.PostHResult( e, IID_ISessionProperties ); } END_CATCH; UNTRANSLATE_EXCEPTIONS; return sc; } //+--------------------------------------------------------------------------- // // Function: CDBSession::SetProperties // // Synopsis: sets ISessionProperties // // Arguments: [cPropertySets] - number of property sets // [rgPropertySets] - array of property sets // // Returns: SCODE - result code indicating error return status. One of // S_OK, DB_S_ERRORSOCCURRED, E_INVALIDARG. // // History: 03-30-97 mohamedn created // 10-28-97 danleg Changed to use common property code //---------------------------------------------------------------------------- STDMETHODIMP CDBSession::SetProperties(ULONG cPropertySets, DBPROPSET rgPropertySets[]) { SCODE sc = S_OK; // Win4Assert( _pCUtlProps ); // Clear previous Error Object for this thread _ErrorInfo.ClearErrorInfo(); // Quick return if the Count of Properties is 0 if( cPropertySets == 0 ) return S_OK; TRANSLATE_EXCEPTIONS; TRY { CLock lck( _mtxSess ); _UtlProps.SetPropertiesArgChk( cPropertySets, rgPropertySets ); sc = _UtlProps.SetProperties( cPropertySets, rgPropertySets); if ( FAILED(sc) ) THROW( CException(sc) ); } CATCH( CException, e ) { sc = _ErrorInfo.PostHResult( e, IID_ISessionProperties ); } END_CATCH; UNTRANSLATE_EXCEPTIONS; return sc; } //+--------------------------------------------------------------------------- // // Method: CDBSession::CreateCommand // // Synopsis: Creates an ICommand object // // Arguments: [pUnkOuter] -- 'Outer' IUnknown // [riid] -- Interface to bind // [ppCommand] -- Interface returned here // // // returns: SCODE of success or failure. // // History: 03-30-97 mohamedn Created // 11-03-97 danleg Aggregation support & error posting // // Notes: // //---------------------------------------------------------------------------- STDMETHODIMP CDBSession::CreateCommand ( IUnknown * pUnkOuter, REFIID riid, IUnknown ** ppCommand ) { // Clear previous Error Object for this thread _ErrorInfo.ClearErrorInfo(); // Check Function Arguments if ( 0 == ppCommand ) return _ErrorInfo.PostHResult( E_INVALIDARG, IID_IDBCreateCommand ); if (0 != pUnkOuter && riid != IID_IUnknown) return _ErrorInfo.PostHResult( DB_E_NOAGGREGATION, IID_IDBCreateCommand ); *ppCommand = 0; SCODE sc = S_OK; CQuerySpec * pQuery = 0; TRANSLATE_EXCEPTIONS; TRY { IUnknown * pUnkInner = 0; // Serialize access to this object. CLock lck( _mtxSess ); pQuery = new CQuerySpec( pUnkOuter, &pUnkInner, this ); XInterface xUnkInner( pUnkInner ); if ( IID_IUnknown == riid ) { *ppCommand = pUnkInner; } else { sc = pUnkInner->QueryInterface( riid, (void **)ppCommand ); if ( FAILED(sc) ) { Win4Assert( 0 == *ppCommand ); THROW( CException(sc) ); } } } CATCH( CException, e ) { sc = _ErrorInfo.PostHResult( e, IID_IDBCreateCommand ); } END_CATCH; UNTRANSLATE_EXCEPTIONS; return sc; } //CreateCommand // // Constants used by OpenRowset // static const WCHAR BASE_SELECT[] = L"SELECT Path, FileName, Size, Write,"\ L"Attrib, DocTitle, DocAuthor, DocSubject,"\ L"DocKeywords, Characterization FROM "\ L"SCOPE('SHALLOW TRAVERSAL OF %s')"; //Approx. size (Includes space for NULL-TERMINATOR, do not subtract this out) const ULONG APPROX_CCH_BASE_SELECT = sizeof(BASE_SELECT) / sizeof(WCHAR); //+--------------------------------------------------------------------------- // // Method: CDBSession::OpenRowset // // Synopsis: Opens and returns a rowset that includes all rows from // a single base table // // Arguments: [pUnkOuter] - Controlling unknown, if any // [pTableID] - Table to open // [pIndexID] - DBID of the index // [riid] - Interface to return // [cPropertySets] - Count of properties // [rgPropertySets] - Array of property values // [ppRowset] - Where to return interface // // Returns: S_OK - The method succeeded // E_INVALIDARG - pTableID and pIndexId were NULL // E_FAIL - Provider-specific error // DB_E_NOTABLE - Specified table does not exist in current Data // Data Source object // E_OUTOFMEMORY - Out of memory // E_NOINTERFACE - The requested interface was not available // // History: 10-26-97 danelg Created from Monarch // //---------------------------------------------------------------------------- STDMETHODIMP CDBSession::OpenRowset ( IUnknown * pUnkOuter, DBID * pTableID, DBID * pIndexID, REFIID riid, ULONG cPropertySets, DBPROPSET rgPropertySets[], IUnknown ** ppRowset ) { ULONG ul, ul2; SCODE sc = S_OK; SCODE scProp = S_OK; // Clear previous Error Object for this thread _ErrorInfo.ClearErrorInfo(); // Intialize Buffer if( ppRowset ) *ppRowset = NULL; if ( 0 != pUnkOuter && IID_IUnknown != riid ) return _ErrorInfo.PostHResult( DB_E_NOAGGREGATION, IID_IOpenRowset ); // Check Arguments if( (!pTableID && !pIndexID) ) return _ErrorInfo.PostHResult( E_INVALIDARG, IID_IOpenRowset ); // We only accept NULL for pIndexID if( pIndexID ) return _ErrorInfo.PostHResult( DB_E_NOINDEX, IID_IOpenRowset ); // If the eKind is not known to use, basically it // means we have no table identifier if( (!pTableID) || (pTableID->eKind != DBKIND_NAME) || ((pTableID->eKind == DBKIND_NAME) && (!(pTableID->uName.pwszName))) || (wcslen(pTableID->uName.pwszName) == 0) ) return _ErrorInfo.PostHResult( DB_E_NOTABLE, IID_IOpenRowset ); // We do not allow the riid to be IID_NULL if( riid == IID_NULL ) return _ErrorInfo.PostHResult( E_NOINTERFACE, IID_IOpenRowset ); TRANSLATE_EXCEPTIONS; TRY { // Serialize access to this object. CLock lck( _mtxSess ); XInterface xICmdText; IUnknown * pUnkInner; // Check Arguments for use by properties CUtlProps::SetPropertiesArgChk( cPropertySets, rgPropertySets ); // // pUnkOuter is the outer unkown from the user on the Session // object. Don't use pUnkOuter for the Command object here. // XInterface xQuery( new CQuerySpec(0, &pUnkInner, this) ); // Tell the command object to post errors as IOpenRowset xQuery->ImpersonateOpenRowset(); // Construct and set Command. Allocate buffer for SQL Statement XArray xwszBuff( wcslen(pTableID->uName.pwszName) + APPROX_CCH_BASE_SELECT ); //@devnote: swprintf not supported on win95? swprintf( xwszBuff.Get(), BASE_SELECT, pTableID->uName.pwszName ); sc = pUnkInner->QueryInterface( IID_ICommandText, xICmdText.GetQIPointer() ); if( SUCCEEDED(sc) ) { Win4Assert( !xICmdText.IsNull() ); sc = xICmdText->SetCommandText( DBGUID_SQL, xwszBuff.Get() ); // Process properties if ( SUCCEEDED(sc) && cPropertySets > 0) { sc = SetOpenRowsetProperties(xICmdText.GetPointer(), cPropertySets, rgPropertySets); scProp = sc; // Save this retcode. } // Execute the SQL Statement if we were given a ppRowset if ( SUCCEEDED(sc) && ppRowset ) { sc = xICmdText->Execute( pUnkOuter, riid, 0, 0, ppRowset ); if ( DB_E_ERRORSOCCURRED == sc && (cPropertySets > 0) ) { sc = MarkOpenRowsetProperties((xICmdText.GetPointer()), cPropertySets, rgPropertySets); } } } sc = (sc == S_OK) ? scProp : sc; if ( FAILED(sc) ) THROW( CException(sc) ); } CATCH( CException, e ) { sc = _ErrorInfo.PostHResult( e, IID_IOpenRowset ); } END_CATCH; UNTRANSLATE_EXCEPTIONS; return sc; } //+--------------------------------------------------------------------------- // // Member: CDBSession::SetOpenRowsetProperties, private // // Synopsis: Loop through the passed in properties andmark those in error. // Used by OpenRowset() // // History: 10-31-97 briants Created // //---------------------------------------------------------------------------- SCODE CDBSession::SetOpenRowsetProperties( ICommandText* pICmdText, ULONG cPropertySets, DBPROPSET rgPropertySets[] ) { Win4Assert( pICmdText != NULL ); XInterface xICmdProp; SCODE sc = pICmdText->QueryInterface( IID_ICommandProperties, (void **) xICmdProp.GetQIPointer() ); if( SUCCEEDED(sc) ) { Win4Assert( !xICmdProp.IsNull() ); sc = xICmdProp->SetProperties( cPropertySets, rgPropertySets ); if ( (DB_E_ERRORSOCCURRED == sc) || (DB_S_ERRORSOCCURRED == sc) ) { // If all the properties set were OPTIONAL then we can set our status to // DB_S_ERRORSOCCURED and continue for(ULONG ul=0;ul xICmdProp; DBPROPSET * pPropSets = 0; ULONG cPropSets = 0; DBPROPIDSET dbPropIdSet[1]; dbPropIdSet[0].guidPropertySet = DBPROPSET_PROPERTIESINERROR; dbPropIdSet[0].cPropertyIDs = 0; dbPropIdSet[0].rgPropertyIDs = 0; SCODE sc = pICmdText->QueryInterface( IID_ICommandProperties, (void **) xICmdProp.GetQIPointer() ); if( SUCCEEDED(sc) ) { Win4Assert( !xICmdProp.IsNull() ); sc = xICmdProp->GetProperties( 1, dbPropIdSet, &cPropSets, &pPropSets ); if( SUCCEEDED(sc) ) { XArrayOLEInPlace xPropSets; xPropSets.Set( cPropSets, (CDbPropSet *)pPropSets ); // Loop through all the properties in error and see if one // of the passed in properties matches. If it matches, then // transfer the in error status. for(ULONG iSet=0; iSetdwPropertyID) && (rgPropertySets[iSet].rgProperties[iProp].dwStatus == DBPROPSTATUS_OK) ) { rgPropertySets[iSet].rgProperties[iProp].dwStatus = pProp->dwStatus; } } } } //+--------------------------------------------------------------------------- // // Member: CImpersonateSessionUser::CImpersonateSessionUser, public // // Purpose: ctor // // History: 01-23-99 danleg Created // //---------------------------------------------------------------------------- CImpersonateSessionUser::CImpersonateSessionUser( HANDLE hToken ) : _fImpersonated( FALSE ), _xSessionToken( INVALID_HANDLE_VALUE ), _xPrevToken( INVALID_HANDLE_VALUE ) { if ( INVALID_HANDLE_VALUE != hToken ) { HANDLE hTempToken = DupToken( hToken ); if ( INVALID_HANDLE_VALUE != hTempToken ) _xSessionToken.Set( hTempToken ); CachePrevToken(); Impersonate(); } } //CImpersonateSessionUser //+--------------------------------------------------------------------------- // // Member: CImpersonateSessionUser::~CImpersonateSessionUser, public // // Synopsis: dtor // // History: 01-23-99 danleg Created // //---------------------------------------------------------------------------- CImpersonateSessionUser::~CImpersonateSessionUser() { TRY { Revert(); } CATCH( CException, e ) { // // Ignore failures in unwind paths -- the query will fail. If we // can't revert here the ole db client has to realize the thread // may be in a bad state after a query failure. // } END_CATCH BOOL fSuccess = TRUE; } //~CImpersonateSessionUser //+--------------------------------------------------------------------------- // // Member: CImpersonateSessionUser::Revert, public // // Synopsis: Reverts the thread to the original state // // History: 02-11-02 dlee Created from ~, so we have a form that // can fail. // //---------------------------------------------------------------------------- void CImpersonateSessionUser::Revert() { BOOL fSuccess = TRUE; if ( INVALID_HANDLE_VALUE == _xPrevToken.Get() ) { // // There is no need to revert to self here if we didn't impersonate // in the first place -- if there was no token or there was no // session object. If you revert here then IIS threads become // system. // if ( _fImpersonated ) { fSuccess = RevertToSelf(); if ( fSuccess ) _fImpersonated = FALSE; } } else { fSuccess = ImpersonateLoggedOnUser( _xPrevToken.Get() ); _xPrevToken.Free(); } if ( !fSuccess ) { DWORD dwError = GetLastError(); vqDebugOut(( DEB_ERROR, "CImpersonateSessionUser::Revert: Impersonation failed with error %d\n", dwError )); THROW( CException( HRESULT_FROM_WIN32( dwError ) ) ); } } //Revert //+--------------------------------------------------------------------------- // // Member: CImpersonateSessionUser:: DupToken, private // // Synopsis: Duplicate the session token for the current thread // // History: 01-23-99 danleg Created // //---------------------------------------------------------------------------- HANDLE CImpersonateSessionUser::DupToken( HANDLE hToken ) { SECURITY_QUALITY_OF_SERVICE qos; qos.Length = sizeof( SECURITY_QUALITY_OF_SERVICE ); qos.ImpersonationLevel = SecurityImpersonation; qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; qos.EffectiveOnly = FALSE; OBJECT_ATTRIBUTES ObjAttr; InitializeObjectAttributes( &ObjAttr, NULL, 0, NULL, NULL ); ObjAttr.SecurityQualityOfService = &qos; HANDLE hNewToken = INVALID_HANDLE_VALUE; NTSTATUS status = NtDuplicateToken( hToken, TOKEN_IMPERSONATE|TOKEN_QUERY, &ObjAttr, FALSE, TokenImpersonation, &hNewToken ); if ( !NT_SUCCESS(status) ) { vqDebugOut(( DEB_ERROR, "DupToken failed to duplicate token, %x\n", status )); THROW( CException( status ) ); } return hNewToken; } //DupToken //+--------------------------------------------------------------------------- // // Member: CImpersonateSessionUser:: CachePrevToken, private // // Synopsis: If the current thread is already impersonated, cache its // impersonation token so it can be restored later. // // History: 01-23-99 danleg Created // //---------------------------------------------------------------------------- void CImpersonateSessionUser::CachePrevToken() { DWORD dwLength; TOKEN_STATISTICS TokenInformation; HANDLE hToken = INVALID_HANDLE_VALUE; NTSTATUS status = NtOpenThreadToken( GetCurrentThread(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, TRUE, &hToken); if ( NT_SUCCESS(status) ) { SHandle xHandle( hToken ); // // If this thread is already impersonated, cache its impersonation // token and impersonate using the session (i.e. logon) token // status = NtQueryInformationToken ( hToken, TokenStatistics, (LPVOID)&TokenInformation, sizeof TokenInformation, &dwLength); if ( NT_SUCCESS(status) ) { if ( TokenInformation.TokenType == TokenImpersonation ) { HANDLE hTempToken = DupToken( hToken ); if ( INVALID_HANDLE_VALUE != hTempToken ) _xPrevToken.Set( hTempToken ); } } else // NtQueryInformation failed { vqDebugOut(( DEB_ERROR, "CImpersonateSessionUser failed to query token information, %x\n", status )); THROW( CException( status ) ); } } else // NtOpenThreadToken failed { // // If it's STATUS_NO_TOKEN then there isn't anything to capture and we // can ignore impersonation for this query. // if ( STATUS_NO_TOKEN != status ) { vqDebugOut(( DEB_ERROR, "CImpersonateSessionUser failed to open thread token, %x\n", status )); THROW( CException( status ) ); } } } //CachePrevToken //+--------------------------------------------------------------------------- // // Member: CImpersonateSessionUser::Impersonate, private // // Synopsis: Impersonate the user who created the OLE DB session. // // History: 01-23-99 danleg Created // //---------------------------------------------------------------------------- void CImpersonateSessionUser::Impersonate() { if ( INVALID_HANDLE_VALUE == _xSessionToken.Get() ) return; BOOL fSuccess = ImpersonateLoggedOnUser( _xSessionToken.Get() ); if ( fSuccess ) _fImpersonated = TRUE; else { DWORD dwError = GetLastError(); vqDebugOut(( DEB_ERROR, "CImpersonateSessionUser failed to impersonate, %d\n", dwError )); THROW( CException( HRESULT_FROM_WIN32( dwError ) ) ); } } //Impersonate