ClientAccessPolicy.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  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 MOBILE
  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")) {
  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. foreach (AccessPolicy policy in AccessPolicyList) {
  109. // does something allow our URI in this policy ?
  110. foreach (AllowFrom af in policy.AllowedServices) {
  111. // is the application (XAP) URI allowed by the policy ?
  112. // check headers
  113. if (!af.HttpRequestHeaders.IsAllowed (headerKeys)) {
  114. return false;
  115. }
  116. if (af.IsAllowed (ApplicationUri, method)) {
  117. foreach (GrantTo gt in policy.GrantedResources) {
  118. // is the requested access to the Uri granted under this policy ?
  119. if (gt.IsGranted (uri)) {
  120. // at this stage the URI has removed the "offending" characters so
  121. // we need to look at the original
  122. return CheckOriginalPath (uri);
  123. }
  124. }
  125. }
  126. }
  127. }
  128. // no policy allows this web connection
  129. return false;
  130. }
  131. public class AllowFrom {
  132. public AllowFrom ()
  133. {
  134. Domains = new List<string> ();
  135. HttpRequestHeaders = new Headers ();
  136. }
  137. public bool AllowAnyDomain { get; set; }
  138. public List<string> Domains { get; private set; }
  139. public Headers HttpRequestHeaders { get; private set; }
  140. public bool AllowAnyMethod { get; set; }
  141. public bool IsAllowed (Uri uri, string method)
  142. {
  143. // check methods
  144. if (!AllowAnyMethod) {
  145. // if not all methods are allowed (*) then only GET and POST request are possible
  146. // further restriction exists in the Client http stack
  147. if ((String.Compare (method, "GET", StringComparison.OrdinalIgnoreCase) != 0) &&
  148. (String.Compare (method, "POST", StringComparison.OrdinalIgnoreCase) != 0)) {
  149. return false;
  150. }
  151. }
  152. // check domains
  153. if (AllowAnyDomain)
  154. return true;
  155. if (Domains.All (domain => !CheckDomainUri (uri, domain)))
  156. return false;
  157. return true;
  158. }
  159. const string AllHttpScheme = "http://*";
  160. const string AllHttpsScheme = "https://*";
  161. const string AllFileScheme = "file:///";
  162. static bool CheckDomainUri (Uri applicationUri, string policy)
  163. {
  164. Uri uri;
  165. if (Uri.TryCreate (policy, UriKind.Absolute, out uri)) {
  166. // if no local path is part of the policy domain then we compare to the root
  167. if (uri.LocalPath == "/")
  168. return (uri.ToString () == ApplicationRoot);
  169. // otherwise the path must match
  170. if (uri.LocalPath != ApplicationUri.LocalPath)
  171. return false;
  172. return (CrossDomainPolicyManager.GetRoot (uri) == ApplicationRoot);
  173. }
  174. // SL policies supports a * wildcard at the start of their host name (but not elsewhere)
  175. // check for matching protocol
  176. if (!policy.StartsWith (ApplicationUri.Scheme))
  177. return false;
  178. switch (ApplicationUri.Scheme) {
  179. case "http":
  180. if (policy == AllHttpScheme)
  181. return (applicationUri.Port == 80);
  182. break;
  183. case "https":
  184. if (policy == AllHttpsScheme)
  185. return (applicationUri.Port == 443);
  186. break;
  187. case "file":
  188. if (policy == AllFileScheme)
  189. return true;
  190. break;
  191. }
  192. if (policy.IndexOf ("://*.", ApplicationUri.Scheme.Length) != ApplicationUri.Scheme.Length)
  193. return false;
  194. // remove *. from uri
  195. policy = policy.Remove (ApplicationUri.Scheme.Length + 3, 2);
  196. // create Uri - without the *. it should be a valid one
  197. if (!Uri.TryCreate (policy, UriKind.Absolute, out uri))
  198. return false;
  199. // path must be "empty" and query and fragment (really) empty
  200. if ((uri.LocalPath != "/") || !String.IsNullOrEmpty (uri.Query) || !String.IsNullOrEmpty (uri.Fragment))
  201. return false;
  202. // port must match
  203. if (ApplicationUri.Port != uri.Port)
  204. return false;
  205. // the application uri host must end with the policy host name
  206. return ApplicationUri.DnsSafeHost.EndsWith (uri.DnsSafeHost);
  207. }
  208. }
  209. public class GrantTo
  210. {
  211. public GrantTo ()
  212. {
  213. Resources = new List<Resource> ();
  214. }
  215. public List<Resource> Resources { get; private set; }
  216. public bool IsGranted (Uri uri)
  217. {
  218. foreach (var gr in Resources) {
  219. if (gr.IncludeSubpaths) {
  220. string granted = gr.Path;
  221. string local = uri.LocalPath;
  222. if (local.StartsWith (granted, StringComparison.Ordinal)) {
  223. // "/test" equals "/test" and "test/xyx" but not "/test2"
  224. // "/test/" equals "test/xyx" but not "/test" or "/test2"
  225. if (local.Length == granted.Length)
  226. return true;
  227. else if (granted [granted.Length - 1] == '/')
  228. return true;
  229. else if (local [granted.Length] == '/')
  230. return true;
  231. }
  232. } else {
  233. if (uri.LocalPath == gr.Path)
  234. return true;
  235. }
  236. }
  237. return false;
  238. }
  239. }
  240. public class Resource {
  241. private string path;
  242. public string Path {
  243. get { return path; }
  244. set {
  245. // an empty Path Ressource makes the *whole* policy file invalid
  246. if (String.IsNullOrEmpty (value))
  247. throw new NotSupportedException ();
  248. path = value;
  249. }
  250. }
  251. public bool IncludeSubpaths { get; set; }
  252. }
  253. }
  254. }
  255. #endif