ClientAccessPolicy.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. //
  2. // ClientAccessPolicy.cs
  3. //
  4. // Authors:
  5. // Atsushi Enomoto <[email protected]>
  6. // Moonlight List ([email protected])
  7. //
  8. // Copyright (C) 2009-2010 Novell, Inc. http://www.novell.com
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining
  11. // a copy of this software and associated documentation files (the
  12. // "Software"), to deal in the Software without restriction, including
  13. // without limitation the rights to use, copy, modify, merge, publish,
  14. // distribute, sublicense, and/or sell copies of the Software, and to
  15. // permit persons to whom the Software is furnished to do so, subject to
  16. // the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. //
  29. #if NET_2_1
  30. using System;
  31. using System.Collections.Generic;
  32. using System.IO;
  33. using System.Linq;
  34. namespace System.Net.Policy {
  35. partial class ClientAccessPolicy : BaseDomainPolicy {
  36. public class AccessPolicy {
  37. public const short MinPort = 4502;
  38. public const short MaxPort = 4534;
  39. public List<AllowFrom> AllowedServices { get; private set; }
  40. public List<GrantTo> GrantedResources { get; private set; }
  41. public long PortMask { get; set; }
  42. public AccessPolicy ()
  43. {
  44. AllowedServices = new List<AllowFrom> ();
  45. GrantedResources = new List<GrantTo> ();
  46. }
  47. public bool PortAllowed (int port)
  48. {
  49. if ((port < MinPort) || (port > MaxPort))
  50. return false;
  51. return (((PortMask >> (port - MinPort)) & 1) == 1);
  52. }
  53. }
  54. public ClientAccessPolicy ()
  55. {
  56. AccessPolicyList = new List<AccessPolicy> ();
  57. }
  58. public List<AccessPolicy> AccessPolicyList { get; private set; }
  59. public bool IsAllowed (IPEndPoint endpoint)
  60. {
  61. foreach (AccessPolicy policy in AccessPolicyList) {
  62. // does something allow our URI in this policy ?
  63. foreach (AllowFrom af in policy.AllowedServices) {
  64. // fake "GET" as method as this does not apply to sockets
  65. if (af.IsAllowed (ApplicationUri, "GET", null)) {
  66. // if so, is our request port allowed ?
  67. if (policy.PortAllowed (endpoint.Port))
  68. return true;
  69. }
  70. }
  71. }
  72. // no policy allows this socket connection
  73. return false;
  74. }
  75. // note: tests show that it only applies to Silverlight policy (seems to work with Flash)
  76. // and only if we're not granting full access (i.e. '/' with all subpaths)
  77. // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=466043
  78. private bool CheckOriginalPath (Uri uri)
  79. {
  80. // Path Restriction for cross-domain requests
  81. // http://msdn.microsoft.com/en-us/library/cc838250(VS.95).aspx
  82. string original = uri.OriginalString;
  83. // applies to the *path* only (not the query part)
  84. int query = original.IndexOf ('?');
  85. if (query != -1)
  86. original = original.Substring (0, query);
  87. if (original.Contains ('%') || original.Contains ("./") || original.Contains ("..")) {
  88. // special case when no path restriction applies - i.e. the above characters are accepted by SL
  89. if (AccessPolicyList.Count != 1)
  90. return false;
  91. AccessPolicy policy = AccessPolicyList [0];
  92. if (policy.GrantedResources.Count != 1)
  93. return false;
  94. GrantTo gt = policy.GrantedResources [0];
  95. if (gt.Resources.Count != 1)
  96. return false;
  97. Resource r = gt.Resources [0];
  98. return (r.IncludeSubpaths && (r.Path == "/"));
  99. }
  100. return true;
  101. }
  102. public override bool IsAllowed (WebRequest request)
  103. {
  104. return IsAllowed (request.RequestUri, request.Method, request.Headers.AllKeys);
  105. }
  106. public bool IsAllowed (Uri uri, string method, params string [] headerKeys)
  107. {
  108. // at this stage the URI has removed the "offending" characters so we need to look at the original
  109. if (!CheckOriginalPath (uri))
  110. return false;
  111. foreach (AccessPolicy policy in AccessPolicyList) {
  112. // does something allow our URI in this policy ?
  113. foreach (AllowFrom af in policy.AllowedServices) {
  114. // is the application (XAP) URI allowed by the policy ?
  115. if (af.IsAllowed (ApplicationUri, method, headerKeys)) {
  116. foreach (GrantTo gt in policy.GrantedResources) {
  117. // is the requested access to the Uri granted under this policy ?
  118. if (gt.IsGranted (uri))
  119. return true;
  120. }
  121. }
  122. }
  123. }
  124. // no policy allows this web connection
  125. return false;
  126. }
  127. public class AllowFrom {
  128. public AllowFrom ()
  129. {
  130. Domains = new List<string> ();
  131. HttpRequestHeaders = new Headers ();
  132. Scheme = String.Empty;
  133. }
  134. public bool AllowAnyDomain { get; set; }
  135. public List<string> Domains { get; private set; }
  136. public Headers HttpRequestHeaders { get; private set; }
  137. public bool AllowAnyMethod { get; set; }
  138. public string Scheme { get; internal set; }
  139. public bool IsAllowed (Uri uri, string method, string [] headerKeys)
  140. {
  141. // check headers
  142. if (!HttpRequestHeaders.IsAllowed (headerKeys))
  143. return false;
  144. // check scheme
  145. if ((Scheme.Length > 0) && (Scheme == uri.Scheme)) {
  146. switch (Scheme) {
  147. case "http":
  148. return (uri.Port == 80);
  149. case "https":
  150. return (uri.Port == 443);
  151. case "file":
  152. return true;
  153. default:
  154. return false;
  155. }
  156. }
  157. // check methods
  158. if (!AllowAnyMethod) {
  159. // if not all methods are allowed (*) then only GET and POST request are possible
  160. // further restriction exists in the Client http stack
  161. if ((String.Compare (method, "GET", StringComparison.OrdinalIgnoreCase) != 0) &&
  162. (String.Compare (method, "POST", StringComparison.OrdinalIgnoreCase) != 0)) {
  163. return false;
  164. }
  165. }
  166. // check domains
  167. if (AllowAnyDomain)
  168. return true;
  169. if (Domains.All (domain => !CheckDomainUri (domain)))
  170. return false;
  171. return true;
  172. }
  173. static bool CheckDomainUri (string policy)
  174. {
  175. Uri uri;
  176. if (Uri.TryCreate (policy, UriKind.Absolute, out uri)) {
  177. // if no local path is part of the policy domain then we compare to the root
  178. if (uri.LocalPath == "/")
  179. return (uri.ToString () == ApplicationRoot);
  180. // otherwise the path must match
  181. if (uri.LocalPath != ApplicationUri.LocalPath)
  182. return false;
  183. return (CrossDomainPolicyManager.GetRoot (uri) == ApplicationRoot);
  184. }
  185. // SL policies supports a * wildcard at the start of their host name (but not elsewhere)
  186. // check for matching protocol
  187. if (!policy.StartsWith (ApplicationUri.Scheme))
  188. return false;
  189. // check for the wirld card immediately after the scheme
  190. if (policy.IndexOf ("://*.", ApplicationUri.Scheme.Length) != ApplicationUri.Scheme.Length)
  191. return false;
  192. // remove *. from uri
  193. policy = policy.Remove (ApplicationUri.Scheme.Length + 3, 2);
  194. // create Uri - without the *. it should be a valid one
  195. if (!Uri.TryCreate (policy, UriKind.Absolute, out uri))
  196. return false;
  197. // path must be "empty" and query and fragment (really) empty
  198. if ((uri.LocalPath != "/") || !String.IsNullOrEmpty (uri.Query) || !String.IsNullOrEmpty (uri.Fragment))
  199. return false;
  200. // port must match
  201. if (ApplicationUri.Port != uri.Port)
  202. return false;
  203. // the application uri host must end with the policy host name
  204. return ApplicationUri.DnsSafeHost.EndsWith (uri.DnsSafeHost);
  205. }
  206. }
  207. public class GrantTo
  208. {
  209. public GrantTo ()
  210. {
  211. Resources = new List<Resource> ();
  212. }
  213. public List<Resource> Resources { get; private set; }
  214. public bool IsGranted (Uri uri)
  215. {
  216. foreach (var gr in Resources) {
  217. if (gr.IncludeSubpaths) {
  218. string granted = gr.Path;
  219. string local = uri.LocalPath;
  220. if (local.StartsWith (granted, StringComparison.Ordinal)) {
  221. // "/test" equals "/test" and "test/xyx" but not "/test2"
  222. // "/test/" equals "test/xyx" but not "/test" or "/test2"
  223. if (local.Length == granted.Length)
  224. return true;
  225. else if (granted [granted.Length - 1] == '/')
  226. return true;
  227. else if (local [granted.Length] == '/')
  228. return true;
  229. }
  230. } else {
  231. if (uri.LocalPath == gr.Path)
  232. return true;
  233. }
  234. }
  235. return false;
  236. }
  237. }
  238. public class Resource {
  239. private string path;
  240. public string Path {
  241. get { return path; }
  242. set {
  243. // an empty Path Ressource makes the *whole* policy file invalid
  244. if (String.IsNullOrEmpty (value))
  245. throw new NotSupportedException ();
  246. path = value;
  247. }
  248. }
  249. public bool IncludeSubpaths { get; set; }
  250. }
  251. }
  252. }
  253. #endif