Leaked source code of windows server 2003
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.

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