diff options
Diffstat (limited to 'private/ole32/dcomss/objex/manager.cxx')
-rw-r--r-- | private/ole32/dcomss/objex/manager.cxx | 1877 |
1 files changed, 1877 insertions, 0 deletions
diff --git a/private/ole32/dcomss/objex/manager.cxx b/private/ole32/dcomss/objex/manager.cxx new file mode 100644 index 000000000..0edefdf31 --- /dev/null +++ b/private/ole32/dcomss/objex/manager.cxx @@ -0,0 +1,1877 @@ +/*++ + +Copyright (c) 1995-1996 Microsoft Corporation + +Module Name: + + Manager.cxx + +Abstract: + + Stub/OR interface + +Author: + + Mario Goertzel [mariogo] Feb-02-1995 + +Revision Hist: + + MarioGo 02-10-95 Bits 'n pieces + MarioGo 01-31-96 New local and remote interfaces + +--*/ + +#include <or.hxx> + +// +// Helper routines +// + +void CheckRemoteSecurity( + IN handle_t hClient, + IN CToken *pToken + ) +/*++ + +Routine Description: + + Checks that a remote call is being made by the correct client. + +Arguments: + + hClient - RPC binding handle (SCALL/SCONN) of the call in progress. + NULL indicates that the call is being made internally and is ok. + + pToken - Token to check access against. If NULL, access is okay. + +Return Value: + + Raises OR_NOACCESS on failure. + +--*/ + +{ + if (hClient && pToken) + { + // BUGBUG + ASSERT(0); + } + + return; +} + +void +CheckLocalSecurity( + IN handle_t hClient, + IN CProcess *pProcess + ) +/*++ + +Routine Description: + + Checks that a client is correctly calling one of the local + (lclor.idl) methods. + +Arguments: + + hClient - Rpc binding handle (SCALL) of the call in progress. If NULL, + then the call is being made internally and is okay. + + pProcess - Context handle passed in by the client. Must not be zero. + +Return Value: + + Raises OR_NOACCESS if not okay. + +--*/ + +{ + UINT type; + + if ( (0 != hClient) + && ( (I_RpcBindingInqTransportType(hClient, &type) != RPC_S_OK) + || (type != TRANSPORT_TYPE_LPC) + || (0 == pProcess) ) ) + { + RpcRaiseException(OR_NOACCESS); + } + + // pProcess is not needed here. On LRPC the RPC runtime + // prevents a different local clients from using a context handle + // of another client. + + return; +} + +// +// Manager (server-side) calls to the local OR interface. lclor.idl +// + +error_status_t +_Connect( + IN handle_t hClient, + OUT PHPROCESS *phProcess, + OUT DWORD *pTimeoutInSeconds, + OUT DUALSTRINGARRAY **ppdsaOrBindings, + OUT MID *pLocalMid, + IN LONG cIdsToReserve, + OUT ID *pidReservedBase, + OUT DWORD *pfConnectFlags, + OUT WCHAR **pLegacySecurity, + OUT DWORD *pAuthnLevel, + OUT DWORD *pImpLevel, + OUT DWORD *pcServerSvc, + OUT USHORT **aServerSvc, + OUT DWORD *pcClientSvc, + OUT USHORT **aClientSvc, + OUT DWORD *pThreadID, + OUT DWORD *pScmProcessID, + OUT DWORD *pSignature + ) +{ + ORSTATUS status; + CProcess *pProcess; + CToken *pToken; + + OrDbgDetailPrint(("OR: Client connected\n")); + + *pfConnectFlags = 0; + + // Fill in security parameters. + if (s_fEnableDCOM == FALSE) *pfConnectFlags |= CONNECT_DISABLEDCOM; + if (s_fMutualAuth) *pfConnectFlags |= CONNECT_MUTUALAUTH; + if (s_fSecureRefs) *pfConnectFlags |= CONNECT_SECUREREF; + *pLegacySecurity = s_pLegacySecurity; + *pAuthnLevel = s_lAuthnLevel; + *pImpLevel = s_lImpLevel; + *pcServerSvc = s_cServerSvc; + *aServerSvc = s_aServerSvc; + *pcClientSvc = s_cClientSvc; + *aClientSvc = s_aClientSvc; + + *pSignature = 0; + + status = StartListeningIfNecessary(); + + if (status != OR_OK) + { + return(status); + } + + ASSERT(pdsaMyBindings); + + status = RegisterAuthInfoIfNecessary(); + + if (status != OR_OK) + { + return(status); + } + + // Do client specific stuff + + gpClientLock->LockShared(); + + status = LookupOrCreateToken(hClient, TRUE, &pToken); // Will check security + + if (OR_OK == status) + { + pProcess = 0; + + // Must be a local client. + + *ppdsaOrBindings = (DUALSTRINGARRAY *)MIDL_user_allocate( + pdsaMyBindings->wNumEntries * sizeof(WCHAR) + + sizeof(DUALSTRINGARRAY) ); + + if (*ppdsaOrBindings) + { + dsaCopy(*ppdsaOrBindings, pdsaMyBindings); + } + else + { + status = OR_NOMEM; + } + + if (status == OR_OK) + { + pProcess = new CProcess(pToken, status); + if (pProcess && status == OR_OK) + { + *phProcess = (void *)pProcess; + } + else + { + ReleaseProcess(pProcess); + // Will cause the process to rundown and release the token. + pToken = 0; + status = OR_NOMEM; + } + } + else + { + status = OR_NOMEM; + } + + if (status != OR_OK) + { + gpClientLock->ConvertToExclusive(); + MIDL_user_free(*ppdsaOrBindings); + *ppdsaOrBindings = 0; + *phProcess = 0; + if ( pToken ) + pToken->Release(); + *pSignature = 0; + gpClientLock->UnlockExclusive(); + return(OR_NOMEM); + } + + *pSignature = (ULONG) pProcess; + } + + *pTimeoutInSeconds = BaseTimeoutInterval; + *pLocalMid = gLocalMid; + + ASSERT( (*phProcess == 0 && *ppdsaOrBindings == 0) || status == OR_OK); + + _AllocateReservedIds(0, + cIdsToReserve, + pidReservedBase); + + *pScmProcessID = GetCurrentProcessId(); + *pThreadID = InterlockedExchangeAdd((long *)&gNextThreadID,1); + + gpClientLock->UnlockShared(); + + return(status); +} + + +error_status_t +_AllocateReservedIds( + IN handle_t hClient, + IN LONG cIdsToReserve, + OUT ID *pidReservedBase + ) +/*++ + +Routine Description: + + // Called by local clients to reserve a range of IDs which will + // not conflict with any other local IDs. + +Arguments: + + hClient - 0 or the connection of the client. + + cIdsToReserve - Number of IDs to reserve. + + pidReservedBase - Starting value of the reserved IDs. The + lower DWORD of this can be increatmented to generate + cIdsToReserve unique IDs. + +Return Value: + + OR_OK + +--*/ +{ + UINT type; + + if (hClient) + { + if ( (I_RpcBindingInqTransportType( hClient, &type) != RPC_S_OK) + || (type != TRANSPORT_TYPE_LPC) ) + { + RpcRaiseException(OR_NOACCESS); + } + } + + if (cIdsToReserve > 10 || cIdsToReserve < 0) + { + cIdsToReserve = 10; + } + + *pidReservedBase = AllocateId(cIdsToReserve); + return(OR_OK); +} + + +error_status_t +_ClientResolveOXID( + IN handle_t hClient, + IN PHPROCESS phProcess, + IN OXID *poxidServer, + IN DUALSTRINGARRAY *pdsaServerBindings, + IN LONG fApartment, + OUT OXID_INFO *poxidInfo, + OUT MID *pDestinationMid + ) +/*++ + +Routine Description: + + Discovers the OXID_INFO for an oxid. Will find local + OXIDs without any help. It needs OR bindings in order + to discover remote OXIDs. + +Arguments: + + phProcess - The context handle of the process. + + poxidServer - The OXID (a uuid) to resolve. + + pdsaServerBindings - Compressed string bindings to + the OR on the server's machine. + + fApartment - non-zero if the client is aparment model. + REVIEW: What to do with mixed model clients? + What to do when auto registering an OID? + + + poxidInfo - If successful this will contain information about the oxid and + an expanded string binding to the server oxid's process. + +Return Value: + + OR_NOMEM - Common. + + OR_BADOXID - Unable to resolve it. + + OR_OK - Success. + +--*/ +{ + // REVIEW: no security check here. OXID info + // is not private and you can allocate memory in + // your process, too. If we needed to store some + // info in the client process then a security + // is needed + + return ResolveClientOXID( hClient, + phProcess, + poxidServer, + pdsaServerBindings, + fApartment, + 0, + poxidInfo, + pDestinationMid ); +} + +error_status_t +ResolveClientOXID( + handle_t hClient, + PHPROCESS phProcess, + OXID *poxidServer, + DUALSTRINGARRAY *pdsaServerBindings, + LONG fApartment, + USHORT wProtseqId, + OXID_INFO *poxidInfo, + MID *pDestinationMid + ) +/*++ + +Routine Description: + + Discovers the OXID_INFO for an oxid. Will find local + OXIDs without any help. It needs OR bindings in order + to discover remote OXIDs. + +Arguments: + + phProcess - The context handle of the process. + Since this is called from SCM directly this function + CAN BE called on the same process by more then one + thread at a time. + + poxidServer - The OXID (a uuid) to resolve. + + pdsaServerBindings - Compressed string bindings to + the OR on the server's machine. + + fApartment - non-zero if the client is aparment model. + REVIEW: What to do with mixed model clients? + What to do when auto registering an OID? + + + poxidInfo - If successful this will contain information about the oxid and + an expanded string binding to the server oxid's process. + +Return Value: + + OR_NOMEM - Common. + + OR_BADOXID - Unable to resolve it. + + OR_OK - Success. + +--*/ +{ + CProcess *pProcess; + CClientOxid *pOxid; + CServerOxid *pServerOxid; + CMid *pMid; + ORSTATUS status = OR_OK; + BOOL fReference; + BOOL fServerApartment = FALSE; + BOOL fRetry = TRUE; + BOOL fReset = FALSE; + + pProcess = ReferenceProcess(phProcess); + ASSERT(pProcess); + + if (! dsaValid(pdsaServerBindings)) + { + return(OR_BADPARAM); + } + + // Attempt to lookup MID and OXID + + gpClientLock->LockExclusive(); + + CMidKey midkey(pdsaServerBindings); + + pMid = (CMid *)gpMidTable->Lookup(midkey); + + if (0 == pMid) + { + fReference = TRUE; + pMid = new(pdsaServerBindings->wNumEntries * sizeof(WCHAR)) CMid(pdsaServerBindings, FALSE); + if (pMid) + { + gpMidTable->Add(pMid); + } + + if (0 == pMid) + { + status = OR_NOMEM; + } + } + else + { + fReference = FALSE; + } + + if (status == OR_OK) + { + CId2Key oxidkey(*poxidServer, pMid->Id()); + + pOxid = (CClientOxid *)gpClientOxidTable->Lookup(oxidkey); + + if (0 == pOxid) + { + if (!fReference) + { + pMid->Reference(); + fReference = TRUE; + } + + // Need to allocate the OXID. First step is too resolve it + // either locally or remotly. + + gpClientLock->UnlockExclusive(); + + if (pMid->IsLocal()) + { + // Local OXID, lookup directly + + gpServerLock->LockShared(); + + CIdKey key(*poxidServer); + pServerOxid = (CServerOxid *)gpServerOxidTable->Lookup(key); + + if (pServerOxid) + { + status = pServerOxid->GetInfo(poxidInfo, TRUE); + fServerApartment = pServerOxid->Apartment(); + } + else + { + status = OR_BADOXID; + } + ASSERT(status != OR_OK || dsaValid(poxidInfo->psa)); + gpServerLock->UnlockShared(); + + } + else if (0 == poxidInfo->psa) + { + // Remote OXID, call ResolveOxid + + USHORT cProtseqs; + USHORT *aProtseqs; + USHORT tmpProtseq; + handle_t hRemoteOr; + USHORT iBinding; + + // BUGBUG: Only sending the protseq we're calling on, + // this should be fixed to send in the whole list if + // it includes the protseq we're calling on. + // This is a workaround for a Cairo setup problem. + + cProtseqs = 1; + aProtseqs = &tmpProtseq; + poxidInfo->psa = 0; + + status = OR_NOMEM; + + while (hRemoteOr = pMid->GetBinding(iBinding)) + { + + tmpProtseq = pMid->ProtseqOfServer(iBinding); + + if (pMid->IsSecure()) + { + status = RpcImpersonateClient(hClient); + + if (status == RPC_S_OK) + { + RPC_SECURITY_QOS qos; + qos.Version = RPC_C_SECURITY_QOS_VERSION; + qos.Capabilities = RPC_C_QOS_CAPABILITIES_DEFAULT; + qos.IdentityTracking = RPC_C_QOS_IDENTITY_DYNAMIC; + qos.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE; + + status = RpcBindingSetAuthInfoEx(hRemoteOr, + 0, + RPC_C_AUTHN_LEVEL_CONNECT, + RPC_C_AUTHN_WINNT, + 0, + 0, + &qos); + } + + if (status != RPC_S_OK) + { + OrDbgPrint(("OR: Unable to setup secure connection %d\n", + status)); + } + + fReset = FALSE; + } + else + { + fReset = TRUE; + } + + fRetry = TRUE; + + for(;;) + { + status = ResolveOxid(hRemoteOr, + poxidServer, + cProtseqs, + aProtseqs, + &poxidInfo->psa, + &poxidInfo->ipidRemUnknown, + &poxidInfo->dwAuthnHint + ); + + if (status == OR_OK) + { + if (dsaValid(poxidInfo->psa)) + { + wProtseqId = tmpProtseq; + } + else + { + OrDbgPrint(("OR: Server %s returned a bogus string array: %p\n", + pMid->PrintableName(), poxidInfo->psa)); + ASSERT(0); + if (poxidInfo->psa) + { + MIDL_user_free(poxidInfo->psa); + poxidInfo->psa = 0; + } + status = OR_BADOXID; + } + break; + } + + if ( fRetry + && (status == RPC_S_UNKNOWN_IF)) + { + status = RpcBindingReset(hRemoteOr); + if (status != RPC_S_OK) + { + OrDbgPrint(("OR: RpcBindingReset failed %d\n", status)); + } + fRetry = FALSE; + continue; + } + + if ( !fReset + && ( status == RPC_S_ACCESS_DENIED + || status == RPC_S_UNKNOWN_AUTHN_SERVICE + || status == RPC_S_UNKNOWN_AUTHZ_SERVICE + || status == RPC_S_SEC_PKG_ERROR ) ) + { + status = RpcBindingSetAuthInfo(hRemoteOr, + 0, + RPC_C_AUTHN_LEVEL_NONE, + RPC_C_AUTHN_NONE, + 0, + 0); + if (status != RPC_S_OK) + { + OrDbgPrint(("OR: RpcBindingSetAuthInfo to NONE failed!! %d\n", + status)); + } + + // Even if the reset failed, don't try it again. + fReset = TRUE; + continue; + } + + OrDbgPrint(("OR: Remote resolve OXID failed %d\n", status)); + break; + } + + if ( status == OR_OK + || status == OR_BADOXID + || status == OR_NOMEM ) + { + break; + } + + RpcBindingFree(&hRemoteOr); + pMid->BindingFailed(iBinding); + } + + if (hRemoteOr) + { + RPC_STATUS tstatus = RpcBindingFree(&hRemoteOr); + ASSERT(tstatus == RPC_S_OK); + } + } + // Else it's a remote MID, but we were given the OXID info + // and protseq id from the SCM after a remote activation. + + gpClientLock->LockExclusive(); + + ASSERT(fReference); + + if ( OR_OK == status + && FALSE == fRetry) + { + OrDbgDetailPrint(("OR: Machine %S, retry ok, assuming dynamic\n", + pMid->PrintableName() )); + pMid->UseDynamicEndpoints(); + } + + if ( OR_OK == status + && TRUE == fReset ) + { + OrDbgDetailPrint(("OR: Machine %S, unsecure retry ok, assuming no sec\n", + pMid->PrintableName() )); + pMid->SecurityFailed(); + } + + if (status == OR_OK) + { + // Lookup the oxid again to make sure it hasn't been added in the meantime. + + pOxid = (CClientOxid *)gpClientOxidTable->Lookup(oxidkey); + + if (0 == pOxid) + { + ASSERT(dsaValid(poxidInfo->psa)); + pOxid = new CClientOxid(*poxidServer, + pMid, + wProtseqId, + fServerApartment); + + if (0 != pOxid) + { + status = pOxid->UpdateInfo(poxidInfo); + + if (OR_OK == status) + { + gpClientOxidTable->Add(pOxid); + + // Will be references by the process and by other + // OIDs are it is used. Will be refernced again + // before we leave the lock if successful. + pOxid->Release(); + } + else + { + // Will release mid, will also remove it (unnecessarily) + // from the table. + delete pOxid; + } + } + else + { + status = OR_NOMEM; + pMid->Release(); // May actually go away.. + } + } + else + { + // Release our now extra reference on the MID + DWORD t = pMid->Release(); + ASSERT(t > 0); + } + + MIDL_user_free(poxidInfo->psa); + poxidInfo->psa = 0; + } + else + { + // Resolve failed, get rid of our extra reference. + pMid->Release(); + } + } + else + { + // Found the OXID, must also have found the MID + ASSERT(fReference == FALSE); + + if ( poxidInfo->psa ) + { + MIDL_user_free(poxidInfo->psa); + poxidInfo->psa = 0; + } + } + } + + ASSERT( (status != OR_OK) || (pOxid && pMid) ); + + if ( status == OR_OK + && pOxid->IsLocal() == FALSE) + { + status = pProcess->AddRemoteOxid(pOxid); + } + + gpClientLock->UnlockExclusive(); + + if (status == OR_OK) + { + *pDestinationMid = pMid->Id(); + + status = pOxid->GetInfo(fApartment, poxidInfo); + + if ( status != OR_OK + && pOxid->IsLocal() == FALSE) + { + gpClientLock->LockExclusive(); + pProcess->RemoveRemoteOxid(pOxid); + gpClientLock->UnlockExclusive(); + } + } + + return(status); +} + + +error_status_t +_BulkUpdateOIDs( + IN handle_t hClient, + IN PHPROCESS phProcess, + IN ULONG cOidsToBeAdded, + IN OXID_OID_PAIR aOidsToBeAdded[], + OUT LONG aStatusOfAdds[], + IN ULONG cOidsToBeRemoved, + IN OID_MID_PAIR aOidsToBeRemoved[], + IN ULONG cServerOidsToFree, + IN OID aServerOidsToFree[], + IN ULONG cClientOxidsToFree, + IN OXID_REF aClientOxidsToFree[] + ) +/*++ + +Routine Description: + + Updates the set of remote OIDs in use by a process. + +Note: + + An OID maybe removed before it is added. This means that + the client was using it and is no longer using it. In + this case a single delete from set ping is made to keep + the object alive. This is only needed if the client + has remarshalled a pointer to the object. + +Arguments: + + phProcess - Context handle for the process. + + cOidsToBeAdded - Count of aOidsToBeAdded and aStatusOfAdds + + aOidsToBeAdded - OID-OXID-MID pairs representing the + oids and the owning oxids to add. + + aStatusOfAdds - Some adds may succeed when other fail. + OR_NOMEM - couldn't allocate storage + OR_BADOXID - OXID doesn't exist. + OR_OK (0) - added to set + + cOidsToBeRemoved - Count of entries in aOidsToBeRemoved. + + aOidsToBeRemoved - OID-MID pairs to be removed. + + cServerOidsToFree - Count of entries in aServerOidsToFree + + aServerOidsToFree - OIDs allocated by the client process + and no longer needed. + + cClientOxidsToFree - COunt of enties in aClientOxidsToFree + + aClientOxidsToFree - OXIDs owned by a process (due to a direct + or indirect call to ClientResolveOxid) which are no longer + in use by the client. + +Return Value: + + OR_OK - All updates completed ok. + + OR_PARTIAL_UPDATE - At least one entry in aStatusOfAdds is not OR_OK + +--*/ +{ + CProcess *pProcess; + CClientOxid *pOxid; + CClientOid *pOid; + CClientSet *pSet; + CMid *pMid; + CToken *pToken; + BOOL fPartial = FALSE; + BOOL fNewSet = FALSE; + INT i; + + pProcess = ReferenceProcess(phProcess); + ASSERT(pProcess); + + CheckLocalSecurity(hClient, pProcess); + + if (cOidsToBeAdded || cOidsToBeRemoved || cClientOxidsToFree) + { + gpClientLock->LockExclusive(); + } + + // ///////////////////////////////////////////////////////////////// + // Process Adds. + + for (i = 0; i < cOidsToBeAdded; i++) + { + // Lookup up the oxid owning this new oid. + + CId2Key oxidkey(aOidsToBeAdded[i].oxid, aOidsToBeAdded[i].mid); + + pOxid = (CClientOxid *)gpClientOxidTable->Lookup(oxidkey); + + if (0 == pOxid) + { + OXID_INFO infoT; + ORSTATUS status; + MID mid; + + gpClientLock->UnlockExclusive(); + + infoT.psa = 0; + status = _ClientResolveOXID(hClient, + phProcess, + &aOidsToBeAdded[i].oxid, + pdsaMyBindings, + TRUE, + &infoT, + &mid); + + gpClientLock->LockExclusive(); + + if (status == OR_OK) + { + ASSERT(infoT.psa); + ASSERT(mid == gLocalMid); + MIDL_user_free(infoT.psa); + pOxid = (CClientOxid *)gpClientOxidTable->Lookup(oxidkey); + if (pOxid == 0) + { + OrDbgDetailPrint(("OR: Auto resolving oxid %p failed, wrong machine\n", + &oxidkey)); + status = OR_BADOXID; + } + } + + if (status != OR_OK) + { + aStatusOfAdds[i] = OR_BADOXID; + fPartial = TRUE; + continue; + } + } + + + // Find or create the set. + + CId2Key setkey(aOidsToBeAdded[i].mid, (ID)pProcess->GetToken()); + + pSet = (CClientSet *)gpClientSetTable->Lookup(setkey); + + if (pSet == 0) + { + pSet = new CClientSet(pOxid->GetMid(), pProcess->GetToken()); + + if (pSet == 0) + { + aStatusOfAdds[i] = OR_NOMEM; + fPartial = TRUE; + continue; + } + else + { + gpClientSetTable->Add(pSet); + pSet->Insert(); + fNewSet = TRUE; + } + } + + // Find or create the oid. If we create it, add a reference + // to the oxid for the new oid. + + CId3Key oidkey(aOidsToBeAdded[i].oid, aOidsToBeAdded[i].mid, pProcess->GetToken()); + + pOid = (CClientOid *)gpClientOidTable->Lookup(oidkey); + + if (0 == pOid) + { + pOid = new CClientOid(aOidsToBeAdded[i].oid, + aOidsToBeAdded[i].mid, + pProcess->GetToken(), + pOxid, + pSet + ); + if (fNewSet) + { + // pOid either owns a refernce now or we need to + // cleanup the set anyway. + pSet->Release(); + fNewSet = FALSE; + } + + if (pOid) + { + + aStatusOfAdds[i] = pSet->RegisterObject(pOid); + + if (aStatusOfAdds[i] == OR_OK) + { + gpClientOidTable->Add(pOid); + } + else + { + pOid->Release(); + pOid = 0; + } + } + else + { + aStatusOfAdds[i] = OR_NOMEM; + fPartial = TRUE; + continue; + } + + } + else + { + ASSERT(fNewSet == FALSE); + pOid->ClientReference(); + } + + // If this fails it will release the oid. + aStatusOfAdds[i] = pProcess->AddOid(pOid); + if (aStatusOfAdds[i] != OR_OK) + { + fPartial = TRUE; + } + } // for oids to add + + // ///////////////////////////////////////////////////////////////// + // Process deletes + + for (i = 0; i < cOidsToBeRemoved; i++) + { + CId3Key oidkey(aOidsToBeRemoved[i].oid, + aOidsToBeRemoved[i].mid, + pProcess->GetToken()); + + pOid = (CClientOid *)gpClientOidTable->Lookup(oidkey); + + if (pOid) + { + CClientOid *pT = pProcess->RemoveOid(pOid); + + if (pT == 0) + { + OrDbgPrint(("OR: Client process %p tried to remove oid %p which" + "it didn't own\n", pProcess, &aOidsToBeRemoved[i])); + } + else + ASSERT(pT == pOid); + } + else + OrDbgDetailPrint(("OR: Client %p removed an OID that doesn't exist\n", pProcess)); + + } // for oids to delete + + // //////////////////////////////////////////////////////////// + // Process client oxid deletes + + if (cClientOxidsToFree) + { + CClientOxid *pOxid; + for (i = 0; i < cClientOxidsToFree; i++) + { + CId2Key oxidKey(aClientOxidsToFree[i].oxid, + aClientOxidsToFree[i].mid); + + pOxid = (CClientOxid *)gpClientOxidTable->Lookup(oxidKey); + if (pOxid) + { + for (int j = 0; j < aClientOxidsToFree[i].refs; j++) + { + pProcess->RemoveRemoteOxid(pOxid); + } + } + else + { + OrDbgPrint(("OR: Process %p freed oxid %p which didn't exist\n", + pProcess, &aClientOxidsToFree[i])); + ASSERT(0); + } + } + } + + if (cOidsToBeAdded || cOidsToBeRemoved || cClientOxidsToFree) + { + gpClientLock->UnlockExclusive(); + } + + // ///////////////////////////////////////////////////////////////// + // Process server deletes + + if (cServerOidsToFree) + { + CServerOid *pOid; + CServerOxid *pOxid; + + gpServerLock->LockExclusive(); + + for (i = 0; i < cServerOidsToFree; i++) + { + CIdKey oidkey(aServerOidsToFree[i]); + + pOid = (CServerOid *)gpServerOidTable->Lookup(oidkey); + if (pOid && pOid->IsRunningDown() == FALSE) + { + pOxid = pOid->GetOxid(); + ASSERT(pOxid); + if (pProcess->IsOwner(pOxid)) + { + if (pOid->References() == 0) + { + pOid->Remove(); + pOid->SetRundown(); + delete pOid; + } + else + { + pOid->Free(); + } + } + else + { + OrDbgPrint(("OR: Process %p tried to free OID %p it didn't own\n", + pProcess, pOid)); + } + } + else + { + OrDbgPrint(("OR: Process %p freed OID %p that didn't exist\n", + pProcess, &aServerOidsToFree[i])); + } + } + + gpServerLock->UnlockExclusive(); + } + + // Done + + if (fPartial) + { + return(OR_PARTIAL_UPDATE); + } + + return(OR_OK); +} + +error_status_t +_ServerAllocateOXIDAndOIDs( + IN handle_t hClient, + IN PHPROCESS phProcess, + OUT OXID *poxidServer, + IN LONG fApartment, + IN ULONG cOids, + OUT OID aOid[], + OUT PULONG pOidsAllocated, + IN OXID_INFO *poxidInfo, // No bindings + IN DUALSTRINGARRAY *pdsaStringBindings, // Expanded + IN DUALSTRINGARRAY *pdsaSecurityBindings // Compressed + ) +/*++ + +Routine Description: + + Allocates an OXID and 0 or more OIDs from the OR. + +Arguments: + + phProcess - The context handle of the process containing the OXID. + + poxidServer - The OXID to register. May only register once. + + cOids - Count of apOids + + apOid - The OIDs to register within the OXID. + + pcOidsAllocated - The number of OIDs actually allocated. Usually the + same as cOids unless a resource failure occures. Maybe 0. + + poxidInfo - The OXID_INFO structure for the OXID without bindings. + + pdsaStringBindings - Expanded string binding of the server. + + pdsaSecurityBindings - The compressed security bindings of the server. + + pOidsAllocated - The number of OIDs actually allocated. >= 0 and <= cOids. + +Return Value: + + OR_OK - success. Returned even if some OID allocations fail. See the + pOidsAllocated parameter. + + OR_NOMEM - Allocation of OXID failed. + + OR_ACCESS_DENIDED - Raised if non-local client + + OR_BADPARAM - if string arrays are incorrect. + +--*/ +{ + ORSTATUS status = OR_OK; + CServerOxid *pNewOxid; + CProcess *pProcess = ReferenceProcess(phProcess); + ASSERT(pProcess); + + CheckLocalSecurity(hClient, pProcess); + + gpServerLock->LockExclusive(); + + // Save the string bindings back to the process + + if (!dsaValid(pdsaStringBindings) ) + { + status = OR_BADPARAM; + } + + if (!dsaValid(pdsaSecurityBindings)) + { + status = OR_BADPARAM; + } + + if (status == OR_OK) + { + status = pProcess->ProcessBindings(pdsaStringBindings, + pdsaSecurityBindings); + } + + VALIDATE((status, OR_NOMEM, OR_BADPARAM, 0)); + + if (status != OR_OK) + { + gpServerLock->UnlockExclusive(); + return(status); + } + + pNewOxid = new CServerOxid(pProcess, + fApartment, + poxidInfo + ); + + if (0 == pNewOxid) + { + gpServerLock->UnlockExclusive(); + return(OR_NOMEM); + } + + // Add to process and lookup table. + + status = pProcess->AddOxid(pNewOxid); + + VALIDATE((status, OR_NOMEM, 0)); + + pNewOxid->Release(); // process has a reference now or failed + + gpServerLock->UnlockExclusive(); + + if (status == OR_OK) + { + *poxidServer = pNewOxid->Id(); + + status = _ServerAllocateOIDs(0, + phProcess, + poxidServer, + cOids, + aOid, + pOidsAllocated); + } + + return(status); +} + +error_status_t _ServerAllocateOIDs( + IN handle_t hClient, + IN PHPROCESS phProcess, + IN OXID *poxidServer, + IN ULONG cOids, + OUT OID aOids[], + OUT PULONG pOidsAllocated + ) +/*++ + +Routine Description: + + Registers additional OIDs on behalf of an existing OXID. + +Arguments: + + phProcess - The context handle of the process containing the OXID and OIDs. + + poxidServer - The OXID associated with the OIDs. + + cOids - Count of apOids + + aOids - The OIDs to register within the OXID. + + pOidsAllocate - Contains the number of OIDs actually allocated + when this function returns success. + +Return Value: + + OR_OK (0) - Success. + + OR_PARTIAL_UPDATE - No all elements in aStatus are 0. + + OR_NOMEM - OXID or one or more OIDs + +--*/ +{ + ORSTATUS status = OR_OK; + CServerOxid *pOxid; + CServerOid *pOid; + BOOL fPartial = FALSE; + CProcess *pProcess = ReferenceProcess(phProcess); + ASSERT(pProcess); + + CheckLocalSecurity(hClient, pProcess); + + gpServerLock->LockExclusive(); + + CIdKey oxidkey(*poxidServer); + + pOxid = (CServerOxid *)gpServerOxidTable->Lookup(oxidkey); + + if (0 == pOxid) + { + gpServerLock->UnlockExclusive(); + status = OR_BADOXID; + return(status); + } + + *pOidsAllocated = 0; + + for (INT i = 0; i < cOids; i++) + { + pOid = new CServerOid(pOxid); + + if (0 != pOid) + { + (*pOidsAllocated)++; + aOids[i] = pOid->Id(); + gpServerOidTable->Add(pOid); + + // The server doesn't want to keep the OID alive. + // This will cause the OID to rundown in six minutes + // unless a set references it in the meantime... + + pOid->Release(); + + } + else + { + break; + } + } + + gpServerLock->UnlockExclusive(); + + ASSERT(status == OR_OK); + + return(status); +} + +error_status_t +_ServerFreeOXIDAndOIDs( + IN handle_t hClient, + IN PHPROCESS phProcess, + IN OXID oxidServer, + IN ULONG cOids, + IN OID aOids[]) +{ + CServerOxid *pOxid; + CServerOid *pOid; + CProcess *pProcess = ReferenceProcess(phProcess); + ORSTATUS status; + UINT i; + + ASSERT(pProcess); + + CheckLocalSecurity(hClient, pProcess); + + gpServerLock->LockExclusive(); + + CIdKey oxidkey(oxidServer); + + pOxid = (CServerOxid *)gpServerOxidTable->Lookup(oxidkey); + + if (0 != pOxid) + { + if (pProcess->RemoveOxid(pOxid) == TRUE) + { + // Found the OXID and this caller owns it. + status = OR_OK; + } + else + { + // Found but not owned by this caller. + status = OR_NOACCESS; + } + } + else + { + // Oxid not found. + status = OR_BADOXID; + } + + // Note pOxid maybe invalid once the last OID is removed. + + if (status == OR_OK) + { + for (i = 0; i < cOids; i++) + { + CIdKey key(aOids[i]); // PERF REVIEW + + pOid = (CServerOid *)gpServerOidTable->Lookup(key); + + if ( (0 != pOid) + && (pOid->IsRunningDown() == FALSE) + && (pOid->GetOxid() == pOxid) ) + { + if (pOid->References() == 0) + { + // Unreferenced by any sets; run it down now.. + pOid->Remove(); + pOid->SetRundown(); + delete pOid; + } + // else - marking it as Free() not need as Oxid is + // now marked as not running. + } + else + { + ASSERT(pOid == 0 || pOxid == pOid->GetOxid()); + } + } + } + + gpServerLock->UnlockExclusive(); + + return(status); +} + +// +// Manager (server-side) calls to the remote OR interface. objex.idl +// + +error_status_t +_ResolveOxid( + IN handle_t hRpc, + IN OXID *poxid, + IN USHORT cRequestedProtseqs, + IN USHORT aRequestedProtseqs[], + OUT DUALSTRINGARRAY **ppdsaOxidBindings, + OUT IPID *pipidRemUnknown, + OUT DWORD *pAuthnHint + ) +{ + ORSTATUS status; + BOOL fDidLazy; + CServerOxid *pServerOxid; + OXID_INFO oxidInfo; + + oxidInfo.psa = 0; + + // No security check required (possible?). OXID info is not private. + +#if DBG + UINT fLocal; + status = I_RpcBindingIsClientLocal(hRpc, &fLocal); + + if (status != OR_OK) + { + fLocal = FALSE; + } + ASSERT(fLocal == FALSE); // Shouldn't be called locally... +#endif + + fDidLazy = FALSE; + + gpServerLock->LockShared(); + + for(;;) + { + CIdKey key(*poxid); + + pServerOxid = (CServerOxid *)gpServerOxidTable->Lookup(key); + + if (!pServerOxid) + { + status = OR_BADOXID; + break; + } + + status = pServerOxid->GetRemoteInfo(&oxidInfo, + cRequestedProtseqs, + aRequestedProtseqs); + + if ( status == OR_I_NOPROTSEQ + && FALSE == fDidLazy ) + { + // Ask the server to start listening, but only try this once. + + fDidLazy = TRUE; + + status = + pServerOxid->LazyUseProtseq(cRequestedProtseqs, + aRequestedProtseqs + ); + + ASSERT(gpServerLock->HeldExclusive()); // Changed during UseProtseq! + + if (status == OR_OK) + { + continue; + } + } + else if (status == OR_I_NOPROTSEQ) + { + // We didn't manage to use a matching protseq. + // Since the client managed to call us why didn't this work? + OrDbgPrint(("OR: Failed to use a matching protseq: %p %p\n", + pServerOxid, aRequestedProtseqs)); + ASSERT(0); + status = OR_NOSERVER; + } + break; + } + + gpServerLock->Unlock(); + + if (status == OR_OK) + { + *pipidRemUnknown = oxidInfo.ipidRemUnknown; + *ppdsaOxidBindings = oxidInfo.psa; + *pAuthnHint = oxidInfo.dwAuthnHint; + } + + return(status); +} + +error_status_t +_SimplePing( + IN handle_t hRpc, + IN SETID *pSetId + ) +{ + ORSTATUS status; + CServerSet *pServerSet; + BOOL fShared = TRUE; + + if (*pSetId == 0) + { + OrDbgPrint(("Client %p simple pinged with a setid of 0\n", + hRpc, pSetId)); + return(OR_BADSET); + } + + gpServerLock->LockShared(); + + pServerSet = (CServerSet *)gpServerSetTable->Lookup(*pSetId); + + if (pServerSet) + { + fShared = pServerSet->Ping(TRUE); + // The lock maybe exclusive now. + status = OR_OK; + } + else + { + status = OR_BADSET; + } + + // See if another set in the table needs to rundown. + // PERF REVIEW - how often should I do this? 0 mod 4? + + // Similar code in worker threads. + + ID setid = gpServerSetTable->CheckForRundowns(); + + if (setid) + { + if (fShared) + { + gpServerLock->ConvertToExclusive(); + fShared = FALSE; + } + + gpServerSetTable->RundownSetIfNeeded(setid); + } + + gpServerLock->Unlock(); + + return(status); +} + +error_status_t +_ComplexPing( + IN handle_t hRpc, + IN SETID *pSetId, + IN USHORT SequenceNum, + IN USHORT cAddToSet, + IN USHORT cDelFromSet, + IN OID AddToSet[], + IN OID DelFromSet[], + OUT USHORT *pPingBackoffFactor + ) +/*++ + +Routine Description: + + Processes a complex (delta to set) ping for a given set. This call + will create the set if necessary. The call will only be processed + if the caller is in fact the creator of the set. + + algorithm: + + if set is not allocated + lookup security info if possible + allocate set + else + lookup set + + + if found or created a set + do a standard ping, updating time stamp and sequence number. + else return failure. + + if oids to add, add each one. + ignore unknown OIDs + if resource allocation fails, abort. + + if oids to delete, process each one. + ignore unknown OIDs + + if resource failure in adds, return OR_BADOID + else return success. + +Arguments: + + hRpc - Handle (SCONN/SCALL) of client. Used to check security. If it is + NULL the call is local and is assumed to be secure. + + REVIEW: + Since the OR _only_ uses NT system security providers it is assumed + that impersonation will work. Other security providers will not. + + We need a generic way to ask for a token and compare tokens in a + security provider independent way. + + pSetId - The setid to ping. If it is NULL a new set will be created, + otherwise, it is assumed to be a set previously allocated by a + call with a NULL setid to this server. + + SequenceNum - A sequence number shared between the client and server + to make sure old and out-of-order pings are not processed in a + non-healthy way. Note that pings are usually datagram RPC calls + which are marked as idempotent. + + cAddToSet + cDelFromSet - The count of element in AddTo/DelFromSet parameter. + + AddToSet + DelFromSet - OID mostly likly belonging to servers on this machine + to Add/Remove from the set of OIDs in use by this client. + + pPingBackoffFactor - Maybe set by servers which want to reduce the + ping load on the server. Serves only as a HINT for the client. + Clients do not to ping more offten then: + (1<<*pPingBackoffFactor)*BasePingInterval seconds. + Clients may choose to assume this parameter is always 0. + +Return Value: + + OR_OK - completed normally + + OR_BADSET - non-zero and unknown setid. + + OR_NOMEM - unable to allocate a resource. Note that + on the first ping a set maybe allocated (setid is non-zero + after call) but some OIDs failed to be allocated. + + OR_BADOID - everything went okay, but some OIDs added where + not recognized. + +--*/ + +{ + CServerSet *pServerSet; + BOOL fProcessPing; + BOOL fBad = FALSE; + PSID psid = 0; + ORSTATUS status = OR_OK; + + gpServerLock->LockExclusive(); + + // Lookup the set + + if (0 != *pSetId) + { + pServerSet = (CServerSet *)gpServerSetTable->Lookup(*pSetId); + if (0 == pServerSet) + { + status = OR_BADSET; + } + + if (status == OR_OK) + { + if (pServerSet->CheckSecurity(hRpc) != TRUE) + { + OrDbgPrint(("OR: Security check on set failed! (%d)\n", GetLastError())); + status = OR_NOACCESS; + } + } + } + else if (hRpc == 0) + { + // Local client + psid = 0; + pServerSet = gpServerSetTable->Allocate(SequenceNum, + psid, + hRpc == 0, + *pSetId); + + if (0 == pServerSet) + status = OR_NOMEM; + else + status = OR_OK; + + } + else + { + HANDLE hT; + BOOL f; + // Unallocated set, lookup security info and allocate the set. + + OrDbgDetailPrint(("OR: New client started pinging: %p\n", hRpc)); + + status = RpcImpersonateClient(hRpc); + + if (status == RPC_S_OK) + { + f = OpenThreadToken(GetCurrentThread(), + TOKEN_IMPERSONATE | TOKEN_QUERY, + TRUE, + &hT); + + if (!f) + { + status = GetLastError(); + } + else + { + status = RPC_S_OK; + } + + } + + if (status != RPC_S_OK) + { + OrDbgPrint(("OR: Unsecure client started pinging: %d %p\n", + status, hRpc)); + status = OR_OK; + } + else + { + ULONG needed = DEBUG_MIN(1, 24); + PTOKEN_USER ptu; + + do + { + ptu = (PTOKEN_USER)alloca(needed); + ASSERT(ptu); + + f = GetTokenInformation(hT, + TokenUser, + (PBYTE)ptu, + needed, + &needed); + + } while( f == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER); + + if (f) + { + ASSERT(needed > sizeof(SID)); + psid = new(needed - sizeof(SID)) SID; + if (psid) + { + f = CopySid(needed, psid, ptu->User.Sid); + ASSERT(f == TRUE); + } + else + { + status = OR_NOMEM; + } + } + else + { + OrDbgPrint(("OR: Error %d from GetTokenInformation\n", GetLastError())); + ASSERT(0); + // Why did this happen. Either return failure to client or + // continue and make the set unsecure. + status = OR_NOMEM; + } + + CloseHandle(hT); + } + + // Allocate the set + + if (status == OR_OK) + { + pServerSet = gpServerSetTable->Allocate(SequenceNum, + psid, + hRpc == 0, + *pSetId); + + if (0 == pServerSet) + { + status = OR_NOMEM; + } + } + } + + if (status != OR_OK) + { + VALIDATE((status, OR_NOMEM, OR_BADSET, OR_NOACCESS, 0)); + gpServerLock->UnlockExclusive(); + return(status); + } + + ASSERT(pServerSet); + + fProcessPing = pServerSet->CheckAndUpdateSequenceNumber(SequenceNum); + + if (fProcessPing) + { + // Do regular ping + + pServerSet->Ping(FALSE); + + *pPingBackoffFactor = 0; + + // Process Add's + for(int i = cAddToSet; i ; i--) + { + status = pServerSet->AddObject(AddToSet[i - 1]); + + if (status == OR_BADOID) + { + fBad = TRUE; + } + else if ( status != OR_OK ) + { + break; + } + } + + // Process Deletes - even some adds failed! + + for(i = cDelFromSet; i; i--) + { + // Removing can't fail, no way to cleanup. + pServerSet->RemoveObject(DelFromSet[i - 1]); + } + } + + gpServerLock->UnlockExclusive(); + + if (status == OR_OK && fBad) + { + return(OR_BADOID); + } + + return(status); +} + + +error_status_t +_ServerAlive( + RPC_BINDING_HANDLE hServer + ) +/*++ + +Routine Description: + + Pign API for the client to validate a binding. Used when the client + is unsure of the correct binding for the server. (Ie. If the server + has multiple IP addresses). + +Arguments: + + hServer - RPC call binding + +Return Value: + + OR_OK + +--*/ +{ + return(OR_OK); +} + + + +void __RPC_USER PHPROCESS_rundown(LPVOID ProcessKey) +{ + CProcess *pProcess = ReferenceProcess(ProcessKey); + + OrDbgDetailPrint(("OR: Client died\n")); + + ASSERT(pProcess); + + ReleaseProcess(pProcess); + + return; +} + + + |