SslnegoCookieResolver.cs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. //
  2. // SslnegoCookieResolver.cs
  3. //
  4. // Author:
  5. // Atsushi Enomoto <[email protected]>
  6. //
  7. // Copyright (C) 2007 Novell, Inc. http://www.novell.com
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining
  10. // a copy of this software and associated documentation files (the
  11. // "Software"), to deal in the Software without restriction, including
  12. // without limitation the rights to use, copy, modify, merge, publish,
  13. // distribute, sublicense, and/or sell copies of the Software, and to
  14. // permit persons to whom the Software is furnished to do so, subject to
  15. // the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. //
  28. using System;
  29. using System.IO;
  30. using System.Security.Cryptography.X509Certificates;
  31. using System.Xml;
  32. /*
  33. LAMESPEC: The cookie value is encoded by
  34. ServiceCredential.SecureConversationAuthentication.SecurityStateEncoder.
  35. When a custom SecurityStateEncoder is used,
  36. - at service side it is actually invoked,
  37. - at client side it is impossible to specify such custom state decoder, so
  38. it is treated as if the key is passed as is, and thus if it could raise
  39. verification error (it is LAMESPEC, since if the custom state encoder is
  40. pass-through it just works fine).
  41. Raw Cookie data format (via pass-through SecurityStateEncoder)
  42. <42 00 42 02 83 42 06 99> L[uuid-_________] bbbb-bb
  43. <42 04 AD> (16bytes) <42 08 9E 1E> (43 bytes)
  44. <C9 08 42 10 8F> (6 bytes)
  45. <C9 08 42 14 8F> (6 bytes)
  46. <C9 08 42 16 8F> (6 bytes)
  47. <C9 08 01>
  48. The uuid seems kept identical while one service is running (i.e. unique per ServiceHost).
  49. Actually the raw octets corresponds to
  50. XmlBinaryWriter output format, so it is likely.
  51. So, it will be parsed as below:
  52. 42 00
  53. 42 02
  54. 83
  55. 42 06 99 2B 75 75 69 64 2D 31 65 38 33 62 63 37 39 2D 35 30 33 37 2D 34 61 32 30 2D 38 32 66 37 2D 64 32 39 37 31 34 61 30 32 62 37 66 2D 31 // UniqueId wsu:Id
  56. 42 04 AD 45 34 07 4E 38 D2 18 4D 8B 22 FD 6C E6 CE B2 17 // UniqueIdFromGuid ContextId
  57. 42 08 9E 1E CA AC F2 71 6E 61 99 DA FB 71 B2 A8 DC 51 36 5B CD F3 F9 60 D2 B6 67 BF 5D B0 CE ED 37 35 9F 02 DC 7D // Base64 Key
  58. 42 0E 8F F4 4C 9C 48 61 33 C9 08 // Int64 KeyCreation
  59. 42 10 8F F4 5C 48 1A B5 33 C9 08 // Int64 KeyExpiration
  60. 42 14 8F F4 4C 9C 48 61 33 C9 08 // Int64 CookieCreatation
  61. 42 16 8F F4 5C 48 1A B5 33 C9 08 // Int64 CookieExpiration
  62. 01
  63. The actual XML looks like:
  64. <n1><n2>1</n2><n4>uuid-950f764e-f6dc-4f5d-8df36699e28618cf-1</n4><n3>urn:uuid:a13aa8b0-f0b5-4a78-967e-fbd05459d882</n3><n5>W0I2qFT/H5ElE14l3wy8rqZHVvjbesvtshaLOdQdXyk=</n5><n8>633092852947500000</n8><n9>633093212947500000</n9><n11>633092852947500000</n11><n12>633093212947500000</n12></n1>
  65. where n[x] are presumed names. They would be meaningful in MS implementation,
  66. but as a binary XML array with preconfigured IXmlDictionary (sigh), it doesn't
  67. matter.
  68. n2 matches the context Identifier for SecurityContextToken.
  69. n3 matches the u:Id for SecurityContextToken.
  70. */
  71. namespace System.ServiceModel.Security.Tokens
  72. {
  73. internal class SslnegoCookieResolver
  74. {
  75. public static SecurityContextSecurityToken ResolveCookie (byte [] bytes, byte [] cookie)
  76. {
  77. string id = null;
  78. UniqueId context = null;
  79. DateTime validFrom = DateTime.MinValue,
  80. validTo = DateTime.MaxValue,
  81. keyEffective = DateTime.MinValue,
  82. keyExpired = DateTime.MaxValue;
  83. byte [] key = null;
  84. X509Certificate2 cert = null;
  85. X500DistinguishedName issuer = null;
  86. XmlDictionary dic = new XmlDictionary ();
  87. for (int i = 0; i < 30; i++)
  88. dic.Add ("n" + i);
  89. // FIXME: create proper quotas
  90. XmlDictionaryReaderQuotas quotas =
  91. new XmlDictionaryReaderQuotas ();
  92. XmlDictionaryReader cr = XmlDictionaryReader.CreateBinaryReader (bytes, 0, bytes.Length, dic, quotas);
  93. cr.MoveToContent (); // -> n1
  94. cr.ReadStartElement ("n0", String.Empty);
  95. do {
  96. cr.MoveToContent ();
  97. if (cr.NodeType == XmlNodeType.EndElement)
  98. break;
  99. if (cr.NodeType != XmlNodeType.Element)
  100. throw new Exception ("Unxpected non-element content:" + cr.NodeType);
  101. switch (cr.Name) {
  102. case "n1":
  103. // FIXME: some integer here
  104. int n1 = cr.ReadElementContentAsInt ();
  105. if (n1 != 1)
  106. throw new Exception ("INTERNAL ERROR: there was unexpected n2 content: " + n1);
  107. break;
  108. case "n2":
  109. context = cr.ReadElementContentAsUniqueId ();
  110. break;
  111. case "n3":
  112. id = cr.ReadElementContentAsString ();
  113. break;
  114. case "n4":
  115. key = cr.ReadElementContentAsBase64 ();
  116. break;
  117. case "n7":
  118. validFrom = new DateTime (cr.ReadElementContentAsLong ());
  119. break;
  120. case "n8":
  121. validTo = new DateTime (cr.ReadElementContentAsLong ());
  122. break;
  123. case "n10":
  124. keyEffective = new DateTime (cr.ReadElementContentAsLong ());
  125. break;
  126. case "n11":
  127. keyExpired = new DateTime (cr.ReadElementContentAsLong ());
  128. break;
  129. case "n13":
  130. // <n18>X509Certificate</n18>
  131. cr.Read ();
  132. cr.MoveToContent ();
  133. cert = new X509Certificate2 (cr.ReadElementContentAsBase64 ());
  134. cr.ReadEndElement ();
  135. break;
  136. case "n15":
  137. // <n16><n24 n25="IssuerName" /></n16>
  138. cr.Read ();
  139. cr.ReadStartElement ("n16", String.Empty);
  140. issuer = new X500DistinguishedName (cr.GetAttribute ("n25"));
  141. bool empty = cr.IsEmptyElement;
  142. cr.ReadStartElement ("n24", String.Empty);
  143. if (!empty)
  144. cr.ReadEndElement (); // n24
  145. cr.ReadEndElement (); // n16
  146. cr.ReadEndElement (); // n15
  147. break;
  148. default:
  149. throw new Exception ("INTERNAL ERROR: there was an unhandled element: " + cr.Name);
  150. }
  151. } while (true);
  152. SecurityContextSecurityToken sct = new SecurityContextSecurityToken (
  153. context, id, key, validFrom, validTo,
  154. null, keyEffective, keyExpired, null);
  155. sct.Cookie = cookie;
  156. return sct;
  157. }
  158. public static byte [] CreateData (UniqueId contextId, UniqueId session, byte [] key, DateTime tokenSince, DateTime tokenUntil, DateTime keySince, DateTime keyUntil)
  159. {
  160. XmlDictionary dic = new XmlDictionary ();
  161. for (int i = 0; i < 12; i++)
  162. dic.Add ("n" + i);
  163. MemoryStream ms = new MemoryStream ();
  164. XmlDictionaryWriter w = XmlDictionaryWriter.CreateBinaryWriter (ms, dic);
  165. XmlDictionaryString e = XmlDictionaryString.Empty;
  166. w.WriteStartElement (dic.Add ("n0"), e);
  167. w.WriteStartElement (dic.Add ("n1"), e);
  168. w.WriteValue (1);
  169. w.WriteEndElement ();
  170. w.WriteStartElement (dic.Add ("n3"), e);
  171. w.WriteValue (contextId);
  172. w.WriteEndElement ();
  173. w.WriteStartElement (dic.Add ("n2"), e);
  174. w.WriteValue (contextId);
  175. w.WriteEndElement ();
  176. w.WriteStartElement (dic.Add ("n4"), e);
  177. w.WriteBase64 (key, 0, key.Length);
  178. w.WriteEndElement ();
  179. w.WriteStartElement (dic.Add ("n7"), e);
  180. w.WriteValue (tokenSince.Ticks);
  181. w.WriteEndElement ();
  182. w.WriteStartElement (dic.Add ("n8"), e);
  183. w.WriteValue (tokenUntil.Ticks);
  184. w.WriteEndElement ();
  185. w.WriteStartElement (dic.Add ("n10"), e);
  186. w.WriteValue (keySince.Ticks);
  187. w.WriteEndElement ();
  188. w.WriteStartElement (dic.Add ("n11"), e);
  189. w.WriteValue (keyUntil.Ticks);
  190. w.WriteEndElement ();
  191. w.Close ();
  192. return ms.ToArray ();
  193. }
  194. }
  195. }