SspiNegotiationTokenAuthenticator.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. namespace System.ServiceModel.Security
  5. {
  6. using System.Collections.Generic;
  7. using System.Collections.ObjectModel;
  8. using System.IdentityModel.Policy;
  9. using System.IdentityModel.Selectors;
  10. using System.IdentityModel.Tokens;
  11. using System.IO;
  12. using System.Runtime;
  13. using System.Runtime.Serialization;
  14. using System.Security.Authentication.ExtendedProtection;
  15. using System.Security.Cryptography;
  16. using System.ServiceModel;
  17. using System.ServiceModel.Channels;
  18. using System.ServiceModel.Diagnostics;
  19. using System.ServiceModel.Dispatcher;
  20. using System.ServiceModel.Security.Tokens;
  21. using System.Xml;
  22. using CanonicalizationDriver = System.IdentityModel.CanonicalizationDriver;
  23. using Psha1DerivedKeyGenerator = System.IdentityModel.Psha1DerivedKeyGenerator;
  24. abstract class SspiNegotiationTokenAuthenticator : NegotiationTokenAuthenticator<SspiNegotiationTokenAuthenticatorState>
  25. {
  26. ExtendedProtectionPolicy extendedProtectionPolicy;
  27. string defaultServiceBinding;
  28. Object thisLock = new Object();
  29. protected SspiNegotiationTokenAuthenticator()
  30. : base()
  31. {
  32. }
  33. public ExtendedProtectionPolicy ExtendedProtectionPolicy
  34. {
  35. get { return this.extendedProtectionPolicy; }
  36. set { this.extendedProtectionPolicy = value; }
  37. }
  38. protected Object ThisLock
  39. {
  40. get { return this.thisLock; }
  41. }
  42. public string DefaultServiceBinding
  43. {
  44. get
  45. {
  46. if (this.defaultServiceBinding == null)
  47. {
  48. lock (ThisLock)
  49. {
  50. if (this.defaultServiceBinding == null)
  51. {
  52. this.defaultServiceBinding = SecurityUtils.GetSpnFromIdentity(
  53. SecurityUtils.CreateWindowsIdentity(),
  54. new EndpointAddress(ListenUri));
  55. }
  56. }
  57. }
  58. return this.defaultServiceBinding;
  59. }
  60. set { this.defaultServiceBinding = value; }
  61. }
  62. // abstract methods
  63. public abstract XmlDictionaryString NegotiationValueType { get; }
  64. protected abstract ReadOnlyCollection<IAuthorizationPolicy> ValidateSspiNegotiation(ISspiNegotiation sspiNegotiation);
  65. protected abstract SspiNegotiationTokenAuthenticatorState CreateSspiState(byte[] incomingBlob, string incomingValueTypeUri);
  66. // helpers
  67. protected virtual void IssueServiceToken(SspiNegotiationTokenAuthenticatorState sspiState, ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies, out SecurityContextSecurityToken serviceToken, out WrappedKeySecurityToken proofToken,
  68. out int issuedKeySize)
  69. {
  70. UniqueId contextId = SecurityUtils.GenerateUniqueId();
  71. string id = SecurityUtils.GenerateId();
  72. if (sspiState.RequestedKeySize == 0)
  73. {
  74. issuedKeySize = this.SecurityAlgorithmSuite.DefaultSymmetricKeyLength;
  75. }
  76. else
  77. {
  78. issuedKeySize = sspiState.RequestedKeySize;
  79. }
  80. byte[] key = new byte[issuedKeySize / 8];
  81. CryptoHelper.FillRandomBytes(key);
  82. DateTime effectiveTime = DateTime.UtcNow;
  83. DateTime expirationTime = TimeoutHelper.Add(effectiveTime, this.ServiceTokenLifetime);
  84. serviceToken = IssueSecurityContextToken(contextId, id, key, effectiveTime, expirationTime, authorizationPolicies, this.EncryptStateInServiceToken);
  85. proofToken = new WrappedKeySecurityToken(string.Empty, key, sspiState.SspiNegotiation);
  86. }
  87. protected virtual void ValidateIncomingBinaryNegotiation(BinaryNegotiation incomingNego)
  88. {
  89. if (incomingNego == null)
  90. {
  91. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.NoBinaryNegoToReceive)));
  92. }
  93. incomingNego.Validate(this.NegotiationValueType);
  94. }
  95. protected virtual BinaryNegotiation GetOutgoingBinaryNegotiation(ISspiNegotiation sspiNegotiation, byte[] outgoingBlob)
  96. {
  97. return new BinaryNegotiation(this.NegotiationValueType, outgoingBlob);
  98. }
  99. static void AddToDigest(HashAlgorithm negotiationDigest, Stream stream)
  100. {
  101. stream.Flush();
  102. stream.Seek(0, SeekOrigin.Begin);
  103. CanonicalizationDriver canonicalizer = new CanonicalizationDriver();
  104. canonicalizer.SetInput(stream);
  105. byte[] canonicalizedData = canonicalizer.GetBytes();
  106. lock (negotiationDigest)
  107. {
  108. negotiationDigest.TransformBlock(canonicalizedData, 0, canonicalizedData.Length, canonicalizedData, 0);
  109. }
  110. }
  111. static void AddToDigest(SspiNegotiationTokenAuthenticatorState sspiState, RequestSecurityToken rst)
  112. {
  113. MemoryStream stream = new MemoryStream();
  114. XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream);
  115. rst.RequestSecurityTokenXml.WriteTo(writer);
  116. writer.Flush();
  117. AddToDigest(sspiState.NegotiationDigest, stream);
  118. }
  119. static void AddToDigest(SspiNegotiationTokenAuthenticatorState sspiState, RequestSecurityTokenResponse rstr, bool wasReceived)
  120. {
  121. MemoryStream stream = new MemoryStream();
  122. XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream);
  123. if (wasReceived)
  124. {
  125. rstr.RequestSecurityTokenResponseXml.WriteTo(writer);
  126. }
  127. else
  128. {
  129. rstr.WriteTo(writer);
  130. }
  131. writer.Flush();
  132. AddToDigest(sspiState.NegotiationDigest, stream);
  133. }
  134. static byte[] ComputeAuthenticator(SspiNegotiationTokenAuthenticatorState sspiState, byte[] key)
  135. {
  136. byte[] negotiationHash;
  137. lock (sspiState.NegotiationDigest)
  138. {
  139. sspiState.NegotiationDigest.TransformFinalBlock(CryptoHelper.EmptyBuffer, 0, 0);
  140. negotiationHash = sspiState.NegotiationDigest.Hash;
  141. }
  142. Psha1DerivedKeyGenerator generator = new Psha1DerivedKeyGenerator(key);
  143. return generator.GenerateDerivedKey(SecurityUtils.CombinedHashLabel, negotiationHash, SecurityNegotiationConstants.NegotiationAuthenticatorSize, 0);
  144. }
  145. // overrides
  146. protected override bool IsMultiLegNegotiation
  147. {
  148. get
  149. {
  150. return true;
  151. }
  152. }
  153. protected override Binding GetNegotiationBinding(Binding binding)
  154. {
  155. return binding;
  156. }
  157. protected override MessageFilter GetListenerFilter()
  158. {
  159. return new SspiNegotiationFilter(this);
  160. }
  161. protected override BodyWriter ProcessRequestSecurityToken(Message request, RequestSecurityToken requestSecurityToken, out SspiNegotiationTokenAuthenticatorState negotiationState)
  162. {
  163. if (request == null)
  164. {
  165. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request");
  166. }
  167. if (requestSecurityToken == null)
  168. {
  169. throw TraceUtility.ThrowHelperArgumentNull("requestSecurityToken", request);
  170. }
  171. if (requestSecurityToken.RequestType != null && requestSecurityToken.RequestType != this.StandardsManager.TrustDriver.RequestTypeIssue)
  172. {
  173. throw TraceUtility.ThrowHelperWarning(new SecurityNegotiationException(SR.GetString(SR.InvalidRstRequestType, requestSecurityToken.RequestType)), request);
  174. }
  175. BinaryNegotiation incomingNego = requestSecurityToken.GetBinaryNegotiation();
  176. ValidateIncomingBinaryNegotiation(incomingNego);
  177. negotiationState = CreateSspiState(incomingNego.GetNegotiationData(), incomingNego.ValueTypeUri);
  178. AddToDigest(negotiationState, requestSecurityToken);
  179. negotiationState.Context = requestSecurityToken.Context;
  180. if (requestSecurityToken.KeySize != 0)
  181. {
  182. WSTrust.Driver.ValidateRequestedKeySize(requestSecurityToken.KeySize, this.SecurityAlgorithmSuite);
  183. }
  184. negotiationState.RequestedKeySize = requestSecurityToken.KeySize;
  185. string appliesToName;
  186. string appliesToNamespace;
  187. requestSecurityToken.GetAppliesToQName(out appliesToName, out appliesToNamespace);
  188. if (appliesToName == AddressingStrings.EndpointReference && appliesToNamespace == request.Version.Addressing.Namespace)
  189. {
  190. DataContractSerializer serializer;
  191. if (request.Version.Addressing == AddressingVersion.WSAddressing10)
  192. {
  193. serializer = DataContractSerializerDefaults.CreateSerializer(typeof(EndpointAddress10), DataContractSerializerDefaults.MaxItemsInObjectGraph);
  194. negotiationState.AppliesTo = requestSecurityToken.GetAppliesTo<EndpointAddress10>(serializer).ToEndpointAddress();
  195. }
  196. else if (request.Version.Addressing == AddressingVersion.WSAddressingAugust2004)
  197. {
  198. serializer = DataContractSerializerDefaults.CreateSerializer(typeof(EndpointAddressAugust2004), DataContractSerializerDefaults.MaxItemsInObjectGraph);
  199. negotiationState.AppliesTo = requestSecurityToken.GetAppliesTo<EndpointAddressAugust2004>(serializer).ToEndpointAddress();
  200. }
  201. else
  202. {
  203. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
  204. new ProtocolException(SR.GetString(SR.AddressingVersionNotSupported, request.Version.Addressing)));
  205. }
  206. negotiationState.AppliesToSerializer = serializer;
  207. }
  208. return ProcessNegotiation(negotiationState, request, incomingNego);
  209. }
  210. protected override BodyWriter ProcessRequestSecurityTokenResponse(SspiNegotiationTokenAuthenticatorState negotiationState, Message request, RequestSecurityTokenResponse requestSecurityTokenResponse)
  211. {
  212. if (request == null)
  213. {
  214. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request");
  215. }
  216. if (requestSecurityTokenResponse == null)
  217. {
  218. throw TraceUtility.ThrowHelperArgumentNull("requestSecurityTokenResponse", request);
  219. }
  220. if (requestSecurityTokenResponse.Context != negotiationState.Context)
  221. {
  222. throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.BadSecurityNegotiationContext)), request);
  223. }
  224. AddToDigest(negotiationState, requestSecurityTokenResponse, true);
  225. BinaryNegotiation incomingNego = requestSecurityTokenResponse.GetBinaryNegotiation();
  226. ValidateIncomingBinaryNegotiation(incomingNego);
  227. return ProcessNegotiation(negotiationState, request, incomingNego);
  228. }
  229. BodyWriter ProcessNegotiation(SspiNegotiationTokenAuthenticatorState negotiationState, Message incomingMessage, BinaryNegotiation incomingNego)
  230. {
  231. ISspiNegotiation sspiNegotiation = negotiationState.SspiNegotiation;
  232. byte[] outgoingBlob = sspiNegotiation.GetOutgoingBlob(incomingNego.GetNegotiationData(),
  233. SecurityUtils.GetChannelBindingFromMessage(incomingMessage),
  234. this.extendedProtectionPolicy);
  235. if (sspiNegotiation.IsValidContext == false)
  236. {
  237. throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.InvalidSspiNegotiation)), incomingMessage);
  238. }
  239. // if there is no blob to send back the nego must be complete from the server side
  240. if (outgoingBlob == null && sspiNegotiation.IsCompleted == false)
  241. {
  242. throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.NoBinaryNegoToSend)), incomingMessage);
  243. }
  244. BinaryNegotiation outgoingBinaryNegotiation;
  245. if (outgoingBlob != null)
  246. {
  247. outgoingBinaryNegotiation = GetOutgoingBinaryNegotiation(sspiNegotiation, outgoingBlob);
  248. }
  249. else
  250. {
  251. outgoingBinaryNegotiation = null;
  252. }
  253. BodyWriter replyBody;
  254. if (sspiNegotiation.IsCompleted)
  255. {
  256. ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies = ValidateSspiNegotiation(sspiNegotiation);
  257. SecurityContextSecurityToken serviceToken;
  258. WrappedKeySecurityToken proofToken;
  259. int issuedKeySize;
  260. IssueServiceToken(negotiationState, authorizationPolicies, out serviceToken, out proofToken, out issuedKeySize);
  261. negotiationState.SetServiceToken(serviceToken);
  262. SecurityKeyIdentifierClause externalTokenReference = this.IssuedSecurityTokenParameters.CreateKeyIdentifierClause(serviceToken, SecurityTokenReferenceStyle.External);
  263. SecurityKeyIdentifierClause internalTokenReference = this.IssuedSecurityTokenParameters.CreateKeyIdentifierClause(serviceToken, SecurityTokenReferenceStyle.Internal);
  264. RequestSecurityTokenResponse dummyRstr = new RequestSecurityTokenResponse(this.StandardsManager);
  265. dummyRstr.Context = negotiationState.Context;
  266. dummyRstr.KeySize = issuedKeySize;
  267. dummyRstr.TokenType = this.SecurityContextTokenUri;
  268. if (outgoingBinaryNegotiation != null)
  269. {
  270. dummyRstr.SetBinaryNegotiation(outgoingBinaryNegotiation);
  271. }
  272. dummyRstr.RequestedUnattachedReference = externalTokenReference;
  273. dummyRstr.RequestedAttachedReference = internalTokenReference;
  274. dummyRstr.SetLifetime(serviceToken.ValidFrom, serviceToken.ValidTo);
  275. if (negotiationState.AppliesTo != null)
  276. {
  277. if (incomingMessage.Version.Addressing == AddressingVersion.WSAddressing10)
  278. {
  279. dummyRstr.SetAppliesTo<EndpointAddress10>(EndpointAddress10.FromEndpointAddress(
  280. negotiationState.AppliesTo),
  281. negotiationState.AppliesToSerializer);
  282. }
  283. else if (incomingMessage.Version.Addressing == AddressingVersion.WSAddressingAugust2004)
  284. {
  285. dummyRstr.SetAppliesTo<EndpointAddressAugust2004>(EndpointAddressAugust2004.FromEndpointAddress(
  286. negotiationState.AppliesTo),
  287. negotiationState.AppliesToSerializer);
  288. }
  289. else
  290. {
  291. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
  292. new ProtocolException(SR.GetString(SR.AddressingVersionNotSupported, incomingMessage.Version.Addressing)));
  293. }
  294. }
  295. dummyRstr.MakeReadOnly();
  296. AddToDigest(negotiationState, dummyRstr, false);
  297. RequestSecurityTokenResponse negotiationRstr = new RequestSecurityTokenResponse(this.StandardsManager);
  298. negotiationRstr.RequestedSecurityToken = serviceToken;
  299. negotiationRstr.RequestedProofToken = proofToken;
  300. negotiationRstr.Context = negotiationState.Context;
  301. negotiationRstr.KeySize = issuedKeySize;
  302. negotiationRstr.TokenType = this.SecurityContextTokenUri;
  303. if (outgoingBinaryNegotiation != null)
  304. {
  305. negotiationRstr.SetBinaryNegotiation(outgoingBinaryNegotiation);
  306. }
  307. negotiationRstr.RequestedAttachedReference = internalTokenReference;
  308. negotiationRstr.RequestedUnattachedReference = externalTokenReference;
  309. if (negotiationState.AppliesTo != null)
  310. {
  311. if (incomingMessage.Version.Addressing == AddressingVersion.WSAddressing10)
  312. {
  313. negotiationRstr.SetAppliesTo<EndpointAddress10>(
  314. EndpointAddress10.FromEndpointAddress(negotiationState.AppliesTo),
  315. negotiationState.AppliesToSerializer);
  316. }
  317. else if (incomingMessage.Version.Addressing == AddressingVersion.WSAddressingAugust2004)
  318. {
  319. negotiationRstr.SetAppliesTo<EndpointAddressAugust2004>(
  320. EndpointAddressAugust2004.FromEndpointAddress(negotiationState.AppliesTo),
  321. negotiationState.AppliesToSerializer);
  322. }
  323. else
  324. {
  325. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
  326. new ProtocolException(SR.GetString(SR.AddressingVersionNotSupported, incomingMessage.Version.Addressing)));
  327. }
  328. }
  329. negotiationRstr.MakeReadOnly();
  330. byte[] authenticator = ComputeAuthenticator(negotiationState, serviceToken.GetKeyBytes());
  331. RequestSecurityTokenResponse authenticatorRstr = new RequestSecurityTokenResponse(this.StandardsManager);
  332. authenticatorRstr.Context = negotiationState.Context;
  333. authenticatorRstr.SetAuthenticator(authenticator);
  334. authenticatorRstr.MakeReadOnly();
  335. List<RequestSecurityTokenResponse> rstrList = new List<RequestSecurityTokenResponse>(2);
  336. rstrList.Add(negotiationRstr);
  337. rstrList.Add(authenticatorRstr);
  338. replyBody = new RequestSecurityTokenResponseCollection(rstrList, this.StandardsManager);
  339. }
  340. else
  341. {
  342. RequestSecurityTokenResponse rstr = new RequestSecurityTokenResponse(this.StandardsManager);
  343. rstr.Context = negotiationState.Context;
  344. rstr.SetBinaryNegotiation(outgoingBinaryNegotiation);
  345. rstr.MakeReadOnly();
  346. AddToDigest(negotiationState, rstr, false);
  347. replyBody = rstr;
  348. }
  349. return replyBody;
  350. }
  351. class SspiNegotiationFilter : HeaderFilter
  352. {
  353. SspiNegotiationTokenAuthenticator authenticator;
  354. public SspiNegotiationFilter(SspiNegotiationTokenAuthenticator authenticator)
  355. {
  356. this.authenticator = authenticator;
  357. }
  358. public override bool Match(Message message)
  359. {
  360. if (message.Headers.Action == authenticator.RequestSecurityTokenAction.Value
  361. || message.Headers.Action == authenticator.RequestSecurityTokenResponseAction.Value)
  362. {
  363. return !SecurityVersion.Default.DoesMessageContainSecurityHeader(message);
  364. }
  365. else
  366. {
  367. return false;
  368. }
  369. }
  370. }
  371. }
  372. }