1
0

QueryLanguage.Remoting.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2. #if !NO_REMOTING
  3. using System.Reactive.Disposables;
  4. using System.Runtime.Remoting;
  5. using System.Runtime.Remoting.Lifetime;
  6. using System.Security;
  7. using System.Threading;
  8. //
  9. // DESIGN: The MarshalByRefObject (MBRO) implementations for RemotableObserver and RemotableSubscription act as
  10. // self-sponsoring objects controlling their lease times in order to tie those to the lifetime of the
  11. // underlying observable sequence (ended by OnError or OnCompleted) or the user-controlled subscription
  12. // lifetime. If we were to implement InitializeLifetimeService to return null, we'd end up with leases
  13. // that are infinite, so we need a more fine-grained lease scheme. The default configuration would time
  14. // out after 5 minutes, causing clients to fail while they're still observing the sequence. To solve
  15. // this, those MBROs also implement ISponsor with a Renewal method that continues to renew the lease
  16. // upon every call. When the sequence comes to an end or the subscription is disposed, the sponsor gets
  17. // unregistered, allowing the objects to be reclaimed eventually by the Remoting infrastructure.
  18. //
  19. // SECURITY: Registration and unregistration of sponsors is protected by SecurityCritical annotations. The
  20. // implementation of ISponsor is known (i.e. no foreign implementation can be passed in) at the call
  21. // sites of the Register and Unregister methods. The call to Register happens in the SecurityCritical
  22. // InitializeLifetimeService method and is called by trusted Remoting infrastructure. The Renewal
  23. // method is also marked as SecurityCritical and called by Remoting. The Unregister method is wrapped
  24. // in a ***SecurityTreatAsSafe*** private method which only gets called by the observer's OnError and
  25. // OnCompleted notifications, or the subscription's Dispose method. In the former case, the sequence
  26. // indicates it has reached the end, and hence resources can be reclaimed. Clients will no longer be
  27. // connected to the source due to auto-detach behavior enforced in the SerializableObservable client-
  28. // side implementation. In the latter case of disposing the subscription, the client is in control
  29. // and will cause the underlying remote subscription to be disposed as well, allowing resources to be
  30. // reclaimed. Rogue messages on either the data or the subscription channel can cause a DoS of the
  31. // client-server communication but this is subject to the security of the Remoting channels used. In
  32. // no case an untrusted party can cause _extension_ of the lease time.
  33. //
  34. //
  35. // Notice this assembly is marked as APTCA in official builds, causing methods to be treated as transparent,
  36. // thus requiring the ***SecurityTreatAsSafe*** annotation on the security boundaries described above. When not
  37. // applied, the following exception would occur at runtime:
  38. //
  39. // System.MethodAccessException:
  40. //
  41. // Attempt by security transparent method 'System.Reactive.Linq.QueryLanguage+RemotableObservable`1+
  42. // RemotableSubscription<T>.Unregister()' to access security critical method 'System.Runtime.Remoting.Lifetime.
  43. // ILease.Unregister(System.Runtime.Remoting.Lifetime.ISponsor)' failed.
  44. //
  45. // Assembly 'System.Reactive.Linq, Version=2.0.ymmdd.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
  46. // is marked with the AllowPartiallyTrustedCallersAttribute, and uses the level 2 security transparency model.
  47. // Level 2 transparency causes all methods in AllowPartiallyTrustedCallers assemblies to become security
  48. // transparent by default, which may be the cause of this exception.
  49. //
  50. //
  51. // The two CodeAnalysis suppressions below are explained by the Justification property (scroll to the right):
  52. //
  53. [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2136:TransparencyAnnotationsShouldNotConflictFxCopRule", Scope = "member", Target = "System.Reactive.Linq.QueryLanguage+RemotableObserver`1.#Unregister()", Justification = "This error only occurs while running FxCop on local builds that don't have NO_CODECOVERAGE set, causing the assembly not to be marked with APTCA (see AssemblyInfo.cs). When APTCA is enabled in official builds, this SecurityTreatAsSafe annotation is required.")]
  54. [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2136:TransparencyAnnotationsShouldNotConflictFxCopRule", Scope = "member", Target = "System.Reactive.Linq.QueryLanguage+RemotableObservable`1+RemotableSubscription.#Unregister()", Justification = "This error only occurs while running FxCop on local builds that don't have NO_CODECOVERAGE set, causing the assembly not to be marked with APTCA (see AssemblyInfo.cs). When APTCA is enabled in official builds, this SecurityTreatAsSafe annotation is required.")]
  55. namespace System.Reactive.Linq
  56. {
  57. public static partial class RemotingObservable
  58. {
  59. #region Remotable
  60. private static IObservable<TSource> Remotable_<TSource>(IObservable<TSource> source)
  61. {
  62. return new SerializableObservable<TSource>(new RemotableObservable<TSource>(source, null));
  63. }
  64. private static IObservable<TSource> Remotable_<TSource>(IObservable<TSource> source, ILease lease)
  65. {
  66. return new SerializableObservable<TSource>(new RemotableObservable<TSource>(source, lease));
  67. }
  68. [Serializable]
  69. class SerializableObservable<T> : IObservable<T>
  70. {
  71. readonly RemotableObservable<T> remotableObservable;
  72. public SerializableObservable(RemotableObservable<T> remotableObservable)
  73. {
  74. this.remotableObservable = remotableObservable;
  75. }
  76. public IDisposable Subscribe(IObserver<T> observer)
  77. {
  78. var d = new SingleAssignmentDisposable();
  79. var o = Observer.Create<T>(
  80. observer.OnNext,
  81. ex =>
  82. {
  83. //
  84. // Make call to the remote subscription, causing lease renewal to be stopped.
  85. //
  86. using (d)
  87. {
  88. observer.OnError(ex);
  89. }
  90. },
  91. () =>
  92. {
  93. //
  94. // Make call to the remote subscription, causing lease renewal to be stopped.
  95. //
  96. using (d)
  97. {
  98. observer.OnCompleted();
  99. }
  100. }
  101. );
  102. //
  103. // [OK] Use of unsafe Subscribe: non-pretentious transparent wrapping through remoting; exception coming from the remote object is not re-routed.
  104. //
  105. d.Disposable = remotableObservable.Subscribe/*Unsafe*/(new RemotableObserver<T>(o));
  106. return d;
  107. }
  108. }
  109. class RemotableObserver<T> : MarshalByRefObject, IObserver<T>, ISponsor
  110. {
  111. readonly IObserver<T> underlyingObserver;
  112. public RemotableObserver(IObserver<T> underlyingObserver)
  113. {
  114. this.underlyingObserver = underlyingObserver;
  115. }
  116. public void OnNext(T value)
  117. {
  118. underlyingObserver.OnNext(value);
  119. }
  120. public void OnError(Exception exception)
  121. {
  122. try
  123. {
  124. underlyingObserver.OnError(exception);
  125. }
  126. finally
  127. {
  128. Unregister();
  129. }
  130. }
  131. public void OnCompleted()
  132. {
  133. try
  134. {
  135. underlyingObserver.OnCompleted();
  136. }
  137. finally
  138. {
  139. Unregister();
  140. }
  141. }
  142. [SecuritySafeCritical] // See remarks at the top of the file.
  143. private void Unregister()
  144. {
  145. var lease = (ILease)RemotingServices.GetLifetimeService(this);
  146. if (lease != null)
  147. lease.Unregister(this);
  148. }
  149. [SecurityCritical]
  150. public override object InitializeLifetimeService()
  151. {
  152. var lease = (ILease)base.InitializeLifetimeService();
  153. lease.Register(this);
  154. return lease;
  155. }
  156. [SecurityCritical]
  157. TimeSpan ISponsor.Renewal(ILease lease)
  158. {
  159. return lease.InitialLeaseTime;
  160. }
  161. }
  162. [Serializable]
  163. sealed class RemotableObservable<T> : MarshalByRefObject, IObservable<T>
  164. {
  165. readonly IObservable<T> underlyingObservable;
  166. readonly ILease lease;
  167. public RemotableObservable(IObservable<T> underlyingObservable, ILease lease)
  168. {
  169. this.underlyingObservable = underlyingObservable;
  170. this.lease = lease;
  171. }
  172. public IDisposable Subscribe(IObserver<T> observer)
  173. {
  174. //
  175. // [OK] Use of unsafe Subscribe: non-pretentious transparent wrapping through remoting; throwing across remoting boundaries is fine.
  176. //
  177. return new RemotableSubscription(underlyingObservable.Subscribe/*Unsafe*/(observer));
  178. }
  179. [SecurityCritical]
  180. public override object InitializeLifetimeService()
  181. {
  182. return lease;
  183. }
  184. sealed class RemotableSubscription : MarshalByRefObject, IDisposable, ISponsor
  185. {
  186. private IDisposable underlyingSubscription;
  187. public RemotableSubscription(IDisposable underlyingSubscription)
  188. {
  189. this.underlyingSubscription = underlyingSubscription;
  190. }
  191. public void Dispose()
  192. {
  193. //
  194. // Avoiding double-dispose and dropping the reference upon disposal.
  195. //
  196. using (Interlocked.Exchange(ref underlyingSubscription, Disposable.Empty))
  197. {
  198. Unregister();
  199. }
  200. }
  201. [SecuritySafeCritical] // See remarks at the top of the file.
  202. private void Unregister()
  203. {
  204. var lease = (ILease)RemotingServices.GetLifetimeService(this);
  205. if (lease != null)
  206. lease.Unregister(this);
  207. }
  208. [SecurityCritical]
  209. public override object InitializeLifetimeService()
  210. {
  211. var lease = (ILease)base.InitializeLifetimeService();
  212. lease.Register(this);
  213. return lease;
  214. }
  215. [SecurityCritical]
  216. TimeSpan ISponsor.Renewal(ILease lease)
  217. {
  218. return lease.InitialLeaseTime;
  219. }
  220. }
  221. }
  222. #endregion
  223. }
  224. }
  225. #endif