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.

647 lines
20 KiB

  1. /*++
  2. Copyright (c) 1995-1997 Microsoft Corporation
  3. Module Name:
  4. servers.cxx
  5. Abstract:
  6. Author:
  7. Revision History:
  8. --*/
  9. #include "act.hxx"
  10. // Maximum number of times we will let the server tell us we are busy
  11. #define MAX_BUSY_RETRIES 3
  12. // Maximum number of times we will let the server tell us it has rejected
  13. // the call, and the sleep time between retries.
  14. #define MAX_REJECT_RETRIES 10
  15. #define DELAYTIME_BETWEEN_REJECTS 1500 // 1.5 seconds
  16. extern InterfaceData *AllocateAndCopy(InterfaceData *pifdIn);
  17. BOOL
  18. CServerList::InList(
  19. IN CServerListEntry * pServerListEntry
  20. )
  21. {
  22. CListElement * pEntry;
  23. for ( pEntry = First(); pEntry; pEntry = pEntry->Next() )
  24. if ( pEntry == (CListElement *) pServerListEntry )
  25. return TRUE;
  26. return FALSE;
  27. }
  28. CServerListEntry::CServerListEntry(
  29. IN CServerTableEntry * pServerTableEntry,
  30. IN CProcess * pServerProcess,
  31. IN IPID ipid,
  32. IN UCHAR Context,
  33. IN UCHAR State,
  34. IN UCHAR SubContext
  35. )
  36. {
  37. _pServerTableEntry = pServerTableEntry;
  38. _pServerTableEntry->Reference();
  39. // This process was already validated in ServerRegisterClsid.
  40. _pServerProcess = ReferenceProcess( pServerProcess, TRUE );
  41. _ipid = ipid;
  42. _hRpc = 0;
  43. _hRpcAnonymous = 0;
  44. _Context = Context;
  45. _SubContext = SubContext;
  46. _State = State;
  47. _NumCalls = 0;
  48. _lThreadToken = 0;
  49. _lSingleUseStatus = SINGLE_USE_AVAILABLE;
  50. _dwServerFaults = 0;
  51. //
  52. // Get a unique registration number without taking a global lock.
  53. // Remember that gRegisterKey uses interlocked increment for ++.
  54. //
  55. for (;;)
  56. {
  57. _RegistrationKey = (DWORD) gRegisterKey;
  58. gRegisterKey++;
  59. if ( (_RegistrationKey + 1) == (DWORD) gRegisterKey )
  60. break;
  61. }
  62. }
  63. CServerListEntry::~CServerListEntry()
  64. {
  65. ASSERT( (Previous() == NULL) && (Next() == NULL) );
  66. ASSERT(_lSingleUseStatus == SINGLE_USE_AVAILABLE ||
  67. _lSingleUseStatus == SINGLE_USE_TAKEN);
  68. ReleaseProcess( _pServerProcess );
  69. _pServerTableEntry->Release();
  70. if ( _hRpc )
  71. RpcBindingFree( &_hRpc );
  72. if ( _hRpcAnonymous )
  73. RpcBindingFree( &_hRpcAnonymous );
  74. }
  75. HANDLE
  76. CServerListEntry::RpcHandle(
  77. IN BOOL bAnonymous
  78. )
  79. {
  80. HANDLE hRpc;
  81. DWORD Status;
  82. if ( ! bAnonymous && _hRpc )
  83. return _hRpc;
  84. if ( bAnonymous && _hRpcAnonymous )
  85. return _hRpcAnonymous;
  86. hRpc = _pServerProcess->GetBindingHandle();
  87. if ( ! hRpc )
  88. return 0;
  89. Status = RpcBindingSetObject( hRpc, (GUID *) &_ipid );
  90. if ( bAnonymous && (ERROR_SUCCESS == Status) )
  91. {
  92. RPC_SECURITY_QOS Qos;
  93. //
  94. // Note that we indicate impersonation level of identify because
  95. // anonymous is not supported. However specifing no authentication
  96. // in SetAuthInfo will keep the server from impersonating us.
  97. //
  98. Qos.Version = RPC_C_SECURITY_QOS_VERSION;
  99. Qos.Capabilities = RPC_C_QOS_CAPABILITIES_DEFAULT;
  100. Qos.ImpersonationType = RPC_C_IMP_LEVEL_IDENTIFY;
  101. Qos.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC;
  102. Status = RpcBindingSetAuthInfoEx(
  103. hRpc,
  104. NULL,
  105. RPC_C_AUTHN_LEVEL_NONE,
  106. RPC_C_AUTHN_WINNT,
  107. NULL,
  108. 0,
  109. &Qos );
  110. }
  111. if ( Status != ERROR_SUCCESS)
  112. {
  113. RpcBindingFree( &hRpc );
  114. hRpc = 0;
  115. }
  116. else
  117. {
  118. if ( ! bAnonymous )
  119. _hRpc = hRpc;
  120. else
  121. _hRpcAnonymous = hRpc;
  122. }
  123. return hRpc;
  124. }
  125. BOOL
  126. CServerListEntry::Match(
  127. IN CToken * pToken,
  128. IN BOOL bRemoteActivation,
  129. IN BOOL bClientImpersonating,
  130. IN WCHAR* pwszWinstaDesktop,
  131. IN BOOL bSurrogate,
  132. IN LONG lThreadToken,
  133. IN LONG lSessionID,
  134. IN DWORD pid,
  135. IN DWORD dwProcessReqType,
  136. IN DWORD dwFlags
  137. )
  138. {
  139. ASSERT(_lSingleUseStatus == SINGLE_USE_AVAILABLE ||
  140. _lSingleUseStatus == SINGLE_USE_TAKEN);
  141. // If server is suspended, don't use it.
  142. if ((_State & SERVERSTATE_SUSPENDED) && !(dwFlags & MATCHFLAG_ALLOW_SUSPENDED))
  143. return FALSE;
  144. // Is the process represented by this entry retired or suspended?
  145. if (!_pServerProcess->AvailableForActivations())
  146. return FALSE;
  147. // If looking for a surrogate, only allow surrogates
  148. if (bSurrogate && !(_State & SERVERSTATE_SURROGATE))
  149. return FALSE;
  150. // Did a custom activator specify a specific process to use?
  151. if (dwProcessReqType == PRT_USE_THIS)
  152. {
  153. // The rule here is, if we are not the specified process, then we
  154. // bail. If we are the right guy, then that's great but then we
  155. // still need to perform all subsequent checks below. A custom
  156. // activator may not override our normal security checks.
  157. if (_pServerProcess->GetPID() != pid)
  158. return FALSE;
  159. }
  160. // Is this a single-use registration that has already been
  161. // consumed - if so, don't use. We will check this again
  162. // at the bottom after all other checks have been made. But
  163. // doing so here saves us time if there are a lot of these
  164. // servers coming and going.
  165. if (_State & SERVERSTATE_SINGLEUSE)
  166. {
  167. if (_lSingleUseStatus == SINGLE_USE_TAKEN)
  168. return FALSE;
  169. }
  170. // If server is running as a service, no need to go further
  171. if (SERVER_SERVICE == _Context)
  172. return TRUE;
  173. // If server is a runas, might need to do more checking (ie, for
  174. // interactive user servers) below
  175. if (SERVER_RUNAS == _Context)
  176. goto EndMatch;
  177. //
  178. // If we reached here then we are an activate-as-activator server (at
  179. // least til we pass the EndMatch label down below)
  180. //
  181. ASSERT(_Context == SERVER_ACTIVATOR && "Unexpected server context");
  182. //
  183. // If the client is anonymous, then forget it
  184. //
  185. if (!pToken)
  186. return FALSE;
  187. // Notes on activator-as-activator client\server identity & desktop matching:
  188. //
  189. // -- If client was remote, then only client & server identity needs to match
  190. // -- If client was local, and not impersonating, then both client & server
  191. // identity and client\server desktop need to match
  192. // -- If client was local, and impersonating, then client & server identity
  193. // need to match, and token luid's need to match.
  194. //
  195. // The reason for this is that apps from NT4 and before expect that act-as-
  196. // activator servers will always run on the client's desktop. If the server
  197. // has the same identity as the client, this is fine. Trouble arises when the
  198. // client is impersonating, and we put the server on the client's desktop -
  199. // sometimes the newly-launched server will not have permissions to the client's
  200. // desktop, and hence dies a quick death. The above rules are meant to
  201. // work around this, and allow us to simulataneously accommodate both legacy apps
  202. // and new apps that activate objects while impersonating.
  203. if (!bRemoteActivation && !bClientImpersonating)
  204. {
  205. ASSERT(pwszWinstaDesktop);
  206. if (!pwszWinstaDesktop)
  207. return FALSE;
  208. if (lstrcmpW(pwszWinstaDesktop, _pServerProcess->WinstaDesktop()) != 0 )
  209. return FALSE;
  210. }
  211. else if (!bRemoteActivation)
  212. {
  213. // By matching luids in the local\impersonation case, we can
  214. // indirectly try to enforce desktops that way.
  215. if (S_OK != pToken->MatchTokenLuid(_pServerProcess->GetToken()))
  216. return FALSE;
  217. }
  218. //
  219. // If the client isn't us, then forget it (remember, at this point we're still
  220. // an activate-as-activator server)
  221. //
  222. if ( S_OK != pToken->MatchToken2(_pServerProcess->GetToken(), FALSE) )
  223. {
  224. //DbgPrint("RPCSS: ServerListEntry %p: Token did not match.\n", this);
  225. return FALSE;
  226. }
  227. //
  228. // If the client is less trusted than us, then forget it as well.
  229. //
  230. if ( gbSAFERAAAChecksEnabled )
  231. {
  232. if (S_FALSE == pToken->CompareSaferLevels(_pServerProcess->GetToken()))
  233. {
  234. DbgPrint("RPCSS: ServerListEntry %p: SAFER level did not match.\n", this);
  235. return FALSE;
  236. }
  237. }
  238. EndMatch:
  239. BOOL bRet = FALSE;
  240. if (lThreadToken == _lThreadToken)
  241. {
  242. CToken* pServerToken = _pServerProcess->GetToken();
  243. ASSERT(pServerToken && "_pServerProcess did not have an associated token reference");
  244. if (pServerToken != NULL)
  245. {
  246. if (_SubContext == SUB_CONTEXT_RUNAS_INTERACTIVE)
  247. {
  248. if (lSessionID != INVALID_SESSION_ID)
  249. {
  250. // User specified a destination session to use; check that this
  251. // server is in that session
  252. bRet = (pServerToken->MatchSessionID(lSessionID) == S_OK);
  253. }
  254. else
  255. {
  256. // No dest. session specified. The only thing left to check is
  257. // if the user is local then the server we select should be in
  258. // that user's session. If the user is not local, then this
  259. // server must be in session 0 to be a match
  260. if (pToken == NULL)
  261. {
  262. // anonymous client; only thing to make sure is that server
  263. // is running in session zero
  264. bRet = (pServerToken->GetSessionId() == 0);
  265. }
  266. else
  267. {
  268. // else the server and client better be in the same session; note
  269. // that if the client is from off-machine, then his session will
  270. // be zero.
  271. bRet = (S_OK == pToken->MatchTokenSessionID(pServerToken));
  272. }
  273. }
  274. }
  275. else
  276. {
  277. // Else the server is either a pure run-as server, or an activate-as-activator
  278. // server. In either case, it is adequate for the activation.
  279. bRet = TRUE;
  280. }
  281. }
  282. }
  283. // If this server registered as single-use, we need to make sure
  284. // no one else uses it. The previous implementation would take
  285. // a write lock here and completely remove the entry from the list,
  286. // but I like this method better since it's more concurrent.
  287. if (bRet && (_State & SERVERSTATE_SINGLEUSE))
  288. {
  289. LONG lICERet;
  290. lICERet = InterlockedCompareExchange(&_lSingleUseStatus,
  291. SINGLE_USE_TAKEN,
  292. SINGLE_USE_AVAILABLE);
  293. if (lICERet != SINGLE_USE_AVAILABLE)
  294. {
  295. // Can't use this one, somebody else grabbed it
  296. bRet = FALSE;
  297. }
  298. }
  299. return bRet;
  300. }
  301. BOOL
  302. CServerListEntry::CallServer(
  303. IN PACTIVATION_PARAMS pActParams,
  304. OUT HRESULT * phr )
  305. /*--
  306. Notes: jsimmons 02/10/01 -- I changed this function to return TRUE on
  307. most error paths. The semantic meaning of this function's
  308. return value is "TRUE if we called a server or encountered a
  309. fatal error trying to do so". Return values of FALSE are interpreted
  310. by the caller to mean "it's okay if I do a retry". My belief
  311. is that we should not be doing retrys of any kind after most
  312. errors -- ie, a memory allocation failure should stop us dead
  313. in our tracks.
  314. --*/
  315. {
  316. HANDLE hRpc;
  317. DWORD BusyRetries;
  318. DWORD RejectRetries;
  319. BOOL fDone;
  320. DWORD CreateInstanceFlags;
  321. error_status_t RpcStatus;
  322. HRESULT hr;
  323. hRpc = RpcHandle( pActParams->UnsecureActivation );
  324. if (!hRpc)
  325. {
  326. *phr = E_OUTOFMEMORY;
  327. return TRUE;
  328. }
  329. BusyRetries = 0;
  330. RejectRetries = 0;
  331. CreateInstanceFlags = 0;
  332. #ifdef SERVER_HANDLER
  333. if ( pActParams->ClsContext & CLSCTX_ESERVER_HANDLER )
  334. {
  335. CreateInstanceFlags |= CREATE_EMBEDDING_SERVER_HANDLER;
  336. if ( gbDisableEmbeddingServerHandler )
  337. {
  338. CreateInstanceFlags |= DISABLE_EMBEDDING_SERVER_HANDLER;
  339. pActParams->pInstantiationInfo->SetInstFlag(CreateInstanceFlags);
  340. }
  341. }
  342. #endif // SERVER_HANDLER
  343. if (!pActParams->UnsecureActivation)
  344. {
  345. BOOL fSuccess = ImpersonateLoggedOnUser( pActParams->pToken->GetToken() );
  346. if (!fSuccess)
  347. {
  348. *phr = HRESULT_FROM_WIN32(GetLastError());
  349. return TRUE;
  350. }
  351. }
  352. // Tell the server that we are using dynamic cloaking.
  353. pActParams->ORPCthis->flags |= ORPCF_DYNAMIC_CLOAKING;
  354. if (_State & SERVERSTATE_SURROGATE)
  355. {
  356. pActParams->pInstantiationInfo->SetIsSurrogate();
  357. }
  358. if (pActParams->MsgType == GETPERSISTENTINSTANCE)
  359. {
  360. Win4Assert(pActParams->pInstanceInfo != NULL);
  361. if (pActParams->pIFDROT)
  362. {
  363. InterfaceData *newIfd = AllocateAndCopy((InterfaceData*)pActParams->pIFDROT);
  364. if (newIfd == NULL)
  365. {
  366. if (!pActParams->UnsecureActivation)
  367. RevertToSelf();
  368. *phr = E_OUTOFMEMORY;
  369. return TRUE;
  370. }
  371. pActParams->pInstanceInfo->SetIfdROT((MInterfacePointer*)newIfd);
  372. }
  373. }
  374. // In case treat as changed the clsid set it now
  375. Win4Assert(pActParams->pInstantiationInfo);
  376. pActParams->pInstantiationInfo->SetClsid(pActParams->Clsid);
  377. do
  378. {
  379. MInterfacePointer *pIFDIn, *pIFDOut=NULL;
  380. DWORD destCtx = MSHCTX_LOCAL;
  381. *phr = ActPropsMarshalHelper(pActParams->pActPropsIn,
  382. IID_IActivationPropertiesIn,
  383. destCtx,
  384. MSHLFLAGS_NORMAL,
  385. &pIFDIn);
  386. if (FAILED(*phr))
  387. {
  388. if (!pActParams->UnsecureActivation)
  389. RevertToSelf();
  390. return TRUE;
  391. }
  392. switch (pActParams->MsgType)
  393. {
  394. case GETCLASSOBJECT:
  395. *phr = LocalGetClassObject(
  396. hRpc,
  397. pActParams->ORPCthis,
  398. pActParams->Localthis,
  399. pActParams->ORPCthat,
  400. pIFDIn,
  401. &pIFDOut,
  402. &RpcStatus );
  403. break;
  404. case GETPERSISTENTINSTANCE:
  405. case CREATEINSTANCE:
  406. *phr = LocalCreateInstance(
  407. hRpc,
  408. pActParams->ORPCthis,
  409. pActParams->Localthis,
  410. pActParams->ORPCthat,
  411. NULL, //No punk outer from here
  412. pIFDIn,
  413. &pIFDOut,
  414. &RpcStatus);
  415. break;
  416. default:
  417. ASSERT(0 && "Unknown activation type");
  418. *phr = E_UNEXPECTED;
  419. break;
  420. } //Switch
  421. MIDL_user_free(pIFDIn);
  422. if ((*phr == S_OK) && (RpcStatus == RPC_S_OK ))
  423. {
  424. // AWFUL HACK ALERT: This is too hacky even for the SCM
  425. ActivationStream ActStream((InterfaceData*)
  426. (((BYTE*)pIFDOut)+48));
  427. pActParams->pActPropsOut = new ActivationPropertiesOut(FALSE /* fBrokenRefCount */ );
  428. if (pActParams->pActPropsOut != NULL)
  429. {
  430. IActivationPropertiesOut *dummy;
  431. hr = pActParams->pActPropsOut->UnmarshalInterface(&ActStream,
  432. IID_IActivationPropertiesOut,
  433. (LPVOID*)&dummy);
  434. if (FAILED(hr))
  435. {
  436. pActParams->pActPropsOut->Release();
  437. pActParams->pActPropsOut = NULL;
  438. *phr = hr;
  439. }
  440. else
  441. dummy->Release();
  442. }
  443. else
  444. *phr = E_OUTOFMEMORY;
  445. MIDL_user_free(pIFDOut);
  446. }
  447. // Determine if we need to retry the call. Assume not.
  448. fDone = TRUE;
  449. if (RpcStatus == RPC_S_SERVER_TOO_BUSY)
  450. {
  451. // server RPC was busy, should we retry?
  452. if (BusyRetries++ < MAX_BUSY_RETRIES)
  453. fDone = FALSE;
  454. }
  455. else if (RpcStatus == RPC_E_CALL_REJECTED)
  456. {
  457. // Take Note: this is somewhat broken, but was added as a hotfix for
  458. // Word Insert Excel'97 with Addins, where Excel registers it's CF
  459. // early then rejects calls until the addin's are ready, which could
  460. // take any amount of time. We give 3 tries, with a sleep between them
  461. // to give the app some CPU time and emulate the delay than a client
  462. // message filter would do.
  463. if (RejectRetries++ < MAX_REJECT_RETRIES)
  464. {
  465. Sleep(DELAYTIME_BETWEEN_REJECTS);
  466. fDone = FALSE;
  467. }
  468. }
  469. }
  470. while ( !fDone );
  471. if ( ! pActParams->UnsecureActivation )
  472. RevertToSelf();
  473. // A RpcStatus of ERROR_ACCESS_DENIED means the server will never
  474. // accept calls from this user. Don't retry the activation.
  475. if (RpcStatus == ERROR_ACCESS_DENIED || RpcStatus == E_ACCESSDENIED)
  476. {
  477. *phr = HRESULT_FROM_WIN32(RpcStatus);
  478. return TRUE;
  479. }
  480. //
  481. // We get a non-zero rpcstat if there was a communication problem
  482. // with the server. We get CO_E_SERVER_STOPPING if a server
  483. // consumes its own single use registration or was in the process of
  484. // revoking its registration when we called.
  485. //
  486. else if ( (RpcStatus != RPC_S_OK) || (*phr == CO_E_SERVER_STOPPING) )
  487. {
  488. if ( RpcStatus != RPC_S_OK )
  489. {
  490. //
  491. // Some rpc errors are of the 8001xxxx variety. We shouldn't do
  492. // the hresult conversion of these.
  493. //
  494. if ( HRESULT_FACILITY( RpcStatus ) == FACILITY_RPC )
  495. *phr = RpcStatus;
  496. else
  497. *phr = HRESULT_FROM_WIN32(RpcStatus);
  498. }
  499. // Decide whether to retry the activation.
  500. return RetryableError(*phr) ? FALSE : TRUE;
  501. }
  502. return TRUE;
  503. }
  504. BOOL
  505. CServerListEntry::ServerDied()
  506. /*--
  507. ServerDied
  508. Used by callers to determine if the server process handle
  509. has been signalled.
  510. --*/
  511. {
  512. BOOL fServerDied = FALSE;
  513. HANDLE hProcess = _pServerProcess->GetProcessHandle();
  514. if (hProcess)
  515. {
  516. DWORD dwRet = WaitForSingleObject(hProcess, 0);
  517. if (dwRet == WAIT_OBJECT_0)
  518. {
  519. fServerDied = TRUE;
  520. }
  521. // else assume still alive on all other return values
  522. }
  523. return fServerDied;
  524. }
  525. BOOL
  526. CServerListEntry::RetryableError(HRESULT hr)
  527. /*--
  528. Returns TRUE if the error is such that the caller should attempt
  529. a retry of the activation, or FALSE otherwise.
  530. --*/
  531. {
  532. BOOL fRetry = TRUE;
  533. switch (hr)
  534. {
  535. case RPC_E_SYS_CALL_FAILED:
  536. // RPC_E_SYS_CALL_FAILED is not used by rpc, only ole32, and it
  537. // is not one we should be doing retrys on.
  538. fRetry = FALSE;
  539. break;
  540. default:
  541. fRetry = TRUE;
  542. break;
  543. }
  544. return fRetry;
  545. }