Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3039 lines
90 KiB

  1. /*++
  2. Copyright (c) 1995-1996 Microsoft Corporation
  3. Module Name:
  4. Manager.cxx
  5. Abstract:
  6. Stub/OR interface
  7. Author:
  8. Mario Goertzel [mariogo] Feb-02-1995
  9. Revision Hist:
  10. MarioGo 02-10-95 Bits 'n pieces
  11. MarioGo 01-31-96 New local and remote interfaces
  12. TarunA 10-12-98 OXIDs are referenced on OID basis (Client side)
  13. TarunA 10-31-98 Added PID of the process connecting
  14. a-sergiv 07-09-99 Impersonate for the entire duration of
  15. ResolveClientOXID for ncacn_http protocol
  16. (to fix a bad COM Internet Services bug)
  17. --*/
  18. #include <or.hxx>
  19. extern "C"
  20. {
  21. #include <inc.hxx>
  22. }
  23. // These variables hold the list of channel hooks registered for the machine.
  24. // They are updated as the registry changes.
  25. LONG s_cChannelHook = 0;
  26. GUID *s_aChannelHook = NULL;
  27. HANDLE s_hChannelHook = NULL;
  28. // Definition of single instance of this class:
  29. CRpcSecurityCallbackManager* gpCRpcSecurityCallbackMgr;
  30. CPingSetQuotaManager* gpPingSetQuotaManager;
  31. //
  32. // Helper routines
  33. //
  34. extern void CheckLocalCall(IN handle_t hRpc);
  35. extern BOOL gbDynamicIPChangesEnabled;
  36. void
  37. CheckLocalSecurity(
  38. IN handle_t hClient,
  39. IN CProcess *pProcess
  40. )
  41. /*++
  42. Routine Description:
  43. Checks that a client is correctly calling one of the local
  44. (lclor.idl) methods.
  45. Arguments:
  46. hClient - Rpc binding handle (SCALL) of the call in progress. If NULL,
  47. then the call is being made internally and is okay.
  48. pProcess - Context handle passed in by the client. Must not be zero.
  49. Return Value:
  50. Raises OR_NOACCESS if not okay.
  51. --*/
  52. {
  53. UINT type;
  54. if ( (0 != hClient)
  55. && ( (I_RpcBindingInqTransportType(hClient, &type) != RPC_S_OK)
  56. || (type != TRANSPORT_TYPE_LPC)
  57. || (0 == pProcess) ) )
  58. {
  59. RpcRaiseException(OR_NOACCESS);
  60. }
  61. // pProcess is not needed here. On LRPC the RPC runtime
  62. // prevents a different local clients from using a context handle
  63. // of another client.
  64. return;
  65. }
  66. //
  67. // Update the channel hook list if it has changed in the registry.
  68. //
  69. void UpdateChannelHooks( LONG *pcChannelHook, GUID **paChannelHook )
  70. {
  71. BOOL fSuccess;
  72. BOOL fUpdate = FALSE;
  73. DWORD result;
  74. HKEY hKey;
  75. DWORD lType;
  76. DWORD lDataSize;
  77. GUID *aChannelHook;
  78. LONG cChannelHook;
  79. DWORD i;
  80. DWORD lExtent;
  81. WCHAR wExtent[39];
  82. DWORD j;
  83. // Lock
  84. gpClientLock->LockExclusive();
  85. // If the handle hasn't been created, create it.
  86. if (s_hChannelHook == NULL)
  87. {
  88. // Nothing can be done if the event can't be created.
  89. s_hChannelHook = CreateEvent(NULL, FALSE, FALSE, NULL);
  90. fUpdate = TRUE;
  91. }
  92. // If the handle has been created, see if it has been signalled.
  93. else
  94. {
  95. result = WaitForSingleObject(s_hChannelHook, 0);
  96. fUpdate = result == WAIT_OBJECT_0;
  97. }
  98. // Reread the registry if necessary.
  99. if (fUpdate)
  100. {
  101. // Register for changes.
  102. RegNotifyChangeKeyValue( s_hOle, TRUE,
  103. REG_NOTIFY_CHANGE_NAME |
  104. REG_NOTIFY_CHANGE_ATTRIBUTES |
  105. REG_NOTIFY_CHANGE_LAST_SET |
  106. REG_NOTIFY_CHANGE_SECURITY,
  107. s_hChannelHook, TRUE );
  108. // Open the channel hook key.
  109. result = RegOpenKeyEx( s_hOle, L"ChannelHook",
  110. NULL, KEY_QUERY_VALUE, &hKey );
  111. if (result == ERROR_SUCCESS)
  112. {
  113. // Find out how many values exist.
  114. cChannelHook = 0;
  115. RegQueryInfoKey( hKey, NULL, NULL, NULL, NULL, NULL, NULL,
  116. (DWORD *) &cChannelHook, NULL, NULL, NULL, NULL );
  117. // If there are no channel hooks, throw away the old data.
  118. if (cChannelHook == 0)
  119. {
  120. delete s_aChannelHook;
  121. s_aChannelHook = NULL;
  122. s_cChannelHook = 0;
  123. aChannelHook = NULL;
  124. }
  125. // Reuse the existing array.
  126. else if (cChannelHook <= s_cChannelHook)
  127. aChannelHook = s_aChannelHook;
  128. // Allocate memory for them.
  129. else
  130. aChannelHook = new GUID[cChannelHook];
  131. // If there is not enough memory, don't make changes.
  132. if (aChannelHook != NULL)
  133. {
  134. // Enumerate over the channel hook ids.
  135. j = 0;
  136. for (i = 0; i < (DWORD)cChannelHook; i++)
  137. {
  138. // Get the next key.
  139. lExtent = sizeof(wExtent) / sizeof(WCHAR);
  140. result = RegEnumValueW( hKey, i, wExtent, &lExtent, NULL,
  141. &lType, NULL, NULL );
  142. // Convert it to a GUID. Note that lExtent is set to
  143. // the length in characters not bytes despite what
  144. // the documentation says.
  145. if (result == ERROR_SUCCESS && lExtent == 38 &&
  146. lType == REG_SZ &&
  147. GUIDFromString( wExtent, &aChannelHook[j] ))
  148. j += 1;
  149. }
  150. // Save the new channel hook array.
  151. if (aChannelHook != s_aChannelHook)
  152. delete s_aChannelHook;
  153. s_aChannelHook = aChannelHook;
  154. s_cChannelHook = j;
  155. }
  156. // Close the registry key.
  157. RegCloseKey( hKey );
  158. }
  159. // There are no channel hooks. Throw away the old data.
  160. else
  161. {
  162. delete s_aChannelHook;
  163. s_aChannelHook = NULL;
  164. s_cChannelHook = 0;
  165. }
  166. }
  167. // Return the current channel hook list.
  168. *paChannelHook = (GUID *) MIDL_user_allocate( s_cChannelHook * sizeof(GUID) );
  169. if (*paChannelHook != NULL)
  170. {
  171. *pcChannelHook = s_cChannelHook;
  172. memcpy( *paChannelHook, s_aChannelHook, s_cChannelHook * sizeof(GUID) );
  173. }
  174. else
  175. *pcChannelHook = 0;
  176. // Unlock
  177. gpClientLock->UnlockExclusive();
  178. }
  179. //
  180. // Manager (server-side) calls to the local OR interface. lclor.idl
  181. //
  182. error_status_t
  183. _Connect(
  184. IN handle_t hClient,
  185. IN WCHAR *pwszWinstaDesktop,
  186. IN DWORD procID,
  187. IN DWORD dwFlags,
  188. OUT PHPROCESS *phProcess,
  189. OUT DWORD *pTimeoutInSeconds,
  190. OUT DUALSTRINGARRAY **ppdsaOrBindings,
  191. OUT MID *pLocalMid,
  192. IN LONG cIdsToReserve,
  193. OUT ID *pidReservedBase,
  194. OUT DWORD *pfConnectFlags,
  195. OUT WCHAR **pLegacySecurity,
  196. OUT DWORD *pAuthnLevel,
  197. OUT DWORD *pImpLevel,
  198. OUT DWORD *pcServerSvc,
  199. OUT USHORT **aServerSvc,
  200. OUT DWORD *pcClientSvc,
  201. OUT SECPKG **aClientSvc,
  202. OUT LONG *pcChannelHook,
  203. OUT GUID **paChannelHook,
  204. OUT DWORD *pThreadID,
  205. OUT DWORD *pScmProcessID,
  206. OUT ULONG64 *pSignature,
  207. OUT GUID *pguidRPCSSProcessIdentifier
  208. )
  209. {
  210. ORSTATUS status;
  211. CProcess *pProcess;
  212. CToken *pToken;
  213. BOOL fRet;
  214. // Ensure this is a local client calling (raises an exception
  215. // if this is not the case):
  216. CheckLocalCall(hClient);
  217. // Parameter validation
  218. if (!pwszWinstaDesktop || !phProcess || !pTimeoutInSeconds ||
  219. !ppdsaOrBindings || !pLocalMid || !pidReservedBase ||
  220. !pfConnectFlags || !pLegacySecurity || !pAuthnLevel ||
  221. !pImpLevel || !pcServerSvc || !aServerSvc || !pcClientSvc ||
  222. !aClientSvc ||!pcChannelHook || !paChannelHook || !pThreadID ||
  223. !pScmProcessID || !pSignature)
  224. {
  225. return OR_BADPARAM;
  226. }
  227. KdPrintEx((DPFLTR_DCOMSS_ID,
  228. DPFLTR_INFO_LEVEL,
  229. "OR: Client connected\n"));
  230. *pfConnectFlags = 0;
  231. // Fill in security parameters.
  232. if (s_fEnableDCOM == FALSE) *pfConnectFlags |= CONNECT_DISABLEDCOM;
  233. if (s_fCatchServerExceptions) *pfConnectFlags |= CONNECT_CATCH_SERVER_EXCEPTIONS;
  234. if (s_fBreakOnSilencedServerExceptions) *pfConnectFlags |= CONNECT_BREAK_ON_SILENCED_SERVER_EXCEPTIONS;
  235. if (s_fMutualAuth) *pfConnectFlags |= CONNECT_MUTUALAUTH;
  236. if (s_fSecureRefs) *pfConnectFlags |= CONNECT_SECUREREF;
  237. *pAuthnLevel = s_lAuthnLevel;
  238. *pImpLevel = s_lImpLevel;
  239. // Get legacy security settings
  240. fRet = GetLegacySecurity(pLegacySecurity);
  241. if (!fRet)
  242. {
  243. return OR_NOMEM;
  244. }
  245. // Get client\server svcs
  246. fRet = GetClientServerSvcs(pcClientSvc, aClientSvc, pcServerSvc, aServerSvc);
  247. if (!fRet)
  248. {
  249. return OR_NOMEM;
  250. }
  251. // Fill in channel hooks.
  252. UpdateChannelHooks( pcChannelHook, paChannelHook );
  253. *pSignature = 0;
  254. // This fails during setup but RPCSS can function anyway.
  255. RegisterAuthInfoIfNecessary();
  256. status = StartListeningIfNecessary();
  257. if (status != OR_OK)
  258. {
  259. return(status);
  260. }
  261. // Do client specific stuff
  262. status = CopyMyOrBindings(ppdsaOrBindings, NULL);
  263. if (status != OR_OK)
  264. {
  265. return(status);
  266. }
  267. status = LookupOrCreateToken(hClient, TRUE, &pToken); // Will check security
  268. if (status != OR_OK)
  269. {
  270. MIDL_user_free(*ppdsaOrBindings);
  271. *ppdsaOrBindings = 0;
  272. return(status);
  273. }
  274. gpClientLock->LockShared();
  275. pProcess = new CProcess(pToken, pwszWinstaDesktop, procID, dwFlags, status);
  276. if (pProcess && status == OR_OK)
  277. {
  278. *phProcess = (void *)pProcess;
  279. }
  280. else
  281. {
  282. if (pProcess)
  283. {
  284. gpClientLock->UnlockShared(); // Rundown takes an exclusive
  285. ReleaseProcess(pProcess);
  286. gpClientLock->LockShared(); // take it back
  287. }
  288. else
  289. {
  290. status = OR_NOMEM;
  291. }
  292. }
  293. if (status != OR_OK)
  294. {
  295. gpClientLock->ConvertToExclusive();
  296. MIDL_user_free(*ppdsaOrBindings);
  297. *ppdsaOrBindings = 0;
  298. *phProcess = 0;
  299. if ( pToken )
  300. pToken->Release();
  301. *pSignature = 0;
  302. gpClientLock->UnlockExclusive();
  303. return(OR_NOMEM);
  304. }
  305. *pSignature = (ULONG64) pProcess;
  306. *pTimeoutInSeconds = BaseTimeoutInterval;
  307. *pLocalMid = gLocalMid;
  308. ASSERT( (*phProcess == 0 && *ppdsaOrBindings == 0) || status == OR_OK);
  309. _AllocateReservedIds(0,
  310. cIdsToReserve,
  311. pidReservedBase);
  312. *pScmProcessID = GetCurrentProcessId();
  313. *pThreadID = InterlockedExchangeAdd((long *)&gNextThreadID,1);
  314. *pguidRPCSSProcessIdentifier = *(pProcess->GetGuidProcessIdentifier());
  315. gpClientLock->UnlockShared();
  316. return(status);
  317. }
  318. error_status_t
  319. _AllocateReservedIds(
  320. IN handle_t hClient,
  321. IN LONG cIdsToReserve,
  322. OUT ID *pidReservedBase
  323. )
  324. /*++
  325. Routine Description:
  326. // Called by local clients to reserve a range of IDs which will
  327. // not conflict with any other local IDs.
  328. Arguments:
  329. hClient - 0 or the connection of the client.
  330. cIdsToReserve - Number of IDs to reserve.
  331. pidReservedBase - Starting value of the reserved IDs. The
  332. lower DWORD of this can be increatmented to generate
  333. cIdsToReserve unique IDs.
  334. Return Value:
  335. OR_OK
  336. --*/
  337. {
  338. UINT type;
  339. // Ensure this is a local client calling (raises an exception
  340. // if this is not the case):
  341. CheckLocalCall(hClient);
  342. // Parameter validation
  343. if (!pidReservedBase)
  344. return OR_BADPARAM;
  345. if (cIdsToReserve > 10 || cIdsToReserve < 0)
  346. {
  347. cIdsToReserve = 10;
  348. }
  349. *pidReservedBase = AllocateId(cIdsToReserve);
  350. return(OR_OK);
  351. }
  352. RPC_STATUS
  353. NegotiateDCOMVersion(
  354. IN OUT COMVERSION *pVersion
  355. )
  356. /*++
  357. Routine Description:
  358. // Called when we receive a COMVERSION from a remote machine
  359. // to determine which DCOM protocol level to talk.
  360. Arguments:
  361. pVersion - version of the remote machine. Modified if necessary
  362. by this routine to be the lower of the two versions.
  363. Return Value:
  364. OR_OK
  365. --*/
  366. {
  367. // Parameter validation
  368. if (!pVersion)
  369. return OR_BADPARAM;
  370. if (pVersion->MajorVersion == COM_MAJOR_VERSION)
  371. {
  372. if (pVersion->MinorVersion > COM_MINOR_VERSION)
  373. {
  374. // since the client has a lower minor version number,
  375. // use the lower of the two.
  376. pVersion->MinorVersion = COM_MINOR_VERSION;
  377. }
  378. return OR_OK;
  379. }
  380. return RPC_E_VERSION_MISMATCH;
  381. }
  382. error_status_t
  383. _ClientResolveOXID(
  384. IN handle_t hClient,
  385. IN PHPROCESS phProcess,
  386. IN OXID *poxidServer,
  387. IN DUALSTRINGARRAY *pdsaServerBindings,
  388. IN LONG fApartment,
  389. OUT OXID_INFO *poxidInfo,
  390. OUT MID *pDestinationMid,
  391. OUT USHORT *pusAuthnSvc
  392. )
  393. /*++
  394. Routine Description:
  395. Discovers the OXID_INFO for an oxid. Will find local
  396. OXIDs without any help. It needs OR bindings in order
  397. to discover remote OXIDs.
  398. Arguments:
  399. phProcess - The context handle of the process.
  400. poxidServer - The OXID (a uuid) to resolve.
  401. pdsaServerBindings - Compressed string bindings to
  402. the OR on the server's machine.
  403. fApartment - non-zero if the client is aparment model.
  404. REVIEW: What to do with mixed model clients?
  405. What to do when auto registering an OID?
  406. poxidInfo - If successful this will contain information about the oxid and
  407. an expanded string binding to the server oxid's process.
  408. pulAuthnSvc - if successful this will contain the exact id (ie, will not be snego)
  409. of the authn svc used to talk to the server.
  410. Return Value:
  411. OR_NOMEM - Common.
  412. OR_BADOXID - Unable to resolve it.
  413. OR_OK - Success.
  414. --*/
  415. {
  416. // REVIEW: no security check here. OXID info
  417. // is not private and you can allocate memory in
  418. // your process, too. If we needed to store some
  419. // info in the client process then a security
  420. // is needed
  421. // Parameter validation done below in ResolveClientOXID
  422. return ResolveClientOXID( hClient,
  423. phProcess,
  424. poxidServer,
  425. pdsaServerBindings,
  426. fApartment,
  427. 0,
  428. NULL,
  429. poxidInfo,
  430. pDestinationMid,
  431. FALSE,
  432. RPC_C_AUTHN_NONE,
  433. NULL,
  434. pusAuthnSvc);
  435. }
  436. error_status_t
  437. ResolveClientOXID(
  438. handle_t hClient,
  439. PHPROCESS phProcess,
  440. OXID *poxidServer,
  441. DUALSTRINGARRAY *pdsaServerBindings,
  442. LONG fApartment,
  443. USHORT wProtseqId,
  444. WCHAR *pMachineName,
  445. OXID_INFO *poxidInfo,
  446. MID *pDestinationMid,
  447. BOOL fUnsecure,
  448. USHORT wAuthnSvc,
  449. BOOL* pIsLocalOxid,
  450. USHORT* pusAuthnSvc
  451. )
  452. /*++
  453. Routine Description:
  454. Discovers the OXID_INFO for an oxid. Will find local
  455. OXIDs without any help. It needs OR bindings in order
  456. to discover remote OXIDs.
  457. Arguments:
  458. phProcess - The context handle of the process.
  459. Since this is called from SCM directly this function
  460. CAN BE called on the same process by more then one
  461. thread at a time.
  462. poxidServer - The OXID (a uuid) to resolve.
  463. pdsaServerBindings - Compressed string bindings to
  464. the OR on the server's machine.
  465. fApartment - non-zero if the client is aparment model.
  466. REVIEW: What to do with mixed model clients?
  467. What to do when auto registering an OID?
  468. poxidInfo - If successful this will contain information about the oxid and
  469. an expanded string binding to the server oxid's process.
  470. fUnsecure - if TRUE, activation was done unsecurely so set
  471. MID appropriately.
  472. wAuthnSvc - Hint of authentication service that might work or
  473. RPC_C_AUTHN_NONE if no hint.
  474. pusAuthnSvc - if successful this will contain the exact id (ie, will not be snego)
  475. of the authn svc used to talk to the server.
  476. Return Value:
  477. OR_NOMEM - Common.
  478. OR_BADOXID - Unable to resolve it.
  479. OR_OK - Success.
  480. --*/
  481. {
  482. CProcess *pProcess;
  483. CClientOxid *pOxid = NULL;
  484. CServerOxid *pServerOxid;
  485. CMid *pMid;
  486. ORSTATUS status = OR_OK;
  487. BOOL fReference;
  488. BOOL fServerApartment = FALSE;
  489. BOOL fResolved = FALSE;
  490. BOOL fLazyReleaseNewCopyOfpOxid = FALSE;
  491. DWORD i = 0;
  492. WCHAR *pPrincipal = NULL;
  493. WCHAR *pMachineNameFromBindings = NULL;
  494. BOOL fImpersonating = FALSE;
  495. // Parameter validation
  496. if (!poxidServer || !pdsaServerBindings || !poxidInfo ||
  497. !pDestinationMid || !pusAuthnSvc)
  498. {
  499. return OR_BADPARAM;
  500. }
  501. pProcess = ReferenceProcess(phProcess);
  502. ASSERT(pProcess);
  503. // CheckLocalSecurity will throw an exception if something is wrong
  504. CheckLocalSecurity(hClient, pProcess);
  505. if (! dsaValid(pdsaServerBindings))
  506. {
  507. return(OR_BADPARAM);
  508. }
  509. // If wProtseqId == ID_DCOMHTTP, we should impersonate
  510. // because RPC will read some info from HKEY_CURRENT_USER.
  511. // Sometimes ole32 will call us with 0 wProtseqId, in
  512. // which cases we'll have to look at pdsaServerBindings.
  513. if(wProtseqId == ID_DCOMHTTP)
  514. {
  515. fImpersonating = (RpcImpersonateClient(hClient) == RPC_S_OK);
  516. }
  517. else if(wProtseqId == 0)
  518. {
  519. if(pdsaServerBindings && pdsaServerBindings->aStringArray
  520. && *(pdsaServerBindings->aStringArray) == ID_DCOMHTTP)
  521. {
  522. fImpersonating = (RpcImpersonateClient(hClient) == RPC_S_OK);
  523. }
  524. }
  525. // Attempt to lookup MID and OXID
  526. gpClientLock->LockExclusive();
  527. CMidKey midkey(pdsaServerBindings);
  528. pMid = (CMid *)gpMidTable->Lookup(midkey);
  529. if (0 == pMid)
  530. {
  531. fReference = TRUE;
  532. pMid = new(pdsaServerBindings->wNumEntries * sizeof(WCHAR)) CMid(pdsaServerBindings, FALSE);
  533. if (pMid)
  534. {
  535. gpMidTable->Add(pMid);
  536. pMid->SetAuthnSvc(wAuthnSvc);
  537. }
  538. if (0 == pMid)
  539. {
  540. status = OR_NOMEM;
  541. }
  542. }
  543. else
  544. {
  545. fReference = FALSE;
  546. }
  547. if (status == OR_OK)
  548. {
  549. CId2Key oxidkey(*poxidServer, pMid->Id());
  550. pOxid = (CClientOxid *)gpClientOxidTable->Lookup(oxidkey);
  551. if (0 == pOxid)
  552. {
  553. if (!fReference)
  554. {
  555. pMid->Reference();
  556. fReference = TRUE;
  557. }
  558. // Need to allocate the OXID. First step is too resolve it
  559. // either locally or remotely.
  560. gpClientLock->UnlockExclusive();
  561. if (pMid->IsLocal())
  562. {
  563. // Local OXID, lookup directly
  564. gpServerLock->LockShared();
  565. CIdKey key(*poxidServer);
  566. pServerOxid = (CServerOxid *)gpServerOxidTable->Lookup(key);
  567. if (pServerOxid)
  568. {
  569. status = pServerOxid->GetInfo(poxidInfo, TRUE);
  570. fServerApartment = pServerOxid->Apartment();
  571. // reset the protseq id so we use LRPC.
  572. wProtseqId = 0;
  573. pMachineName = NULL;
  574. }
  575. else
  576. {
  577. status = OR_BADOXID;
  578. }
  579. ASSERT(status != OR_OK || dsaValid(poxidInfo->psa));
  580. gpServerLock->UnlockShared();
  581. }
  582. else if (0 == poxidInfo->psa)
  583. {
  584. // Remote OXID, call ResolveOxid
  585. handle_t hRemoteOr;
  586. void *pAuthId = NULL;
  587. USHORT iBinding;
  588. poxidInfo->psa = 0;
  589. ASSERT(!pMachineName);
  590. status = OR_NOMEM;
  591. hRemoteOr = pMid->GetBinding();
  592. if (hRemoteOr)
  593. {
  594. if (pMid->IsSecure())
  595. {
  596. i = 0;
  597. // Form server principal name
  598. pMachineNameFromBindings = ExtractMachineName( pMid->GetStringBinding() );
  599. if (pMachineNameFromBindings)
  600. {
  601. pPrincipal = new WCHAR[lstrlenW(pMachineNameFromBindings) +
  602. (sizeof(RPCSS_SPN_PREFIX) / sizeof(WCHAR)) + 1];
  603. if (pPrincipal)
  604. {
  605. lstrcpyW(pPrincipal, RPCSS_SPN_PREFIX);
  606. lstrcatW(pPrincipal, pMachineNameFromBindings);
  607. }
  608. delete pMachineNameFromBindings;
  609. }
  610. // It is possible that we already impersonated above,
  611. // so it might be unnecessary to do it here.
  612. if(!fImpersonating)
  613. {
  614. status = RpcImpersonateClient(hClient);
  615. if (status != RPC_S_OK)
  616. {
  617. KdPrintEx((DPFLTR_DCOMSS_ID,
  618. DPFLTR_WARNING_LEVEL,
  619. "OR: Unable to impersonate for resolve %d\n",
  620. status));
  621. }
  622. }
  623. }
  624. else
  625. {
  626. i = s_cRpcssSvc+1;
  627. }
  628. // The loop index has the following meanings:
  629. // 0 - Try pMid->GetAuthnSvc
  630. // 1 through s_cRpcssSvc - Try s_aRpcssSvc
  631. // s_cRpcssSvc+1 - Try unsecure
  632. USHORT wFirstSvc = pMid->GetAuthnSvc();
  633. for (; i < s_cRpcssSvc + 2; i++)
  634. {
  635. BOOL bSetSecurityCallBack = FALSE;
  636. USHORT usAuthSvcFromCallback;
  637. // Choose an authentication service.
  638. if (i < s_cRpcssSvc+1)
  639. {
  640. if (i == 0)
  641. wAuthnSvc = wFirstSvc;
  642. else
  643. {
  644. // Skip this authentication service if already
  645. // tried.
  646. wAuthnSvc = s_aRpcssSvc[i-1].wId;
  647. if (wAuthnSvc == wFirstSvc)
  648. continue;
  649. }
  650. // See if the server uses this authentication service.
  651. if (ValidAuthnSvc( pMid->GetStrings(), wAuthnSvc ))
  652. {
  653. RPC_SECURITY_QOS qos;
  654. qos.Version = RPC_C_SECURITY_QOS_VERSION;
  655. qos.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH;
  656. qos.IdentityTracking = RPC_C_QOS_IDENTITY_DYNAMIC;
  657. qos.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE;
  658. if (wAuthnSvc == RPC_C_AUTHN_GSS_NEGOTIATE)
  659. {
  660. pAuthId = ComputeSvcList( pMid->GetStrings() );
  661. if (pAuthId)
  662. {
  663. // if using snego, we need to know what sec pkg is eventually negotiated:
  664. if (gpCRpcSecurityCallbackMgr->RegisterForRpcAuthSvcCallBack(hRemoteOr))
  665. bSetSecurityCallBack = TRUE;
  666. }
  667. else
  668. {
  669. // ComputeSvcList couldn't get the memory; just keep going
  670. status = OR_NOMEM;
  671. continue;
  672. }
  673. }
  674. // Set the security info
  675. // AuthnSvc is unsigned long and 0xFFFF gets 0 extended
  676. status = RpcBindingSetAuthInfoEx(hRemoteOr,
  677. pPrincipal,
  678. RPC_C_AUTHN_LEVEL_CONNECT,
  679. wAuthnSvc != 0xFFFF ? wAuthnSvc
  680. : RPC_C_AUTHN_DEFAULT,
  681. pAuthId,
  682. 0,
  683. &qos);
  684. if (status != RPC_S_OK)
  685. {
  686. KdPrintEx((DPFLTR_DCOMSS_ID,
  687. DPFLTR_WARNING_LEVEL,
  688. "OR: RpcBindingSetAuthInfo to %d failed!! %d\n",
  689. wAuthnSvc,
  690. status));
  691. if (bSetSecurityCallBack)
  692. {
  693. // Get rid of our callback registration
  694. gpCRpcSecurityCallbackMgr->GetAuthSvcAndTurnOffCallback(hRemoteOr, NULL);
  695. }
  696. delete pAuthId;
  697. pAuthId = NULL;
  698. continue;
  699. }
  700. }
  701. else
  702. continue;
  703. }
  704. // Force the binding handle unsecure.
  705. else if (pMid->IsSecure())
  706. {
  707. wAuthnSvc = RPC_C_AUTHN_NONE;
  708. status = RpcBindingSetAuthInfo(hRemoteOr,
  709. 0,
  710. RPC_C_AUTHN_LEVEL_NONE,
  711. RPC_C_AUTHN_NONE,
  712. 0,
  713. 0);
  714. if (status != RPC_S_OK)
  715. {
  716. KdPrintEx((DPFLTR_DCOMSS_ID,
  717. DPFLTR_WARNING_LEVEL,
  718. "OR: RpcBindingSetAuthInfo to NONE failed!! %d\n",
  719. status));
  720. }
  721. }
  722. // try calling ResolveOxid2 first, if that fails,
  723. // try ResolveOxid.
  724. status = ResolveOxid2(hRemoteOr,
  725. poxidServer,
  726. cMyProtseqs,
  727. aMyProtseqs,
  728. &poxidInfo->psa,
  729. &poxidInfo->ipidRemUnknown,
  730. &poxidInfo->dwAuthnHint,
  731. &poxidInfo->version
  732. );
  733. if (status == RPC_S_PROCNUM_OUT_OF_RANGE)
  734. {
  735. // must be a downlevel server (COMVERSION == 5.1), try calling on
  736. // the old ResolveOXID method.
  737. // REVIEW if it's a downlevel server what does this mean wrt
  738. // bug 406902 and the snego mess? I think we're still okay.
  739. poxidInfo->version.MajorVersion = COM_MAJOR_VERSION;
  740. poxidInfo->version.MinorVersion = COM_MINOR_VERSION_1;
  741. poxidInfo->dwFlags = 0;
  742. status = ResolveOxid(hRemoteOr,
  743. poxidServer,
  744. cMyProtseqs,
  745. aMyProtseqs,
  746. &poxidInfo->psa,
  747. &poxidInfo->ipidRemUnknown,
  748. &poxidInfo->dwAuthnHint
  749. );
  750. }
  751. // At this point we are done making calls on the binding handle
  752. // Turn off the security callback no matter what the result of
  753. // the call was
  754. if (bSetSecurityCallBack)
  755. {
  756. if (status == OR_OK)
  757. {
  758. if (!gpCRpcSecurityCallbackMgr->GetAuthSvcAndTurnOffCallback(hRemoteOr,
  759. &usAuthSvcFromCallback))
  760. {
  761. // something went wrong. Basically we don't trust what the callback
  762. // told us. Fall back on the original behavior
  763. bSetSecurityCallBack = FALSE;
  764. }
  765. }
  766. else
  767. {
  768. // call did not go through; just cancel the callback registration
  769. gpCRpcSecurityCallbackMgr->GetAuthSvcAndTurnOffCallback(hRemoteOr, NULL);
  770. bSetSecurityCallBack = FALSE;
  771. }
  772. }
  773. if (status == OR_OK)
  774. {
  775. status = NegotiateDCOMVersion(&poxidInfo->version);
  776. }
  777. if (status == OR_OK)
  778. {
  779. // Remember which auth.svc got used:
  780. if (bSetSecurityCallBack)
  781. {
  782. // we should not have set it unless we're using snego, and we
  783. // should have gotten something other than snego back
  784. ASSERT(wAuthnSvc == RPC_C_AUTHN_GSS_NEGOTIATE &&
  785. usAuthSvcFromCallback != RPC_C_AUTHN_GSS_NEGOTIATE);
  786. // Set the negotiated pkg; if we got back Kerberos, then cache
  787. // Snego. This will partially fix the fact that we are
  788. // hard-coding the authn svc for all future users of this mid
  789. if (usAuthSvcFromCallback == RPC_C_AUTHN_GSS_KERBEROS)
  790. pMid->SetAuthnSvc( RPC_C_AUTHN_GSS_NEGOTIATE );
  791. else
  792. pMid->SetAuthnSvc( usAuthSvcFromCallback );
  793. }
  794. else
  795. {
  796. // Just use whatever was set on the call
  797. pMid->SetAuthnSvc( wAuthnSvc );
  798. }
  799. if (dsaValid(poxidInfo->psa))
  800. {
  801. wProtseqId = poxidInfo->psa->aStringArray[0];
  802. }
  803. else
  804. {
  805. KdPrintEx((DPFLTR_DCOMSS_ID,
  806. DPFLTR_WARNING_LEVEL,
  807. "OR: Server %s returned a bogus string array: %p\n",
  808. pMid->PrintableName(),
  809. poxidInfo->psa));
  810. ASSERT(0);
  811. if (poxidInfo->psa)
  812. {
  813. MIDL_user_free(poxidInfo->psa);
  814. poxidInfo->psa = 0;
  815. }
  816. status = OR_BADOXID;
  817. }
  818. break;
  819. }
  820. else if (status != RPC_S_ACCESS_DENIED &&
  821. status != RPC_S_UNKNOWN_AUTHN_SERVICE &&
  822. status != RPC_S_UNKNOWN_AUTHZ_SERVICE &&
  823. status != RPC_S_SEC_PKG_ERROR )
  824. {
  825. KdPrintEx((DPFLTR_DCOMSS_ID,
  826. DPFLTR_WARNING_LEVEL,
  827. "OR: Remote resolve OXID failed %d\n",
  828. status));
  829. break;
  830. }
  831. }
  832. RpcBindingFree(&hRemoteOr);
  833. delete pPrincipal;
  834. pPrincipal = NULL;
  835. delete pAuthId;
  836. pAuthId = NULL;
  837. }
  838. }
  839. // Else it's a remote MID, but we were given the OXID info
  840. // and protseq id from the SCM after a remote activation.
  841. else
  842. fResolved = TRUE;
  843. gpClientLock->LockExclusive();
  844. ASSERT(fReference);
  845. if ( OR_OK == status
  846. && (pMid->GetAuthnSvc() == RPC_C_AUTHN_NONE ||
  847. TRUE == fUnsecure) )
  848. {
  849. KdPrintEx((DPFLTR_DCOMSS_ID,
  850. DPFLTR_INFO_LEVEL,
  851. "OR: Machine %S, unsecure retry ok, assuming no sec\n",
  852. pMid->PrintableName()));
  853. pMid->SecurityFailed();
  854. }
  855. if (status == OR_OK)
  856. {
  857. // Lookup the oxid again to make sure it hasn't been added in the meantime.
  858. pOxid = (CClientOxid *)gpClientOxidTable->Lookup(oxidkey);
  859. if (0 == pOxid)
  860. {
  861. ASSERT(dsaValid(poxidInfo->psa));
  862. pOxid = new CClientOxid(*poxidServer,
  863. pMid,
  864. wProtseqId,
  865. pMachineName,
  866. fServerApartment);
  867. if (0 != pOxid)
  868. {
  869. status = pOxid->UpdateInfo(poxidInfo);
  870. if (OR_OK == status)
  871. {
  872. gpClientOxidTable->Add(pOxid);
  873. }
  874. else
  875. {
  876. // Will release mid, will also remove it (unnecessarily)
  877. // from the table.
  878. delete pOxid;
  879. pOxid = NULL;
  880. }
  881. }
  882. else
  883. {
  884. status = OR_NOMEM;
  885. pMid->Release(); // May actually go away..
  886. }
  887. }
  888. else
  889. {
  890. // Release our now extra reference on the MID
  891. DWORD t = pMid->Release();
  892. ASSERT(t > 0);
  893. pOxid->Reference();
  894. }
  895. MIDL_user_free(poxidInfo->psa);
  896. poxidInfo->psa = 0;
  897. }
  898. else
  899. {
  900. // Resolve failed, get rid of our extra reference.
  901. pMid->Release();
  902. }
  903. }
  904. else
  905. {
  906. // Found the OXID, must also have found the MID
  907. ASSERT(fReference == FALSE);
  908. fResolved = TRUE;
  909. if ( poxidInfo->psa )
  910. {
  911. MIDL_user_free(poxidInfo->psa);
  912. poxidInfo->psa = 0;
  913. }
  914. pOxid->Reference();
  915. }
  916. }
  917. ASSERT( (status != OR_OK) || (pOxid && pMid) );
  918. if ( status == OR_OK
  919. && pOxid->IsLocal() == FALSE)
  920. {
  921. if (fResolved)
  922. *poxidServer = 0;
  923. }
  924. if(NULL != pIsLocalOxid)
  925. {
  926. if((status == OR_OK) && pOxid->IsLocal())
  927. *pIsLocalOxid = TRUE;
  928. else
  929. *pIsLocalOxid = FALSE;
  930. }
  931. if (status == OR_OK)
  932. {
  933. *pDestinationMid = pMid->Id();
  934. *pusAuthnSvc = pMid->GetAuthnSvc();
  935. // GetInfo may release the lock
  936. status = pOxid->GetInfo(fApartment, poxidInfo);
  937. }
  938. if (pOxid)
  939. {
  940. pOxid->Release();
  941. }
  942. gpClientLock->UnlockExclusive();
  943. return(status);
  944. }
  945. void
  946. FreeServerOids(
  947. CProcess *pProcess,
  948. ULONG cServerOidsToFree,
  949. OID aServerOidsToFree[]
  950. )
  951. /*++
  952. Routine Description:
  953. Frees the OIDs passed in.
  954. Arguments:
  955. phProcess - The context handle of the process.
  956. Since this is called from SCM directly this function
  957. CAN BE called on the same process by more then one
  958. thread at a time.
  959. cServerOidsToFree - Count of entries in aServerOidsToFree
  960. aServerOidsToFree - OIDs allocated by the server process
  961. and no longer needed.
  962. --*/
  963. {
  964. // KdPrintEx((DPFLTR_DCOMSS_ID,
  965. // DPFLTR_WARNING_LEVEL,
  966. // "OR: FreeServerOids: pProcess:%x cOids:%x pOids:%x\n",
  967. // pProcess,
  968. // cServerOidsToFree,
  969. // aServerOidsToFree));
  970. ASSERT(gpServerLock->HeldExclusive());
  971. if (cServerOidsToFree)
  972. {
  973. CServerOid *pOid;
  974. CServerOxid *pOxid;
  975. for (ULONG i = 0; i < cServerOidsToFree; i++)
  976. {
  977. CIdKey oidkey(aServerOidsToFree[i]);
  978. pOid = (CServerOid *)gpServerOidTable->Lookup(oidkey);
  979. if (pOid && pOid->IsRunningDown() == FALSE)
  980. {
  981. pOxid = pOid->GetOxid();
  982. ASSERT(pOxid);
  983. if (pProcess->IsOwner(pOxid))
  984. {
  985. if (pOid->References() == 0)
  986. {
  987. pOid->Remove();
  988. pOid->SetRundown(TRUE);
  989. delete pOid;
  990. }
  991. else
  992. {
  993. pOid->Free();
  994. }
  995. }
  996. else
  997. {
  998. KdPrintEx((DPFLTR_DCOMSS_ID,
  999. DPFLTR_WARNING_LEVEL,
  1000. "OR: Process %p tried to free OID %p it didn't own\n",
  1001. pProcess,
  1002. pOid));
  1003. }
  1004. }
  1005. else
  1006. {
  1007. KdPrintEx((DPFLTR_DCOMSS_ID,
  1008. DPFLTR_WARNING_LEVEL,
  1009. "OR: Process %p freed OID %p that didn't exist\n",
  1010. pProcess,
  1011. &aServerOidsToFree[i]));
  1012. }
  1013. }
  1014. }
  1015. }
  1016. error_status_t
  1017. _BulkUpdateOIDs(
  1018. IN handle_t hClient,
  1019. IN PHPROCESS phProcess,
  1020. IN ULONG cOidsToBeAdded,
  1021. IN OXID_OID_PAIR aOidsToBeAdded[],
  1022. OUT LONG aStatusOfAdds[],
  1023. IN ULONG cOidsToBeRemoved,
  1024. IN OID_MID_PAIR aOidsToBeRemoved[],
  1025. IN ULONG cServerOidsToFree,
  1026. IN OID aServerOidsToFree[],
  1027. IN ULONG cServerOidsToUnPin,
  1028. IN OID aServerOidsToUnPin[],
  1029. IN ULONG cClientOxidsToFree,
  1030. IN OXID_REF aClientOxidsToFree[]
  1031. )
  1032. /*++
  1033. Routine Description:
  1034. Updates the set of remote OIDs in use by a process.
  1035. Note:
  1036. An OID maybe removed before it is added. This means that
  1037. the client was using it and is no longer using it. In
  1038. this case a single delete from set ping is made to keep
  1039. the object alive. This is only needed if the client
  1040. has remarshalled a pointer to the object.
  1041. Arguments:
  1042. phProcess - Context handle for the process.
  1043. cOidsToBeAdded - Count of aOidsToBeAdded and aStatusOfAdds
  1044. aOidsToBeAdded - OID-OXID-MID pairs representing the
  1045. oids and the owning oxids to add.
  1046. aStatusOfAdds - Some adds may succeed when other fail.
  1047. OR_NOMEM - couldn't allocate storage
  1048. OR_BADOXID - OXID doesn't exist.
  1049. OR_OK (0) - added to set
  1050. cOidsToBeRemoved - Count of entries in aOidsToBeRemoved.
  1051. aOidsToBeRemoved - OID-MID pairs to be removed.
  1052. cServerOidsToFree - Count of entries in aServerOidsToFree
  1053. aServerOidsToFree - OIDs allocated by the client process
  1054. and no longer needed.
  1055. cServerOidsToUnPin - Count of entries in aServerOidsToUnPin
  1056. aServerOidsToUnPin - OIDs that the client process previously
  1057. told us were pinned\locked, and now no longer are.
  1058. cClientOxidsToFree - COunt of enties in aClientOxidsToFree
  1059. aClientOxidsToFree - OXIDs owned by a process (due to a direct
  1060. or indirect call to ClientResolveOxid) which are no longer
  1061. in use by the client.
  1062. Return Value:
  1063. OR_OK - All updates completed ok.
  1064. OR_PARTIAL_UPDATE - At least one entry in aStatusOfAdds is not OR_OK
  1065. --*/
  1066. {
  1067. CProcess *pProcess;
  1068. CClientOxid *pOxid;
  1069. CClientOid *pOid;
  1070. CClientSet *pSet;
  1071. CMid *pMid;
  1072. CToken *pToken;
  1073. BOOL fPartial = FALSE;
  1074. BOOL fNewSet = FALSE;
  1075. DUALSTRINGARRAY *pdsa = NULL;
  1076. ULONG i;
  1077. // Parameter validation. If zero is passed for the size-of-array param
  1078. // then we don't care about the array param itself (since we never
  1079. // look at it)
  1080. if (!(cOidsToBeAdded > 0 ? (aOidsToBeAdded != NULL) : TRUE) ||
  1081. !(cOidsToBeAdded > 0 ? (aStatusOfAdds != NULL) : TRUE) ||
  1082. !(cOidsToBeRemoved > 0 ? (aOidsToBeRemoved != NULL) : TRUE) ||
  1083. !(cServerOidsToFree > 0 ? (aServerOidsToFree != NULL): TRUE) ||
  1084. !(cServerOidsToUnPin > 0 ? (aServerOidsToUnPin != NULL): TRUE) ||
  1085. !(cClientOxidsToFree > 0 ? (aClientOxidsToFree != NULL) : TRUE))
  1086. {
  1087. return OR_BADPARAM;
  1088. }
  1089. pProcess = ReferenceProcess(phProcess);
  1090. ASSERT(pProcess);
  1091. CheckLocalSecurity(hClient, pProcess);
  1092. if (cOidsToBeAdded || cOidsToBeRemoved)
  1093. {
  1094. ORSTATUS status = CopyMyOrBindings(&pdsa, NULL);
  1095. if (status != RPC_S_OK)
  1096. {
  1097. return status;
  1098. }
  1099. gpClientLock->LockExclusive();
  1100. }
  1101. // /////////////////////////////////////////////////////////////////
  1102. // Process Adds.
  1103. for (i = 0; i < cOidsToBeAdded; i++)
  1104. {
  1105. // Lookup up the oxid owning this new oid.
  1106. CId2Key oxidkey(aOidsToBeAdded[i].oxid, aOidsToBeAdded[i].mid);
  1107. pOxid = (CClientOxid *)gpClientOxidTable->Lookup(oxidkey);
  1108. if (0 == pOxid)
  1109. {
  1110. OXID_INFO infoT;
  1111. ORSTATUS status;
  1112. MID mid;
  1113. gpClientLock->UnlockExclusive();
  1114. infoT.psa = 0;
  1115. USHORT usAuthnSvc;
  1116. status = _ClientResolveOXID(hClient,
  1117. phProcess,
  1118. &aOidsToBeAdded[i].oxid,
  1119. pdsa,
  1120. TRUE,
  1121. &infoT,
  1122. &mid,
  1123. &usAuthnSvc);
  1124. gpClientLock->LockExclusive();
  1125. if (status == OR_OK)
  1126. {
  1127. ASSERT(infoT.psa);
  1128. ASSERT(mid == gLocalMid);
  1129. MIDL_user_free(infoT.psa);
  1130. pOxid = (CClientOxid *)gpClientOxidTable->Lookup(oxidkey);
  1131. if (pOxid == 0)
  1132. {
  1133. KdPrintEx((DPFLTR_DCOMSS_ID,
  1134. DPFLTR_INFO_LEVEL,
  1135. "OR: Auto resolving oxid %p failed, wrong machine\n",
  1136. &oxidkey));
  1137. status = OR_BADOXID;
  1138. }
  1139. }
  1140. if (status != OR_OK)
  1141. {
  1142. aStatusOfAdds[i] = OR_BADOXID;
  1143. fPartial = TRUE;
  1144. continue;
  1145. }
  1146. }
  1147. // Find or create the set.
  1148. CId2Key setkey(aOidsToBeAdded[i].mid, (ID)pProcess->GetToken());
  1149. pSet = (CClientSet *)gpClientSetTable->Lookup(setkey);
  1150. if (pSet == 0)
  1151. {
  1152. pSet = new CClientSet(pOxid->GetMid(), pProcess->GetToken());
  1153. if (pSet == 0)
  1154. {
  1155. aStatusOfAdds[i] = OR_NOMEM;
  1156. fPartial = TRUE;
  1157. continue;
  1158. }
  1159. else
  1160. {
  1161. gpClientSetTable->Add(pSet);
  1162. pSet->Insert();
  1163. fNewSet = TRUE;
  1164. }
  1165. }
  1166. // Find or create the oid. If we create it, add a reference
  1167. // to the oxid for the new oid.
  1168. CId3Key oidkey(aOidsToBeAdded[i].oid, aOidsToBeAdded[i].mid, pProcess->GetToken());
  1169. pOid = (CClientOid *)gpClientOidTable->Lookup(oidkey);
  1170. if (0 == pOid)
  1171. {
  1172. pOid = new CClientOid(aOidsToBeAdded[i].oid,
  1173. aOidsToBeAdded[i].mid,
  1174. pProcess->GetToken(),
  1175. pOxid,
  1176. pSet
  1177. );
  1178. if (fNewSet)
  1179. {
  1180. // pOid either owns a refernce now or we need to
  1181. // cleanup the set anyway.
  1182. pSet->Release();
  1183. fNewSet = FALSE;
  1184. }
  1185. if (pOid)
  1186. {
  1187. aStatusOfAdds[i] = pSet->RegisterObject(pOid);
  1188. if (aStatusOfAdds[i] == OR_OK)
  1189. {
  1190. gpClientOidTable->Add(pOid);
  1191. }
  1192. else
  1193. {
  1194. pOid->Release();
  1195. pOid = 0;
  1196. fPartial = TRUE;
  1197. continue;
  1198. }
  1199. }
  1200. else
  1201. {
  1202. aStatusOfAdds[i] = OR_NOMEM;
  1203. fPartial = TRUE;
  1204. continue;
  1205. }
  1206. }
  1207. else
  1208. {
  1209. ASSERT(fNewSet == FALSE);
  1210. pOid->ClientReference();
  1211. }
  1212. // If this fails it will release the oid.
  1213. aStatusOfAdds[i] = pProcess->AddOid(pOid);
  1214. if (aStatusOfAdds[i] != OR_OK)
  1215. {
  1216. fPartial = TRUE;
  1217. }
  1218. } // for oids to add
  1219. // /////////////////////////////////////////////////////////////////
  1220. // Process deletes
  1221. for (i = 0; i < cOidsToBeRemoved; i++)
  1222. {
  1223. CId3Key oidkey(aOidsToBeRemoved[i].oid,
  1224. aOidsToBeRemoved[i].mid,
  1225. pProcess->GetToken());
  1226. pOid = (CClientOid *)gpClientOidTable->Lookup(oidkey);
  1227. if (pOid)
  1228. {
  1229. CClientOid *pT = pProcess->RemoveOid(pOid);
  1230. if (pT == 0)
  1231. {
  1232. KdPrintEx((DPFLTR_DCOMSS_ID,
  1233. DPFLTR_WARNING_LEVEL,
  1234. "OR: Client process %p tried to remove oid %p which"
  1235. "it didn't own\n",
  1236. pProcess,
  1237. &aOidsToBeRemoved[i]));
  1238. }
  1239. else
  1240. ASSERT(pT == pOid);
  1241. }
  1242. else
  1243. KdPrintEx((DPFLTR_DCOMSS_ID,
  1244. DPFLTR_INFO_LEVEL,
  1245. "OR: Client %p removed an OID that doesn't exist\n",
  1246. pProcess));
  1247. } // for oids to delete
  1248. if (cOidsToBeAdded || cOidsToBeRemoved)
  1249. {
  1250. gpClientLock->UnlockExclusive();
  1251. }
  1252. ///////////////////////////////////////////////////////////////////
  1253. // Process server oid deletes
  1254. //
  1255. if (cServerOidsToFree > 0)
  1256. {
  1257. gpServerLock->LockExclusive();
  1258. FreeServerOids(pProcess, cServerOidsToFree, aServerOidsToFree);
  1259. gpServerLock->UnlockExclusive();
  1260. }
  1261. ///////////////////////////////////////////////////////////////////
  1262. //
  1263. // Process server oid unpins. We do not have an array of individual
  1264. // status update values for this operation; if the client gets back
  1265. // a success code from BulkUpdateOids, it can assume that all of the
  1266. // requested unpins were executed correctly.
  1267. //
  1268. // Also note that it is *always* safe to unpin an oid even if it should
  1269. // remain pinned; the worse that can happen is extra rundown calls.
  1270. //
  1271. if (cServerOidsToUnPin > 0)
  1272. {
  1273. gpServerLock->LockExclusive();
  1274. for (i = 0; i < cServerOidsToUnPin; i++)
  1275. {
  1276. CServerOid* pOid;
  1277. CIdKey key(aServerOidsToUnPin[i]);
  1278. pOid = (CServerOid *)gpServerOidTable->Lookup(key);
  1279. // Only unpin the oid if the calling process owns it. Don't
  1280. // assert if we didn't find it, since under stress we may
  1281. // be executing this code after the owning process\apt died.
  1282. if (pOid && pProcess->IsOwner(pOid->GetOxid()))
  1283. {
  1284. if (pOid->IsPinned())
  1285. {
  1286. pOid->SetPinned(FALSE);
  1287. }
  1288. }
  1289. }
  1290. gpServerLock->UnlockExclusive();
  1291. }
  1292. // Done
  1293. if (pdsa)
  1294. {
  1295. MIDL_user_free(pdsa);
  1296. }
  1297. if (fPartial)
  1298. {
  1299. return(OR_PARTIAL_UPDATE);
  1300. }
  1301. return(OR_OK);
  1302. }
  1303. error_status_t
  1304. _ServerAllocateOXIDAndOIDs(
  1305. IN handle_t hClient,
  1306. IN PHPROCESS phProcess,
  1307. OUT OXID *poxidServer,
  1308. IN LONG fApartment,
  1309. IN ULONG cOids,
  1310. OUT OID aOid[],
  1311. OUT PULONG pOidsAllocated,
  1312. IN OXID_INFO *poxidInfo, // No bindings
  1313. IN DUALSTRINGARRAY *pdsaStringBindings, // Expanded
  1314. IN DUALSTRINGARRAY *pdsaSecurityBindings, // Compressed
  1315. OUT DWORD64 *pdwBindingsID,
  1316. OUT DUALSTRINGARRAY **ppdsaOrBindings
  1317. )
  1318. /*++
  1319. Routine Description:
  1320. Allocates an OXID and 0 or more OIDs from the OR.
  1321. Arguments:
  1322. phProcess - The context handle of the process containing the OXID.
  1323. poxidServer - The OXID to register. May only register once.
  1324. cOids - Count of apOids
  1325. apOid - The OIDs to register within the OXID.
  1326. pcOidsAllocated - The number of OIDs actually allocated. Usually the
  1327. same as cOids unless a resource failure occures. Maybe 0.
  1328. poxidInfo - The OXID_INFO structure for the OXID without bindings.
  1329. pdsaStringBindings - Expanded string binding of the server.
  1330. pdsaSecurityBindings - The compressed security bindings of the server.
  1331. pOidsAllocated - The number of OIDs actually allocated. >= 0 and <= cOids.
  1332. pdwBindingsID -- The id of the bindings returned in ppdsaOrBindings
  1333. ppdsaOrBindings -- The current resolver bindings. Normally this is not
  1334. ever allocated, unless dynamic address tracking is enabled.
  1335. Return Value:
  1336. OR_OK - success. Returned even if some OID allocations fail. See the
  1337. pOidsAllocated parameter.
  1338. OR_NOMEM - Allocation of OXID failed.
  1339. OR_ACCESS_DENIDED - Raised if non-local client
  1340. OR_BADPARAM - if string arrays are incorrect.
  1341. --*/
  1342. {
  1343. ORSTATUS status = OR_OK;
  1344. CServerOxid *pNewOxid;
  1345. CProcess *pProcess = ReferenceProcess(phProcess);
  1346. ASSERT(pProcess);
  1347. CheckLocalSecurity(hClient, pProcess);
  1348. // Parameter validation
  1349. if (!poxidServer ||
  1350. !(cOids > 0 ? (aOid != NULL) : TRUE) ||
  1351. !pOidsAllocated || !poxidInfo ||
  1352. !pdsaStringBindings || !pdsaSecurityBindings ||
  1353. !pdwBindingsID || !ppdsaOrBindings)
  1354. {
  1355. return OR_BADPARAM;
  1356. }
  1357. gpServerLock->LockExclusive();
  1358. // Save the string bindings back to the process
  1359. if (!dsaValid(pdsaStringBindings) )
  1360. {
  1361. status = OR_BADPARAM;
  1362. }
  1363. if (!dsaValid(pdsaSecurityBindings))
  1364. {
  1365. status = OR_BADPARAM;
  1366. }
  1367. if (status == OR_OK)
  1368. {
  1369. status = pProcess->ProcessBindings(pdsaStringBindings,
  1370. pdsaSecurityBindings);
  1371. }
  1372. *pdwBindingsID = 0;
  1373. *ppdsaOrBindings = NULL;
  1374. VALIDATE((status, OR_NOMEM, OR_BADPARAM, 0));
  1375. if (status != OR_OK)
  1376. {
  1377. gpServerLock->UnlockExclusive();
  1378. return(status);
  1379. }
  1380. pNewOxid = new CServerOxid(pProcess,
  1381. fApartment,
  1382. poxidInfo
  1383. );
  1384. if (0 == pNewOxid)
  1385. {
  1386. gpServerLock->UnlockExclusive();
  1387. return(OR_NOMEM);
  1388. }
  1389. // Add to process and lookup table.
  1390. status = pProcess->AddOxid(pNewOxid);
  1391. VALIDATE((status, OR_NOMEM, 0));
  1392. pNewOxid->Release(); // process has a reference now or failed
  1393. gpServerLock->UnlockExclusive();
  1394. if (status == OR_OK)
  1395. {
  1396. *poxidServer = pNewOxid->Id();
  1397. status = _ServerAllocateOIDs(0,
  1398. phProcess,
  1399. poxidServer,
  1400. 0,
  1401. NULL,
  1402. cOids,
  1403. aOid,
  1404. pOidsAllocated);
  1405. }
  1406. if (status == OR_OK && gbDynamicIPChangesEnabled)
  1407. {
  1408. if (ppdsaOrBindings && pProcess->NeedsORBindings())
  1409. {
  1410. // If doing dynamic IP changes, give process the current
  1411. // OR bindings
  1412. status = CopyMyOrBindings(ppdsaOrBindings, pdwBindingsID);
  1413. if (status == OR_OK)
  1414. {
  1415. pProcess->BindingsUpdated();
  1416. }
  1417. }
  1418. }
  1419. return(status);
  1420. }
  1421. error_status_t _ServerAllocateOIDs(
  1422. IN handle_t hClient,
  1423. IN PHPROCESS phProcess,
  1424. IN OXID *poxidServer,
  1425. IN ULONG cOidsReturn,
  1426. IN OID aOidsReturn[],
  1427. IN ULONG cOids,
  1428. OUT OID aOids[],
  1429. OUT PULONG pOidsAllocated
  1430. )
  1431. /*++
  1432. Routine Description:
  1433. Registers additional OIDs on behalf of an existing OXID.
  1434. Arguments:
  1435. phProcess - The context handle of the process containing the OXID and OIDs.
  1436. poxidServer - The OXID associated with the OIDs.
  1437. cOidsReturn - Count of aOidsReturn
  1438. aOidsReturn - Array of OIDs the process is no longer using.
  1439. cOids - Count of aOids
  1440. aOids - The OIDs to register within the OXID.
  1441. pOidsAllocate - Contains the number of OIDs actually allocated
  1442. when this function returns success.
  1443. Return Value:
  1444. OR_OK (0) - Success.
  1445. OR_PARTIAL_UPDATE - No all elements in aStatus are 0.
  1446. OR_NOMEM - OXID or one or more OIDs
  1447. --*/
  1448. {
  1449. ORSTATUS status = OR_OK;
  1450. CServerOxid *pOxid;
  1451. CServerOid *pOid;
  1452. BOOL fPartial = FALSE;
  1453. CProcess *pProcess = ReferenceProcess(phProcess);
  1454. ASSERT(pProcess);
  1455. CheckLocalSecurity(hClient, pProcess);
  1456. // Parameter validation
  1457. if (!poxidServer ||
  1458. !(cOidsReturn > 0 ? (aOidsReturn != NULL) : TRUE) ||
  1459. !((cOids > 0 ? (aOids != NULL) : TRUE) ||
  1460. !pOidsAllocated))
  1461. {
  1462. return OR_BADPARAM;
  1463. }
  1464. gpServerLock->LockExclusive();
  1465. CIdKey oxidkey(*poxidServer);
  1466. pOxid = (CServerOxid *)gpServerOxidTable->Lookup(oxidkey);
  1467. if (0 == pOxid)
  1468. {
  1469. gpServerLock->UnlockExclusive();
  1470. status = OR_BADOXID;
  1471. return(status);
  1472. }
  1473. if (cOidsReturn)
  1474. {
  1475. // free the Oids returned
  1476. FreeServerOids(pProcess, cOidsReturn, aOidsReturn);
  1477. }
  1478. *pOidsAllocated = 0;
  1479. for (ULONG i = 0; i < cOids; i++)
  1480. {
  1481. pOid = new CServerOid(pOxid);
  1482. if (0 != pOid)
  1483. {
  1484. (*pOidsAllocated)++;
  1485. aOids[i] = pOid->Id();
  1486. gpServerOidTable->Add(pOid);
  1487. // The server doesn't want to keep the OID alive.
  1488. // This will cause the OID to rundown in six minutes
  1489. // unless a set references it in the meantime...
  1490. pOid->Release();
  1491. }
  1492. else
  1493. {
  1494. break;
  1495. }
  1496. }
  1497. gpServerLock->UnlockExclusive();
  1498. ASSERT(status == OR_OK);
  1499. return(status);
  1500. }
  1501. error_status_t
  1502. _ServerFreeOXIDAndOIDs(
  1503. IN handle_t hClient,
  1504. IN PHPROCESS phProcess,
  1505. IN OXID oxidServer,
  1506. IN ULONG cOids,
  1507. IN OID aOids[])
  1508. {
  1509. CServerOxid *pOxid;
  1510. CServerOid *pOid;
  1511. CProcess *pProcess = ReferenceProcess(phProcess);
  1512. ORSTATUS status;
  1513. UINT i;
  1514. ASSERT(pProcess);
  1515. CheckLocalSecurity(hClient, pProcess);
  1516. // Parameter validation
  1517. if (!(cOids > 0 ? (aOids != NULL) : TRUE))
  1518. return OR_BADPARAM;
  1519. gpServerLock->LockExclusive();
  1520. CIdKey oxidkey(oxidServer);
  1521. pOxid = (CServerOxid *)gpServerOxidTable->Lookup(oxidkey);
  1522. if (0 != pOxid)
  1523. {
  1524. if (pProcess->RemoveOxid(pOxid) == TRUE)
  1525. {
  1526. // Found the OXID and this caller owns it.
  1527. status = OR_OK;
  1528. }
  1529. else
  1530. {
  1531. // Found but not owned by this caller.
  1532. status = OR_NOACCESS;
  1533. }
  1534. }
  1535. else
  1536. {
  1537. // Oxid not found.
  1538. status = OR_BADOXID;
  1539. }
  1540. // Note pOxid maybe invalid once the last OID is removed.
  1541. if (status == OR_OK)
  1542. {
  1543. for (i = 0; i < cOids; i++)
  1544. {
  1545. CIdKey key(aOids[i]); // PERF REVIEW
  1546. pOid = (CServerOid *)gpServerOidTable->Lookup(key);
  1547. if ( (0 != pOid)
  1548. && (pOid->IsRunningDown() == FALSE)
  1549. && (pOid->GetOxid() == pOxid) )
  1550. {
  1551. if (pOid->References() == 0)
  1552. {
  1553. // Unreferenced by any sets; run it down now..
  1554. pOid->Remove();
  1555. pOid->SetRundown(TRUE);
  1556. delete pOid;
  1557. }
  1558. // else - marking it as Free() not need as Oxid is
  1559. // now marked as not running.
  1560. }
  1561. else
  1562. {
  1563. ASSERT(pOid == 0 || pOxid == pOid->GetOxid());
  1564. }
  1565. }
  1566. }
  1567. gpServerLock->UnlockExclusive();
  1568. return(status);
  1569. }
  1570. //
  1571. // Manager (server-side) calls to the remote OR interface. objex.idl
  1572. //
  1573. error_status_t
  1574. _ResolveOxid(
  1575. IN handle_t hRpc,
  1576. IN OXID *poxid,
  1577. IN USHORT cRequestedProtseqs,
  1578. IN USHORT aRequestedProtseqs[],
  1579. OUT DUALSTRINGARRAY **ppdsaOxidBindings,
  1580. OUT IPID *pipidRemUnknown,
  1581. OUT DWORD *pAuthnHint
  1582. )
  1583. {
  1584. COMVERSION ComVersion;
  1585. // just forward to the new manager routine (parameter
  1586. // validation done by callee)
  1587. return _ResolveOxid2(hRpc,
  1588. poxid,
  1589. cRequestedProtseqs,
  1590. aRequestedProtseqs,
  1591. ppdsaOxidBindings,
  1592. pipidRemUnknown,
  1593. pAuthnHint,
  1594. &ComVersion);
  1595. }
  1596. //
  1597. // Manager (server-side) calls to the remote OR interface. objex.idl
  1598. //
  1599. error_status_t
  1600. _ResolveOxid2(
  1601. IN handle_t hRpc,
  1602. IN OXID *poxid,
  1603. IN USHORT cRequestedProtseqs,
  1604. IN USHORT aRequestedProtseqs[],
  1605. OUT DUALSTRINGARRAY **ppdsaOxidBindings,
  1606. OUT IPID *pipidRemUnknown,
  1607. OUT DWORD *pAuthnHint,
  1608. OUT COMVERSION *pComVersion
  1609. )
  1610. {
  1611. ORSTATUS status;
  1612. BOOL fDidLazy;
  1613. CServerOxid *pServerOxid;
  1614. OXID_INFO oxidInfo;
  1615. // Parameter validation. Note that it would be exceedingly odd for
  1616. // a client to request zero protseqs, but we handle this anyway.
  1617. if (!poxid ||
  1618. !(cRequestedProtseqs > 0 ? (aRequestedProtseqs != NULL) : TRUE) ||
  1619. !ppdsaOxidBindings || !pipidRemUnknown ||
  1620. !pipidRemUnknown || !pAuthnHint || !pComVersion)
  1621. {
  1622. return OR_BADPARAM;
  1623. }
  1624. oxidInfo.psa = 0;
  1625. // No security check required (possible?). OXID info is not private.
  1626. #if DBG
  1627. UINT fLocal;
  1628. status = I_RpcBindingIsClientLocal(hRpc, &fLocal);
  1629. if (status != OR_OK)
  1630. {
  1631. fLocal = FALSE;
  1632. }
  1633. ASSERT(fLocal == FALSE); // Shouldn't be called locally...
  1634. #endif
  1635. fDidLazy = FALSE;
  1636. // intersect allowed protseqs with those
  1637. // requested by the client.
  1638. //
  1639. // NOTE: we are modifying memory passed in. This will not cause side
  1640. // effects because this call is always in the context of an RPC, and
  1641. // aRequestedProtseqs is an IN parameter and therefore will not change
  1642. // in the calling process.
  1643. gpClientLock->LockExclusive();
  1644. USHORT cAllowedProtseqs = 0;
  1645. for (USHORT iReqProtseq=0; iReqProtseq < cRequestedProtseqs; iReqProtseq++)
  1646. {
  1647. for (USHORT iAllowProtseq=0; iAllowProtseq < cMyProtseqs; iAllowProtseq++)
  1648. {
  1649. if (aRequestedProtseqs[iReqProtseq] == aMyProtseqs[iAllowProtseq])
  1650. {
  1651. // this protocol is in the allowed list, shift it up
  1652. // if necessary.
  1653. aRequestedProtseqs[cAllowedProtseqs] = aRequestedProtseqs[iReqProtseq];
  1654. cAllowedProtseqs++;
  1655. break;
  1656. }
  1657. }
  1658. }
  1659. cRequestedProtseqs = cAllowedProtseqs;
  1660. gpClientLock->UnlockExclusive();
  1661. gpServerLock->LockShared();
  1662. for (;;)
  1663. {
  1664. CIdKey key(*poxid);
  1665. pServerOxid = (CServerOxid *)gpServerOxidTable->Lookup(key);
  1666. if (!pServerOxid)
  1667. {
  1668. status = OR_BADOXID;
  1669. break;
  1670. }
  1671. status = pServerOxid->GetRemoteInfo(&oxidInfo,
  1672. cRequestedProtseqs,
  1673. aRequestedProtseqs);
  1674. // Work around: original intersection of clients requested protocols
  1675. // with this SCM's did'nt match. But we know that client
  1676. // does'nt send it's entire set, just one so break here
  1677. //
  1678. // Note: W2K sends all protocols on both activations and ResolveOxid
  1679. // calls; NT4 only sends one protocol on activations, and all on
  1680. // ResolveOxid calls.
  1681. //
  1682. if ((cRequestedProtseqs == 0) && (status == OR_I_NOPROTSEQ))
  1683. {
  1684. break;
  1685. }
  1686. if ( status == OR_I_NOPROTSEQ
  1687. && FALSE == fDidLazy )
  1688. {
  1689. // Ask the server to start listening, but only try this once.
  1690. fDidLazy = TRUE;
  1691. status =
  1692. pServerOxid->LazyUseProtseq(cRequestedProtseqs,
  1693. aRequestedProtseqs
  1694. );
  1695. ASSERT(gpServerLock->HeldExclusive()); // Changed during UseProtseq!
  1696. if (status == OR_OK)
  1697. {
  1698. continue;
  1699. }
  1700. }
  1701. else if (status == OR_I_NOPROTSEQ)
  1702. {
  1703. // We didn't manage to use a matching protseq.
  1704. // Since we can call on any protocal this is possible
  1705. KdPrintEx((DPFLTR_DCOMSS_ID,
  1706. DPFLTR_WARNING_LEVEL,
  1707. "OR: Failed to use a matching protseq: %p %p\n",
  1708. pServerOxid,
  1709. aRequestedProtseqs));
  1710. status = OR_NOSERVER;
  1711. }
  1712. break;
  1713. }
  1714. gpServerLock->Unlock();
  1715. if (status == OR_OK)
  1716. {
  1717. *pipidRemUnknown = oxidInfo.ipidRemUnknown;
  1718. *ppdsaOxidBindings = oxidInfo.psa;
  1719. *pAuthnHint = oxidInfo.dwAuthnHint;
  1720. *pComVersion = oxidInfo.version;
  1721. }
  1722. else
  1723. {
  1724. // Work around: original intersection of clients requested protocols
  1725. // with this SCM's did'nt match. But we know that client
  1726. // does'nt send it's entire set, just one, so send back
  1727. // an empty set but o.k activation result for bindings
  1728. //
  1729. // Note: W2K sends all protocols on both activations and ResolveOxid
  1730. // calls; NT4 only sends one protocol on activations, and all on
  1731. // ResolveOxid calls.
  1732. //
  1733. if ((cRequestedProtseqs == 0) && (status == OR_I_NOPROTSEQ))
  1734. {
  1735. *pipidRemUnknown = oxidInfo.ipidRemUnknown;
  1736. *ppdsaOxidBindings = NULL;
  1737. *pAuthnHint = oxidInfo.dwAuthnHint;
  1738. *pComVersion = oxidInfo.version;
  1739. status = OR_OK;
  1740. }
  1741. }
  1742. return(status);
  1743. }
  1744. error_status_t
  1745. _SimplePing(
  1746. IN handle_t hRpc,
  1747. IN SETID *pSetId
  1748. )
  1749. {
  1750. ORSTATUS status;
  1751. CServerSet *pServerSet;
  1752. BOOL fShared = TRUE;
  1753. // Parameter validation
  1754. if (!pSetId)
  1755. return OR_BADPARAM;
  1756. if (*pSetId == 0)
  1757. {
  1758. KdPrintEx((DPFLTR_DCOMSS_ID,
  1759. DPFLTR_WARNING_LEVEL,
  1760. "Client %p simple pinged with a setid of 0\n",
  1761. hRpc,
  1762. pSetId));
  1763. return(OR_BADSET);
  1764. }
  1765. gpServerLock->LockShared();
  1766. pServerSet = (CServerSet *)gpServerSetTable->Lookup(*pSetId);
  1767. if (pServerSet)
  1768. {
  1769. fShared = pServerSet->Ping(TRUE);
  1770. // The lock maybe exclusive now.
  1771. status = OR_OK;
  1772. }
  1773. else
  1774. {
  1775. status = OR_BADSET;
  1776. }
  1777. // See if another set in the table needs to rundown.
  1778. // PERF REVIEW - how often should I do this? 0 mod 4?
  1779. // Similar code in worker threads.
  1780. ID setid = gpServerSetTable->CheckForRundowns();
  1781. if (setid)
  1782. {
  1783. if (fShared)
  1784. {
  1785. gpServerLock->ConvertToExclusive();
  1786. fShared = FALSE;
  1787. }
  1788. gpServerSetTable->RundownSetIfNeeded(setid);
  1789. }
  1790. gpServerLock->Unlock();
  1791. return(status);
  1792. }
  1793. error_status_t
  1794. ComplexPingInternal(
  1795. IN handle_t hRpc,
  1796. IN SETID *pSetId,
  1797. IN USHORT SequenceNum,
  1798. IN ULONG cAddToSet,
  1799. IN ULONG cDelFromSet,
  1800. IN OID AddToSet[],
  1801. IN OID DelFromSet[],
  1802. OUT USHORT *pPingBackoffFactor
  1803. )
  1804. /*++
  1805. Routine Description:
  1806. Processes a complex (delta to set) ping for a given set. This call
  1807. will create the set if necessary. The call will only be processed
  1808. if the caller is in fact the creator of the set.
  1809. algorithm:
  1810. if set is not allocated
  1811. lookup security info if possible
  1812. allocate set
  1813. else
  1814. lookup set
  1815. if found or created a set
  1816. do a standard ping, updating time stamp and sequence number.
  1817. else return failure.
  1818. if oids to add, add each one.
  1819. ignore unknown OIDs
  1820. if resource allocation fails, abort.
  1821. if oids to delete, process each one.
  1822. ignore unknown OIDs
  1823. if resource failure in adds, return OR_BADOID
  1824. else return success.
  1825. Arguments:
  1826. hRpc - Handle (SCONN/SCALL) of client. Used to check security. If it is
  1827. NULL the call is local and is assumed to be secure.
  1828. REVIEW:
  1829. Since the OR _only_ uses NT system security providers it is assumed
  1830. that impersonation will work. Other security providers will not.
  1831. We need a generic way to ask for a token and compare tokens in a
  1832. security provider independent way.
  1833. pSetId - The setid to ping. If it is NULL a new set will be created,
  1834. otherwise, it is assumed to be a set previously allocated by a
  1835. call with a NULL setid to this server.
  1836. SequenceNum - A sequence number shared between the client and server
  1837. to make sure old and out-of-order pings are not processed in a
  1838. non-healthy way. Note that pings are usually datagram RPC calls
  1839. which are marked as idempotent.
  1840. cAddToSet
  1841. cDelFromSet - The count of element in AddTo/DelFromSet parameter.
  1842. AddToSet
  1843. DelFromSet - OID mostly likly belonging to servers on this machine
  1844. to Add/Remove from the set of OIDs in use by this client.
  1845. pPingBackoffFactor - Maybe set by servers which want to reduce the
  1846. ping load on the server. Serves only as a HINT for the client.
  1847. Clients do not to ping more offten then:
  1848. (1<<*pPingBackoffFactor)*BasePingInterval seconds.
  1849. Clients may choose to assume this parameter is always 0.
  1850. Return Value:
  1851. OR_OK - completed normally
  1852. OR_BADSET - non-zero and unknown setid.
  1853. OR_NOMEM - unable to allocate a resource. Note that
  1854. on the first ping a set maybe allocated (setid is non-zero
  1855. after call) but some OIDs failed to be allocated.
  1856. OR_BADOID - everything went okay, but some OIDs added where
  1857. not recognized.
  1858. --*/
  1859. {
  1860. CServerSet *pServerSet;
  1861. BOOL fProcessPing;
  1862. BOOL fBad = FALSE;
  1863. PSID psid = 0;
  1864. ORSTATUS status = OR_OK;
  1865. // Parameter validation.
  1866. if (!pSetId ||
  1867. !(cAddToSet > 0 ? (AddToSet != NULL) : TRUE) ||
  1868. !(cDelFromSet > 0 ? (DelFromSet != NULL) : TRUE) ||
  1869. !pPingBackoffFactor)
  1870. {
  1871. return OR_BADPARAM;
  1872. }
  1873. gpServerLock->LockExclusive();
  1874. // Lookup the set
  1875. if (0 != *pSetId)
  1876. {
  1877. pServerSet = (CServerSet *)gpServerSetTable->Lookup(*pSetId);
  1878. if (0 == pServerSet)
  1879. {
  1880. status = OR_BADSET;
  1881. }
  1882. if (status == OR_OK)
  1883. {
  1884. if (pServerSet->CheckSecurity(hRpc) != TRUE)
  1885. {
  1886. KdPrintEx((DPFLTR_DCOMSS_ID,
  1887. DPFLTR_WARNING_LEVEL,
  1888. "OR: Security check on set failed! (%d)\n",
  1889. GetLastError()));
  1890. status = OR_NOACCESS;
  1891. }
  1892. }
  1893. }
  1894. else if (hRpc == 0)
  1895. {
  1896. // Local client
  1897. psid = 0;
  1898. pServerSet = gpServerSetTable->Allocate(SequenceNum,
  1899. psid,
  1900. hRpc == 0,
  1901. *pSetId);
  1902. if (0 == pServerSet)
  1903. status = OR_NOMEM;
  1904. else
  1905. status = OR_OK;
  1906. }
  1907. else
  1908. {
  1909. HANDLE hT;
  1910. BOOL f;
  1911. // Unallocated set, lookup security info and allocate the set.
  1912. KdPrintEx((DPFLTR_DCOMSS_ID,
  1913. DPFLTR_INFO_LEVEL,
  1914. "OR: New client started pinging: %p\n",
  1915. hRpc));
  1916. status = RpcImpersonateClient(hRpc);
  1917. if (status == RPC_S_OK)
  1918. {
  1919. f = OpenThreadToken(GetCurrentThread(),
  1920. TOKEN_IMPERSONATE | TOKEN_QUERY,
  1921. TRUE,
  1922. &hT);
  1923. if (!f)
  1924. {
  1925. status = GetLastError();
  1926. }
  1927. else
  1928. {
  1929. status = RPC_S_OK;
  1930. }
  1931. }
  1932. if (status != RPC_S_OK)
  1933. {
  1934. KdPrintEx((DPFLTR_DCOMSS_ID,
  1935. DPFLTR_WARNING_LEVEL,
  1936. "OR: Unsecure client started pinging: %d %p\n",
  1937. status,
  1938. hRpc));
  1939. status = OR_OK;
  1940. }
  1941. else
  1942. {
  1943. ULONG needed = DEBUG_MIN(1, 24);
  1944. PTOKEN_USER ptu;
  1945. do
  1946. {
  1947. ptu = (PTOKEN_USER)alloca(needed);
  1948. ASSERT(ptu);
  1949. f = GetTokenInformation(hT,
  1950. TokenUser,
  1951. (PBYTE)ptu,
  1952. needed,
  1953. &needed);
  1954. } while ( f == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
  1955. if (f)
  1956. {
  1957. ASSERT(needed > sizeof(SID));
  1958. psid = new(needed - sizeof(SID)) SID;
  1959. if (psid)
  1960. {
  1961. f = CopySid(needed, psid, ptu->User.Sid);
  1962. ASSERT(f == TRUE);
  1963. }
  1964. else
  1965. {
  1966. status = OR_NOMEM;
  1967. }
  1968. }
  1969. else
  1970. {
  1971. KdPrintEx((DPFLTR_DCOMSS_ID,
  1972. DPFLTR_WARNING_LEVEL,
  1973. "OR: Error %d from GetTokenInformation\n",
  1974. GetLastError()));
  1975. ASSERT(0);
  1976. // Why did this happen. Either return failure to client or
  1977. // continue and make the set unsecure.
  1978. status = OR_NOMEM;
  1979. }
  1980. CloseHandle(hT);
  1981. }
  1982. // Allocate the set
  1983. if (status == OR_OK)
  1984. {
  1985. ASSERT(gpPingSetQuotaManager);
  1986. if (gpPingSetQuotaManager->IsUserQuotaExceeded(psid))
  1987. status = OR_NORESOURCE;
  1988. if (OR_OK == status)
  1989. {
  1990. if (!gpPingSetQuotaManager->ManageQuotaForUser(psid, TRUE))
  1991. {
  1992. status = OR_NOMEM;
  1993. }
  1994. else
  1995. {
  1996. pServerSet = gpServerSetTable->Allocate(SequenceNum,
  1997. psid,
  1998. hRpc == 0,
  1999. *pSetId);
  2000. if (0 == pServerSet)
  2001. {
  2002. gpPingSetQuotaManager->ManageQuotaForUser(psid, FALSE);
  2003. status = OR_NOMEM;
  2004. }
  2005. }
  2006. }
  2007. }
  2008. }
  2009. if (status != OR_OK)
  2010. {
  2011. VALIDATE((status, OR_NOMEM, OR_BADSET, OR_NOACCESS, 0));
  2012. gpServerLock->UnlockExclusive();
  2013. return(status);
  2014. }
  2015. ASSERT(pServerSet);
  2016. fProcessPing = pServerSet->CheckAndUpdateSequenceNumber(SequenceNum);
  2017. if (fProcessPing)
  2018. {
  2019. // Do regular ping
  2020. pServerSet->Ping(FALSE);
  2021. *pPingBackoffFactor = 0;
  2022. // Process Add's
  2023. for (int i = cAddToSet; i ; i--)
  2024. {
  2025. status = pServerSet->AddObject(AddToSet[i - 1]);
  2026. if (status == OR_BADOID)
  2027. {
  2028. fBad = TRUE;
  2029. }
  2030. else if ( status != OR_OK )
  2031. {
  2032. break;
  2033. }
  2034. }
  2035. // Process Deletes - even some adds failed!
  2036. for (i = cDelFromSet; i; i--)
  2037. {
  2038. // Removing can't fail, no way to cleanup.
  2039. pServerSet->RemoveObject(DelFromSet[i - 1]);
  2040. }
  2041. }
  2042. gpServerLock->UnlockExclusive();
  2043. if (status == OR_OK && fBad)
  2044. {
  2045. return(OR_BADOID);
  2046. }
  2047. return(status);
  2048. }
  2049. error_status_t
  2050. _ComplexPing(
  2051. IN handle_t hRpc,
  2052. IN SETID *pSetId,
  2053. IN USHORT SequenceNum,
  2054. IN USHORT cAddToSet,
  2055. IN USHORT cDelFromSet,
  2056. IN OID AddToSet[],
  2057. IN OID DelFromSet[],
  2058. OUT USHORT *pPingBackoffFactor
  2059. )
  2060. /*--
  2061. Routine Description:
  2062. This is the exposed RPC entry point for this function. We
  2063. simply call the internal function below. See description
  2064. for ComplexPingInternal. The reason for a separate function
  2065. is because the RPC method is typed to to take USHORT's, but
  2066. internally it is more convienent to pass ULONG's.
  2067. Arguments:
  2068. See argument list for ComplexPingInternal
  2069. --*/
  2070. {
  2071. return ComplexPingInternal(hRpc,
  2072. pSetId,
  2073. SequenceNum,
  2074. cAddToSet,
  2075. cDelFromSet,
  2076. AddToSet,
  2077. DelFromSet,
  2078. pPingBackoffFactor);
  2079. }
  2080. error_status_t
  2081. _ServerAlive(
  2082. RPC_ASYNC_STATE *pAsync,
  2083. RPC_BINDING_HANDLE hServer
  2084. )
  2085. /*++
  2086. Routine Description:
  2087. Ping API for the client to validate a binding. Used when the client
  2088. is unsure of the correct binding for the server. (Ie. If the server
  2089. has multiple IP addresses).
  2090. Arguments:
  2091. hServer - RPC call binding
  2092. Return Value:
  2093. OR_OK
  2094. --*/
  2095. {
  2096. error_status_t RetVal = OR_OK;
  2097. RPC_STATUS rpcstatus;
  2098. RegisterAuthInfoIfNecessary();
  2099. rpcstatus = RpcAsyncCompleteCall(pAsync, &RetVal);
  2100. return (rpcstatus == RPC_S_OK) ? RetVal : rpcstatus;
  2101. }
  2102. error_status_t
  2103. _ServerAlive2(
  2104. RPC_ASYNC_STATE *pAsync,
  2105. RPC_BINDING_HANDLE hServer,
  2106. COMVERSION *pComVersion,
  2107. DUALSTRINGARRAY **ppdsaOrBindings,
  2108. DWORD *pReserved
  2109. )
  2110. /*++
  2111. Routine Description:
  2112. Ping API for the client to validate a binding. Used when the client
  2113. is unsure of the correct binding or authentication service for the server.
  2114. (Ie. If the server has multiple IP addresses).
  2115. Arguments:
  2116. hServer - RPC call binding
  2117. Return Value:
  2118. OR_OK
  2119. --*/
  2120. {
  2121. error_status_t RetVal = OR_OK;
  2122. RPC_STATUS rpcstatus = RPC_S_OK;
  2123. // Parameter validation
  2124. if (!pComVersion || !ppdsaOrBindings || !pReserved)
  2125. return OR_BADPARAM;
  2126. RegisterAuthInfoIfNecessary();
  2127. pComVersion->MajorVersion = COM_MAJOR_VERSION;
  2128. pComVersion->MinorVersion = COM_MINOR_VERSION;
  2129. *pReserved = 0;
  2130. RetVal = CopyMyOrBindings(ppdsaOrBindings, NULL);
  2131. if (RetVal == OR_OK)
  2132. {
  2133. rpcstatus = RpcAsyncCompleteCall(pAsync, &RetVal);
  2134. }
  2135. return (rpcstatus == RPC_S_OK) ? RetVal : rpcstatus;
  2136. }
  2137. void __RPC_USER PHPROCESS_rundown(LPVOID ProcessKey)
  2138. {
  2139. CProcess *pProcess = ReferenceProcess(ProcessKey);
  2140. KdPrintEx((DPFLTR_DCOMSS_ID,
  2141. DPFLTR_INFO_LEVEL,
  2142. "OR: Client died\n"));
  2143. ASSERT(pProcess);
  2144. //
  2145. // This revokes OLE class registrations which were not revoked by this
  2146. // dead process. This must be done here rather then CProcess::Rundown
  2147. // because these things have references to the CProcess object.
  2148. //
  2149. pProcess->RevokeClassRegs();
  2150. pProcess->Cleanup();
  2151. ReleaseProcess(pProcess);
  2152. return;
  2153. }
  2154. //+---------------------------------------------------------------------------
  2155. //
  2156. // Function: CRpcSecurityCallbackManager::RegisterForRpcAuthSvcCallBack
  2157. //
  2158. // Synopsis: Register the security callback with RPC. A return value of
  2159. // TRUE means the calling thread may make a call on the supplied
  2160. // binding handle, then call the the RetrieveAuthSvc method
  2161. // for the negotiated authentication svc used on the rpc call.
  2162. //
  2163. // Parameters: hRpc -- the binding handle for which the calling thread wants
  2164. // the negotiated auth. svc.
  2165. //
  2166. // Algorithm: Register the security callback with RPC. Create a new list
  2167. // element to represent this particular callback, and add to to
  2168. // the list of callbacks.
  2169. //
  2170. // Notes: There is a limitation: a thread can only be registered for one
  2171. // callback at a time. This should be ok for expected usage.
  2172. //
  2173. //----------------------------------------------------------------------------
  2174. BOOL CRpcSecurityCallbackManager::RegisterForRpcAuthSvcCallBack(handle_t hRpc)
  2175. {
  2176. RPC_STATUS status;
  2177. CRpcSecurityCallback* pNewCallback;
  2178. ASSERT(hRpc != 0 && "Callbacks are meant to be used only for remote calls!");
  2179. // Create a new list element to represent this callback
  2180. pNewCallback = new CRpcSecurityCallback(hRpc, GetCurrentThreadId());
  2181. if (!pNewCallback)
  2182. return FALSE; // out-of-memory, not much we can do
  2183. // Try to register the callback
  2184. status = RpcBindingSetOption(hRpc,
  2185. RPC_C_OPT_SECURITY_CALLBACK,
  2186. (ULONG_PTR)CRpcSecurityCallbackManager::RpcSecurityCallbackFunction);
  2187. ASSERT(status == RPC_S_OK); // this should never fail AFAIK
  2188. if (status != RPC_S_OK)
  2189. {
  2190. // again, not much we can do
  2191. delete pNewCallback;
  2192. return FALSE;
  2193. }
  2194. // Okay now we got all we need; add it to the list
  2195. _plistlock->LockExclusive();
  2196. _CallbackList.Insert(pNewCallback);
  2197. _plistlock->UnlockExclusive();
  2198. return TRUE;
  2199. };
  2200. //+---------------------------------------------------------------------------
  2201. //
  2202. // Function: CRpcSecurityCallbackManager::GetAuthSvcAndTurnOffCallback
  2203. //
  2204. // Synopsis: Tries to retrieve the negotiated authentication service for the
  2205. // call just completed on the supplied binding handle. Also
  2206. // turns off callbacks on the binding handle.
  2207. //
  2208. // Parameters: pusAuthSvc -- if the rpc call was made, this must be a valid ptr;
  2209. // if that is the case, then upon a return value of TRUE, it
  2210. // will contain the auth. svc used for the call; a value of NULL
  2211. // passed here means the caller doesn't care about the result, he
  2212. // just wants things cleaned up (typically this means either the
  2213. // call failed or was never made).
  2214. //
  2215. // Returns: TRUE -- if everything worked
  2216. // FALSE -- something is wrong or you passed a NULL pusAuthSvc
  2217. //
  2218. // Algorithm: Use the caller's thread id to search for the callback result
  2219. //
  2220. //----------------------------------------------------------------------------
  2221. BOOL CRpcSecurityCallbackManager::GetAuthSvcAndTurnOffCallback(handle_t hRpc, USHORT* pusAuthSvc)
  2222. {
  2223. RPC_STATUS status;
  2224. CRpcSecurityCallback* pCallback;
  2225. DWORD dwCurrentThread = GetCurrentThreadId();
  2226. _plistlock->LockExclusive();
  2227. // No matter what happens after this, we should turn off callbacks for the handle
  2228. TurnOffCallback(hRpc);
  2229. // Look for the callback result for the calling thread
  2230. for (pCallback = (CRpcSecurityCallback*)_CallbackList.First();
  2231. pCallback;
  2232. pCallback = (CRpcSecurityCallback*)pCallback->Next() )
  2233. {
  2234. if (dwCurrentThread == pCallback->GetRegisteredThreadId())
  2235. {
  2236. // found it
  2237. break;
  2238. }
  2239. };
  2240. // Given the normal usage of this stuff, we should always find a result
  2241. ASSERT(pCallback && "Didn't find rpc sec. callback result; this is unexpected");
  2242. if (!pCallback)
  2243. {
  2244. _plistlock->UnlockExclusive();
  2245. return FALSE;
  2246. };
  2247. if (pusAuthSvc)
  2248. {
  2249. ASSERT(pCallback->WasAuthSvcSet() && "Caller is retrieving auth svc before it was set");
  2250. *pusAuthSvc = pCallback->GetAuthSvcResult();
  2251. }
  2252. // The callback's work is done, so remove it from the list and delete it
  2253. _CallbackList.Remove(pCallback);
  2254. delete pCallback;
  2255. // Check list to see if other threads also had registered callbacks on the
  2256. // same handle. We will record the result for them in case we turned off the
  2257. // callback above before they got recorded. In fact, any time two or more threads
  2258. // register for a callback on the same handle we will hit this scenario.
  2259. //
  2260. // I don't think this is 100% foolproof, but it constitutes a best-faith effort
  2261. //
  2262. for (pCallback = (CRpcSecurityCallback*)_CallbackList.First();
  2263. pCallback;
  2264. pCallback = (CRpcSecurityCallback*)pCallback->Next() )
  2265. {
  2266. if (hRpc == pCallback->RegisteredHandle())
  2267. {
  2268. // found a match
  2269. ASSERT(dwCurrentThread != pCallback->GetRegisteredThreadId());
  2270. pCallback->SetAuthSvc(*pusAuthSvc);
  2271. }
  2272. };
  2273. _plistlock->UnlockExclusive();
  2274. return pusAuthSvc ? (*pusAuthSvc != ERROR_AUTHNSVC_VALUE) : FALSE;
  2275. };
  2276. //+---------------------------------------------------------------------------
  2277. //
  2278. // Function: CRpcSecurityCallbackManager::TurnOffCallback
  2279. //
  2280. // Synopsis: Function to turn off the callback on an rpc binding handle
  2281. //
  2282. // Parameters: hRpc -- the binding handle to turn off callbacks on
  2283. //
  2284. BOOL CRpcSecurityCallbackManager::TurnOffCallback(handle_t hRpc)
  2285. {
  2286. RPC_STATUS status;
  2287. status = RpcBindingSetOption(hRpc,
  2288. RPC_C_OPT_SECURITY_CALLBACK,
  2289. (ULONG_PTR) NULL );
  2290. ASSERT(status == RPC_S_OK && "RpcBindingSetOption failed when turning off security callback");
  2291. return TRUE;
  2292. };
  2293. //+---------------------------------------------------------------------------
  2294. //
  2295. // Function: CRpcSecurityCallbackManager::StoreCallbackResult
  2296. //
  2297. // Synopsis: This is a helper function for the callback function; no one
  2298. // else should use it obviously.
  2299. //
  2300. // Parameters: usAuthSvc -- the negotiated authentication service for the rpc
  2301. // call that the calling thread presumably just completed.
  2302. //
  2303. void CRpcSecurityCallbackManager::StoreCallbackResult(USHORT usAuthSvc)
  2304. {
  2305. CRpcSecurityCallback* pCallback;
  2306. DWORD dwCurrentThread = GetCurrentThreadId();
  2307. // Only need a shared lock for this
  2308. _plistlock->LockShared();
  2309. for (pCallback = (CRpcSecurityCallback*)_CallbackList.First();
  2310. pCallback;
  2311. pCallback = (CRpcSecurityCallback*)pCallback->Next() )
  2312. {
  2313. if (dwCurrentThread == pCallback->GetRegisteredThreadId())
  2314. {
  2315. // found it
  2316. break;
  2317. }
  2318. };
  2319. ASSERT(pCallback); // we should always find it
  2320. pCallback->SetAuthSvc(usAuthSvc);
  2321. _plistlock->UnlockShared();
  2322. return;
  2323. };
  2324. //+---------------------------------------------------------------------------
  2325. //
  2326. // Function: CRpcSecurityCallbackManager::RpcSecurityCallbackFunction
  2327. //
  2328. // Synopsis: This is a callback function; we use this by setting it on a
  2329. // binding handle (using the RPC_C_OPT_SECURITY_CALLBACK option)
  2330. // so that RPC will call us back; this gives us a chance to
  2331. // determine what authentication svc was negotiated when using
  2332. // snego.
  2333. //
  2334. // Parameters: pvContext -- an opaque parameter
  2335. //
  2336. // Algorithm: call I_RpcBindingInqWireIdForSnego (undocumented rpc call)
  2337. // passing it the opaque pvContext parameter.
  2338. //
  2339. // Notes: it would be nice if we could get some info in this callback
  2340. // as to *which* handle the callback is for, but unfortunately
  2341. // kamenm was explicit on this point: pvContext is to remain
  2342. // opaque. :)
  2343. //
  2344. //----------------------------------------------------------------------------
  2345. void RPC_ENTRY CRpcSecurityCallbackManager::RpcSecurityCallbackFunction(void* pvContext)
  2346. {
  2347. RPC_STATUS status;
  2348. UCHAR ucWireId;
  2349. // Call back to get the authsvc:
  2350. status = I_RpcBindingInqWireIdForSnego((RPC_BINDING_HANDLE)pvContext, &ucWireId);
  2351. ASSERT(status != RPC_S_SEC_PKG_ERROR); // RPC should not have called us back
  2352. // in the first place, so assert on this
  2353. ASSERT(status != RPC_S_INVALID_BINDING); // RPC folks say this can be returned for the
  2354. // following reasons: 1) unauthenticated call;
  2355. // 2) invalid context; 3) snego was not used in
  2356. // the first place. None of these should apply
  2357. // to us, so assert
  2358. if (status == RPC_S_OK)
  2359. {
  2360. ASSERT( (ucWireId != RPC_C_AUTHN_GSS_NEGOTIATE) && "We're supposed to get back the real deal not snego");
  2361. gpCRpcSecurityCallbackMgr->StoreCallbackResult(ucWireId);
  2362. }
  2363. else
  2364. {
  2365. // something went wrong (most likely in the lower level security code).
  2366. gpCRpcSecurityCallbackMgr->StoreCallbackResult(ERROR_AUTHNSVC_VALUE);
  2367. }
  2368. return;
  2369. };
  2370. DWORD CPingSetQuotaManager::_dwPerUserPingSetQuota = 1000;
  2371. //+---------------------------------------------------------------------------
  2372. //
  2373. // Function: CPingSetQuotaManager::ManageQuotaForUser
  2374. //
  2375. // Synopsis: This function manages the pingset quota for the user indicated by
  2376. // pSID. fAlloc = TRUE means alloc pingset quota, else deduct quota
  2377. //
  2378. BOOL CPingSetQuotaManager::ManageQuotaForUser(PSID pSid, BOOL fAlloc)
  2379. {
  2380. CUserPingSetCount *pNode = NULL;
  2381. RPC_STATUS status = OR_NOMEM;
  2382. // first see if have the user in the list
  2383. _plistlock->LockExclusive();
  2384. for (pNode = (CUserPingSetCount*)_UserPingSetCountList.First();
  2385. pNode;
  2386. pNode = (CUserPingSetCount*)pNode->Next() )
  2387. {
  2388. if (pNode->IsEqual(pSid))
  2389. {
  2390. // found it
  2391. break;
  2392. }
  2393. };
  2394. if (pNode)
  2395. {
  2396. // Have one, munge count
  2397. if (fAlloc)
  2398. {
  2399. pNode->Increment();
  2400. }
  2401. else
  2402. {
  2403. pNode->Decrement();
  2404. // last ping set for this user, delete node
  2405. if (pNode->GetCount() == 0)
  2406. {
  2407. _UserPingSetCountList.Remove(pNode);
  2408. delete pNode;
  2409. }
  2410. }
  2411. status = OR_OK;
  2412. }
  2413. else
  2414. {
  2415. // Don't have one, add.
  2416. ASSERT( fAlloc == TRUE );
  2417. pNode = new CUserPingSetCount(status, pSid);
  2418. if (pNode && (status == OR_OK) )
  2419. {
  2420. // start with 1
  2421. pNode->Increment();
  2422. _UserPingSetCountList.Insert(pNode);
  2423. }
  2424. }
  2425. _plistlock->UnlockExclusive();
  2426. return (OR_OK == status);
  2427. }
  2428. //+---------------------------------------------------------------------------
  2429. //
  2430. // Function: CPingSetQuotaManager::IsUserQuotaExceeded
  2431. //
  2432. // Synopsis: This function determines if the quota is above limit for a given user
  2433. // pSID. returns TRUE if the limit is reached, FALSE otherwise
  2434. //
  2435. BOOL CPingSetQuotaManager::IsUserQuotaExceeded(PSID pSid)
  2436. {
  2437. CUserPingSetCount *pNode = NULL;
  2438. // first see if have the user in the list
  2439. _plistlock->LockShared();
  2440. for (pNode = (CUserPingSetCount*)_UserPingSetCountList.First();
  2441. pNode;
  2442. pNode = (CUserPingSetCount*)pNode->Next() )
  2443. {
  2444. if (pNode->IsEqual(pSid))
  2445. {
  2446. // found it
  2447. break;
  2448. }
  2449. };
  2450. if (pNode)
  2451. {
  2452. DWORD dw = pNode->GetCount();
  2453. if (dw >= _dwPerUserPingSetQuota )
  2454. {
  2455. _plistlock->UnlockShared();
  2456. return TRUE;
  2457. }
  2458. }
  2459. _plistlock->UnlockShared();
  2460. return FALSE;
  2461. }
  2462. void CPingSetQuotaManager::SetPerUserPingSetQuota(DWORD dwQuota)
  2463. {
  2464. _dwPerUserPingSetQuota = dwQuota;
  2465. }