IdentityModelServiceAuthorizationManager.cs 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806
  1. //------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //------------------------------------------------------------
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Collections.ObjectModel;
  7. using System.Diagnostics;
  8. using System.IdentityModel;
  9. using System.IdentityModel.Claims;
  10. using System.IdentityModel.Policy;
  11. using System.IdentityModel.Tokens;
  12. using System.Linq;
  13. using System.Security.Claims;
  14. using System.Security.Principal;
  15. using System.ServiceModel.Channels;
  16. using System.ServiceModel.Description;
  17. using System.ServiceModel.Diagnostics;
  18. using System.Xml;
  19. using SysAuthorizationContext = System.IdentityModel.Policy.AuthorizationContext;
  20. namespace System.ServiceModel.Security
  21. {
  22. /// <summary>
  23. /// Custom ServiceAuthorizationManager implementation. This class substitues the WCF
  24. /// generated IAuthorizationPolicies with
  25. /// <see cref="System.IdentityModel.Tokens.AuthorizationPolicy"/>. These
  26. /// policies do not participate in the EvaluationContext and hence will render an
  27. /// empty WCF AuthorizationConext. Once this AuthorizationManager is substitued to
  28. /// a ServiceHost, only <see cref="System.Security.Claims.ClaimsPrincipal"/>
  29. /// will be available for Authorization decisions.
  30. /// </summary>
  31. class IdentityModelServiceAuthorizationManager : ServiceAuthorizationManager
  32. {
  33. /// <summary>
  34. /// Authorization policy for anonymous authentication.
  35. /// </summary>
  36. protected static readonly ReadOnlyCollection<IAuthorizationPolicy> AnonymousAuthorizationPolicy
  37. = new ReadOnlyCollection<IAuthorizationPolicy>(
  38. new List<IAuthorizationPolicy>() { new AuthorizationPolicy(new ClaimsIdentity()) });
  39. /// <summary>
  40. /// Override of the base class method. Substitues WCF IAuthorizationPolicy with
  41. /// <see cref="System.IdentityModel.Tokens.AuthorizationPolicy"/>.
  42. /// </summary>
  43. /// <param name="operationContext">Current OperationContext that contains all the IAuthorizationPolicies.</param>
  44. /// <returns>Read-Only collection of <see cref="IAuthorizationPolicy"/> </returns>
  45. protected override ReadOnlyCollection<IAuthorizationPolicy> GetAuthorizationPolicies(OperationContext operationContext)
  46. {
  47. //
  48. // Make sure we always return at least one claims identity, if there are no auth policies
  49. // that contain any identities, then return an anonymous identity wrapped in an authorization policy.
  50. //
  51. // If we do not, then Thread.CurrentPrincipal may end up being null inside service operations after the
  52. // authorization polices are evaluated since ServiceCredentials.ConfigureServiceHost will
  53. // turn the PrincipalPermissionMode knob to Custom.
  54. //
  55. ReadOnlyCollection<IAuthorizationPolicy> baseAuthorizationPolicies = base.GetAuthorizationPolicies(operationContext);
  56. if (baseAuthorizationPolicies == null)
  57. {
  58. return AnonymousAuthorizationPolicy;
  59. }
  60. else
  61. {
  62. ServiceCredentials sc = GetServiceCredentials();
  63. AuthorizationPolicy transformedPolicy = TransformAuthorizationPolicies(baseAuthorizationPolicies,
  64. sc.IdentityConfiguration.SecurityTokenHandlers,
  65. true);
  66. if (transformedPolicy == null || transformedPolicy.IdentityCollection.Count == 0)
  67. {
  68. return AnonymousAuthorizationPolicy;
  69. }
  70. return (new List<IAuthorizationPolicy>() { transformedPolicy }).AsReadOnly();
  71. }
  72. }
  73. internal static AuthorizationPolicy TransformAuthorizationPolicies(
  74. ReadOnlyCollection<IAuthorizationPolicy> baseAuthorizationPolicies,
  75. SecurityTokenHandlerCollection securityTokenHandlerCollection,
  76. bool includeTransportTokens)
  77. {
  78. List<ClaimsIdentity> identities = new List<ClaimsIdentity>();
  79. List<IAuthorizationPolicy> uncheckedAuthorizationPolicies = new List<IAuthorizationPolicy>();
  80. //
  81. // STEP 1: Filter out the IAuthorizationPolicy that WCF generated. These
  82. // are generated as IDFx does not have a proper SecurityTokenHandler
  83. // to handle these. For example, SSPI at message layer and all token
  84. // types at the Transport layer.
  85. //
  86. foreach (IAuthorizationPolicy authPolicy in baseAuthorizationPolicies)
  87. {
  88. if ((authPolicy is SctAuthorizationPolicy) ||
  89. (authPolicy is EndpointAuthorizationPolicy))
  90. {
  91. //
  92. // We ignore the SctAuthorizationPolicy if any found as they were created
  93. // as wrapper policies to hold the primary identity claim during a token renewal path.
  94. // WCF would otherwise fault thinking the token issuance and renewal identities are
  95. // different. This policy should be treated as a dummy policy and thereby should not be transformed.
  96. //
  97. // We ignore EndpointAuthorizationPolicy as well. This policy is used only to carry
  98. // the endpoint Identity and there is no useful claims that this policy contributes.
  99. //
  100. continue;
  101. }
  102. AuthorizationPolicy idfxAuthPolicy = authPolicy as AuthorizationPolicy;
  103. if (idfxAuthPolicy != null)
  104. {
  105. // Identities obtained from the Tokens in the message layer would
  106. identities.AddRange(idfxAuthPolicy.IdentityCollection);
  107. }
  108. else
  109. {
  110. uncheckedAuthorizationPolicies.Add(authPolicy);
  111. }
  112. }
  113. //
  114. // STEP 2: Generate IDFx claims from the transport token
  115. //
  116. if (includeTransportTokens && (OperationContext.Current != null) &&
  117. (OperationContext.Current.IncomingMessageProperties != null) &&
  118. (OperationContext.Current.IncomingMessageProperties.Security != null) &&
  119. (OperationContext.Current.IncomingMessageProperties.Security.TransportToken != null))
  120. {
  121. SecurityToken transportToken =
  122. OperationContext.Current.IncomingMessageProperties.Security.TransportToken.SecurityToken;
  123. ReadOnlyCollection<IAuthorizationPolicy> policyCollection =
  124. OperationContext.Current.IncomingMessageProperties.Security.TransportToken.SecurityTokenPolicies;
  125. bool isWcfAuthPolicy = true;
  126. foreach (IAuthorizationPolicy policy in policyCollection)
  127. {
  128. //
  129. // Iterate over each of the policies in the policyCollection to make sure
  130. // we don't have an idfx policy, if we do we will not consider this as
  131. // a wcf auth policy: Such a case will be hit for the SslStreamSecurityBinding over net tcp
  132. //
  133. if (policy is AuthorizationPolicy)
  134. {
  135. isWcfAuthPolicy = false;
  136. break;
  137. }
  138. }
  139. if (isWcfAuthPolicy)
  140. {
  141. ReadOnlyCollection<ClaimsIdentity> tranportTokenIdentities = GetTransportTokenIdentities(transportToken);
  142. identities.AddRange(tranportTokenIdentities);
  143. //
  144. // NOTE: In the below code, we are trying to identify the IAuthorizationPolicy that WCF
  145. // created for the Transport token and eliminate it. This assumes that any client Security
  146. // Token that came in the Security header would have been validated by the SecurityTokenHandler
  147. // and hence would have created a IDFx AuthorizationPolicy.
  148. // For example, if X.509 Certificate was used to authenticate the client at the transport layer
  149. // and then again at the Message security layer we depend on our TokenHandlers to have been in
  150. // place to validate the X.509 Certificate at the message layer. This would clearly distinguish
  151. // which policy was created for the Transport token by WCF.
  152. //
  153. EliminateTransportTokenPolicy(transportToken, tranportTokenIdentities, uncheckedAuthorizationPolicies);
  154. }
  155. }
  156. //
  157. // STEP 3: Process any uncheckedAuthorizationPolicies here. Convert these to IDFx
  158. // Claims.
  159. //
  160. if (uncheckedAuthorizationPolicies.Count > 0)
  161. {
  162. identities.AddRange(ConvertToIDFxIdentities(uncheckedAuthorizationPolicies, securityTokenHandlerCollection));
  163. }
  164. //
  165. // STEP 4: Create an AuthorizationPolicy with all the ClaimsIdentities.
  166. //
  167. AuthorizationPolicy idfxAuthorizationPolicy = null;
  168. if (identities.Count == 0)
  169. {
  170. //
  171. // No IDFx ClaimsIdentity was found. Return AnonymousIdentity.
  172. //
  173. idfxAuthorizationPolicy = new AuthorizationPolicy(new ClaimsIdentity());
  174. }
  175. else
  176. {
  177. idfxAuthorizationPolicy = new AuthorizationPolicy(identities.AsReadOnly());
  178. }
  179. return idfxAuthorizationPolicy;
  180. }
  181. /// <summary>
  182. /// Creates ClaimsIdentityCollection for the given Transport SecurityToken.
  183. /// </summary>
  184. /// <param name="transportToken">Client SecurityToken provided at the Transport layer.</param>
  185. /// <returns>ClaimsIdentityCollection built from the Transport SecurityToken</returns>
  186. static ReadOnlyCollection<ClaimsIdentity> GetTransportTokenIdentities(SecurityToken transportToken)
  187. {
  188. if (transportToken == null)
  189. {
  190. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("transportToken");
  191. }
  192. ServiceCredentials serviceCreds = GetServiceCredentials();
  193. List<ClaimsIdentity> transportTokenIdentityCollection = new List<ClaimsIdentity>();
  194. //////////////////////////////////////////////////////////////////////////////////////////
  195. //
  196. // There are 5 well-known Client Authentication types at the transport layer. Each of these will
  197. // result either in a WindowsSecurityToken, X509SecurityToken or UserNameSecurityToken.
  198. // All other type of credentials (like OAuth token) result other token that will be passed trough regular validation process.
  199. //
  200. // ClientCredential Type || Transport Token Type
  201. // -------------------------------------------------------------------
  202. // Basic -> UserNameSecurityToken (In Self-hosted case)
  203. // Basic -> WindowsSecurityToken (In Web-Hosted case)
  204. // NTLM -> WindowsSecurityToken
  205. // Negotiate -> WindowsSecurityToken
  206. // Windows -> WindowsSecurityToken
  207. // Certificate -> X509SecurityToken
  208. //
  209. //////////////////////////////////////////////////////////////////////////////////////////
  210. WindowsSecurityToken windowsSecurityToken = transportToken as WindowsSecurityToken;
  211. if ( windowsSecurityToken != null )
  212. {
  213. WindowsIdentity claimsIdentity = new WindowsIdentity( windowsSecurityToken.WindowsIdentity.Token,
  214. AuthenticationTypes.Windows );
  215. AddAuthenticationMethod( claimsIdentity, AuthenticationMethods.Windows );
  216. AddAuthenticationInstantClaim(claimsIdentity, XmlConvert.ToString(DateTime.UtcNow, DateTimeFormats.Generated));
  217. // Just reflect on the wrapped WindowsIdentity and build the WindowsClaimsIdentity class.
  218. transportTokenIdentityCollection.Add(claimsIdentity);
  219. }
  220. else
  221. {
  222. // WCF does not call our SecurityTokenHandlers for the Transport token. So run the token through
  223. // the SecurityTokenHandler and generate claims for this token.
  224. transportTokenIdentityCollection.AddRange(serviceCreds.IdentityConfiguration.SecurityTokenHandlers.ValidateToken( transportToken ));
  225. }
  226. return transportTokenIdentityCollection.AsReadOnly();
  227. }
  228. /// <summary>
  229. /// Given a collection of IAuthorizationPolicies this method will eliminate the IAuthorizationPolicy
  230. /// that was created for the given transport Security Token. The method modifies the given collection
  231. /// of IAuthorizationPolicy.
  232. /// </summary>
  233. /// <param name="transportToken">Client's Security Token provided at the transport layer.</param>
  234. /// <param name="tranportTokenIdentities"></param>
  235. /// <param name="baseAuthorizationPolicies">Collection of IAuthorizationPolicies that were created by WCF.</param>
  236. static void EliminateTransportTokenPolicy(
  237. SecurityToken transportToken,
  238. IEnumerable<ClaimsIdentity> tranportTokenIdentities,
  239. List<IAuthorizationPolicy> baseAuthorizationPolicies)
  240. {
  241. if (transportToken == null)
  242. {
  243. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("transportToken");
  244. }
  245. if (tranportTokenIdentities == null)
  246. {
  247. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tranportTokenIdentities");
  248. }
  249. if (baseAuthorizationPolicies == null)
  250. {
  251. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("baseAuthorizationPolicy");
  252. }
  253. if (baseAuthorizationPolicies.Count == 0)
  254. {
  255. // This should never happen in our current configuration. IDFx token handlers do not validate
  256. // client tokens present at the transport level. So we should atleast have one IAuthorizationPolicy
  257. // that WCF generated for the transport token.
  258. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("baseAuthorizationPolicy", SR.GetString(SR.ID0020));
  259. }
  260. //
  261. // We will process one IAuthorizationPolicy at a time. Transport token will have been authenticated
  262. // by WCF and would have created a IAuthorizationPolicy for the same. If the transport token is a X.509
  263. // SecurityToken and 'mapToWindows' was set to true then the IAuthorizationPolicy that was created
  264. // by WCF will have two Claimsets, a X509ClaimSet and a WindowsClaimSet. We need to prune out this case
  265. // and ignore both these Claimsets as we have made a call to the token handler to authenticate this
  266. // token above. If we create a AuthorizationContext using all the IAuthorizationPolicies then all
  267. // the claimsets are merged and it becomes hard to identify this case.
  268. //
  269. IAuthorizationPolicy policyToEliminate = null;
  270. foreach (IAuthorizationPolicy authPolicy in baseAuthorizationPolicies)
  271. {
  272. if (DoesPolicyMatchTransportToken(transportToken, tranportTokenIdentities, authPolicy))
  273. {
  274. policyToEliminate = authPolicy;
  275. break;
  276. }
  277. }
  278. if (policyToEliminate == null)
  279. {
  280. throw DiagnosticUtility.ExceptionUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4271, transportToken));
  281. }
  282. baseAuthorizationPolicies.Remove(policyToEliminate);
  283. }
  284. /// <summary>
  285. /// Returns true if the IAuthorizationPolicy could have been created from the given Transport token.
  286. /// The method can handle only X509SecurityToken and WindowsSecurityToken.
  287. /// </summary>
  288. /// <param name="transportToken">Client's Security Token provided at the transport layer.</param>
  289. /// <param name="tranportTokenIdentities">A collection of <see cref="ClaimsIdentity"/> to match.</param>
  290. /// <param name="authPolicy">IAuthorizationPolicy to check.</param>
  291. /// <returns>True if the IAuthorizationPolicy could have been created from the given Transpor token.</returns>
  292. static bool DoesPolicyMatchTransportToken(
  293. SecurityToken transportToken,
  294. IEnumerable<ClaimsIdentity> tranportTokenIdentities,
  295. IAuthorizationPolicy authPolicy
  296. )
  297. {
  298. if (transportToken == null)
  299. {
  300. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("transportToken");
  301. }
  302. if (tranportTokenIdentities == null)
  303. {
  304. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tranportTokenIdentities");
  305. }
  306. if (authPolicy == null)
  307. {
  308. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("authPolicy");
  309. }
  310. //////////////////////////////////////////////////////////////////////////////////////////
  311. //
  312. // There are 5 Client Authentication types at the transport layer. Each of these will
  313. // result either in a WindowsSecurityToken, X509SecurityToken or UserNameSecurityToken.
  314. //
  315. // ClientCredential Type || Transport Token Type
  316. // -------------------------------------------------------------------
  317. // Basic -> UserNameSecurityToken (In Self-hosted case)
  318. // Basic -> WindowsSecurityToken (In Web-Hosted case)
  319. // NTLM -> WindowsSecurityToken
  320. // Negotiate -> WindowsSecurityToken
  321. // Windows -> WindowsSecurityToken
  322. // Certificate -> X509SecurityToken
  323. //
  324. //////////////////////////////////////////////////////////////////////////////////////////
  325. X509SecurityToken x509SecurityToken = transportToken as X509SecurityToken;
  326. SysAuthorizationContext defaultAuthContext = SysAuthorizationContext.CreateDefaultAuthorizationContext(new List<IAuthorizationPolicy>() { authPolicy });
  327. foreach (System.IdentityModel.Claims.ClaimSet claimset in defaultAuthContext.ClaimSets)
  328. {
  329. if (x509SecurityToken != null)
  330. {
  331. // Check if the claimset contains a claim that matches the X.509 certificate thumbprint.
  332. if (claimset.ContainsClaim(new System.IdentityModel.Claims.Claim(
  333. System.IdentityModel.Claims.ClaimTypes.Thumbprint,
  334. x509SecurityToken.Certificate.GetCertHash(),
  335. System.IdentityModel.Claims.Rights.PossessProperty)))
  336. {
  337. return true;
  338. }
  339. }
  340. else
  341. {
  342. // For WindowsSecurityToken and UserNameSecurityToken check that IClaimsdentity.Name
  343. // matches the Name Claim in the ClaimSet.
  344. // In most cases, we will have only one Identity in the ClaimsIdentityCollection
  345. // generated from transport token.
  346. foreach (ClaimsIdentity transportTokenIdentity in tranportTokenIdentities)
  347. {
  348. if (claimset.ContainsClaim(new System.IdentityModel.Claims.Claim(
  349. System.IdentityModel.Claims.ClaimTypes.Name,
  350. transportTokenIdentity.Name,
  351. System.IdentityModel.Claims.Rights.PossessProperty), new ClaimStringValueComparer()))
  352. {
  353. return true;
  354. }
  355. }
  356. }
  357. }
  358. return false;
  359. }
  360. /// <summary>
  361. /// Converts a given set of WCF IAuthorizationPolicy to WIF ClaimIdentities.
  362. /// </summary>
  363. /// <param name="authorizationPolicies">Set of AuthorizationPolicies to convert to IDFx.</param>
  364. /// <param name="securityTokenHandlerCollection">The SecurityTokenHandlerCollection to use.</param>
  365. /// <returns>ClaimsIdentityCollection</returns>
  366. static ReadOnlyCollection<ClaimsIdentity> ConvertToIDFxIdentities(IList<IAuthorizationPolicy> authorizationPolicies,
  367. SecurityTokenHandlerCollection securityTokenHandlerCollection)
  368. {
  369. if (authorizationPolicies == null)
  370. {
  371. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("authorizationPolicies");
  372. }
  373. if (securityTokenHandlerCollection == null)
  374. {
  375. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityTokenHandlerCollection");
  376. }
  377. List<ClaimsIdentity> identities = new List<ClaimsIdentity>();
  378. SecurityTokenSpecification kerberosTokenSpecification = null;
  379. SysAuthorizationContext kerberosAuthContext = null;
  380. if ((OperationContext.Current != null) &&
  381. (OperationContext.Current.IncomingMessageProperties != null) &&
  382. (OperationContext.Current.IncomingMessageProperties.Security != null))
  383. {
  384. SecurityMessageProperty securityMessageProperty = OperationContext.Current.IncomingMessageProperties.Security;
  385. foreach (SecurityTokenSpecification tokenSpecification in new SecurityTokenSpecificationEnumerable(securityMessageProperty))
  386. {
  387. if (tokenSpecification.SecurityToken is KerberosReceiverSecurityToken)
  388. {
  389. kerberosTokenSpecification = tokenSpecification;
  390. kerberosAuthContext = SysAuthorizationContext.CreateDefaultAuthorizationContext(kerberosTokenSpecification.SecurityTokenPolicies);
  391. break;
  392. }
  393. }
  394. }
  395. bool hasKerberosTokenPolicyMatched = false;
  396. foreach (IAuthorizationPolicy policy in authorizationPolicies)
  397. {
  398. bool authPolicyHandled = false;
  399. if ((kerberosTokenSpecification != null) && !hasKerberosTokenPolicyMatched)
  400. {
  401. if (kerberosTokenSpecification.SecurityTokenPolicies.Contains(policy))
  402. {
  403. hasKerberosTokenPolicyMatched = true;
  404. }
  405. else
  406. {
  407. SysAuthorizationContext authContext = SysAuthorizationContext.CreateDefaultAuthorizationContext(new List<IAuthorizationPolicy>() { policy });
  408. // Kerberos creates only one ClaimSet. So any more ClaimSet would mean that this is not a Policy created from Kerberos.
  409. if (authContext.ClaimSets.Count == 1)
  410. {
  411. bool allClaimsMatched = true;
  412. foreach (System.IdentityModel.Claims.Claim c in authContext.ClaimSets[0])
  413. {
  414. if (!kerberosAuthContext.ClaimSets[0].ContainsClaim(c))
  415. {
  416. allClaimsMatched = false;
  417. break;
  418. }
  419. }
  420. hasKerberosTokenPolicyMatched = allClaimsMatched;
  421. }
  422. }
  423. if (hasKerberosTokenPolicyMatched)
  424. {
  425. SecurityTokenHandler tokenHandler = securityTokenHandlerCollection[kerberosTokenSpecification.SecurityToken];
  426. if ((tokenHandler != null) && tokenHandler.CanValidateToken)
  427. {
  428. identities.AddRange(tokenHandler.ValidateToken(kerberosTokenSpecification.SecurityToken));
  429. authPolicyHandled = true;
  430. }
  431. }
  432. }
  433. if (!authPolicyHandled)
  434. {
  435. SysAuthorizationContext defaultAuthContext = SysAuthorizationContext.CreateDefaultAuthorizationContext(new List<IAuthorizationPolicy>() { policy });
  436. //
  437. // Merge all ClaimSets to IClaimsIdentity.
  438. //
  439. identities.Add(ConvertToIDFxIdentity(defaultAuthContext.ClaimSets, securityTokenHandlerCollection.Configuration));
  440. }
  441. }
  442. return identities.AsReadOnly();
  443. }
  444. /// <summary>
  445. /// Converts a given set of WCF ClaimSets to IDFx ClaimsIdentity.
  446. /// </summary>
  447. /// <param name="claimSets">Collection of <see cref="ClaimSet"/> to convert to IDFx.</param>
  448. /// <param name="securityTokenHandlerConfiguration">The SecurityTokenHandlerConfiguration to use.</param>
  449. /// <returns>ClaimsIdentity</returns>
  450. static ClaimsIdentity ConvertToIDFxIdentity(IList<ClaimSet> claimSets, SecurityTokenHandlerConfiguration securityTokenHandlerConfiguration)
  451. {
  452. if (claimSets == null)
  453. {
  454. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("claimSets");
  455. }
  456. ClaimsIdentity claimsIdentity = null;
  457. foreach (System.IdentityModel.Claims.ClaimSet claimSet in claimSets)
  458. {
  459. WindowsClaimSet windowsClaimSet = claimSet as WindowsClaimSet;
  460. if (windowsClaimSet != null)
  461. {
  462. //
  463. // The ClaimSet in the authorizationContext is simply a reflection of the NT Token.
  464. // The WindowsClaimsIdentity will generate that information properly. So ignore the ClaimSets.
  465. //
  466. //
  467. // WCF does not propogate the WindowsIdentity.AuthenticationType properly.
  468. // To avoid WindowsClaimsIdentity.AuthenticationType from throwing, specify
  469. // this authenticationType value. Since we only have to handle SPNEGO specify Negotiate.
  470. //
  471. claimsIdentity = MergeClaims(claimsIdentity, new WindowsIdentity(windowsClaimSet.WindowsIdentity.Token,
  472. AuthenticationTypes.Negotiate));
  473. AddAuthenticationMethod(claimsIdentity, AuthenticationMethods.Windows);
  474. AddAuthenticationInstantClaim(claimsIdentity, XmlConvert.ToString(DateTime.UtcNow, DateTimeFormats.Generated));
  475. }
  476. else
  477. {
  478. claimsIdentity = MergeClaims(claimsIdentity, ClaimsConversionHelper.CreateClaimsIdentityFromClaimSet(claimSet));
  479. AddAuthenticationInstantClaim(claimsIdentity, XmlConvert.ToString(DateTime.UtcNow, DateTimeFormats.Generated));
  480. }
  481. }
  482. return claimsIdentity;
  483. }
  484. /// <summary>
  485. /// Gets the ServiceCredentials from the OperationContext.
  486. /// </summary>
  487. /// <returns>ServiceCredentials</returns>
  488. static ServiceCredentials GetServiceCredentials()
  489. {
  490. ServiceCredentials serviceCredentials = null;
  491. if (OperationContext.Current != null &&
  492. OperationContext.Current.Host != null &&
  493. OperationContext.Current.Host.Description != null &&
  494. OperationContext.Current.Host.Description.Behaviors != null)
  495. {
  496. serviceCredentials = OperationContext.Current.Host.Description.Behaviors.Find<ServiceCredentials>();
  497. }
  498. return serviceCredentials;
  499. }
  500. // Adds an Authentication Method claims to the given ClaimsIdentity if one is not already present.
  501. static void AddAuthenticationMethod(ClaimsIdentity claimsIdentity, string authenticationMethod)
  502. {
  503. System.Security.Claims.Claim authenticationMethodClaim =
  504. claimsIdentity.Claims.FirstOrDefault(claim => claim.Type == System.Security.Claims.ClaimTypes.AuthenticationMethod);
  505. if (authenticationMethodClaim == null)
  506. {
  507. // AuthenticationMethod claims does not exist. Add one.
  508. claimsIdentity.AddClaim(
  509. new System.Security.Claims.Claim(
  510. System.Security.Claims.ClaimTypes.AuthenticationMethod, authenticationMethod));
  511. }
  512. }
  513. // Adds an Authentication Method claims to the given ClaimsIdentity if one is not already present.
  514. static void AddAuthenticationInstantClaim(ClaimsIdentity claimsIdentity, string authenticationInstant)
  515. {
  516. // the issuer for this claim should always be the default issuer.
  517. string issuerName = ClaimsIdentity.DefaultIssuer;
  518. System.Security.Claims.Claim authenticationInstantClaim =
  519. claimsIdentity.Claims.FirstOrDefault(claim => claim.Type == System.Security.Claims.ClaimTypes.AuthenticationInstant);
  520. if (authenticationInstantClaim == null)
  521. {
  522. // AuthenticationInstance claims does not exist. Add one.
  523. claimsIdentity.AddClaim(
  524. new System.Security.Claims.Claim(
  525. System.Security.Claims.ClaimTypes.AuthenticationInstant, authenticationInstant, ClaimValueTypes.DateTime,
  526. issuerName));
  527. }
  528. }
  529. // When a token creates more than one Identity we have to merge these identities.
  530. // The below method takes two Identities and will return a single identity. If one of the
  531. // Identities is a WindowsIdentity then all claims from the other identity are
  532. // merged into the WindowsIdentity. If neither are WindowsIdentity then it
  533. // selects 'identity1' and merges all the claims from 'identity2' into 'identity1'.
  534. //
  535. // It is not clear how we can handler duplicate name claim types and delegates.
  536. // So, we are just cloning the claims from one identity and adding it to another.
  537. internal static ClaimsIdentity MergeClaims(ClaimsIdentity identity1, ClaimsIdentity identity2)
  538. {
  539. if ((identity1 == null) && (identity2 == null))
  540. {
  541. throw DiagnosticUtility.ExceptionUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4268));
  542. }
  543. if (identity1 == null)
  544. {
  545. return identity2;
  546. }
  547. if (identity2 == null)
  548. {
  549. return identity1;
  550. }
  551. WindowsIdentity windowsIdentity = identity1 as WindowsIdentity;
  552. if (windowsIdentity != null)
  553. {
  554. windowsIdentity.AddClaims(identity2.Claims);
  555. return windowsIdentity;
  556. }
  557. windowsIdentity = identity2 as WindowsIdentity;
  558. if (windowsIdentity != null)
  559. {
  560. windowsIdentity.AddClaims(identity1.Claims);
  561. return windowsIdentity;
  562. }
  563. identity1.AddClaims(identity2.Claims);
  564. return identity1;
  565. }
  566. /// <summary>
  567. /// Checks authorization for the given operation context based on policy evaluation.
  568. /// </summary>
  569. /// <param name="operationContext">The OperationContext for the current authorization request.</param>
  570. /// <returns>true if authorized, false otherwise</returns>
  571. protected override bool CheckAccessCore(OperationContext operationContext)
  572. {
  573. if (operationContext == null)
  574. {
  575. return false;
  576. }
  577. string action = string.Empty;
  578. // WebRequests will not always have an action specified in the operation context.
  579. // If action is null or empty, check the httpRequest.
  580. if (!string.IsNullOrEmpty(operationContext.IncomingMessageHeaders.Action))
  581. {
  582. action = operationContext.IncomingMessageHeaders.Action;
  583. }
  584. else
  585. {
  586. HttpRequestMessageProperty request = operationContext.IncomingMessageProperties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
  587. if (request != null)
  588. {
  589. action = request.Method;
  590. }
  591. }
  592. System.Uri resource = operationContext.IncomingMessageHeaders.To;
  593. ServiceCredentials credentials = GetServiceCredentials();
  594. if ((credentials == null) || string.IsNullOrEmpty(action) || (resource == null))
  595. {
  596. return false;
  597. }
  598. //
  599. // CheckAccess is called prior to impersonation in WCF, so we need to pull
  600. // the ClaimsPrincipal from the OperationContext.ServiceSecurityContext.AuthorizationContext.Properties[ "Principal" ].
  601. //
  602. ClaimsPrincipal claimsPrincipal = operationContext.ServiceSecurityContext.AuthorizationContext.Properties[AuthorizationPolicy.ClaimsPrincipalKey] as ClaimsPrincipal;
  603. claimsPrincipal = credentials.IdentityConfiguration.ClaimsAuthenticationManager.Authenticate(resource.AbsoluteUri, claimsPrincipal);
  604. operationContext.ServiceSecurityContext.AuthorizationContext.Properties[AuthorizationPolicy.ClaimsPrincipalKey] = claimsPrincipal;
  605. if ((claimsPrincipal == null) || (claimsPrincipal.Identities == null))
  606. {
  607. return false;
  608. }
  609. if (DiagnosticUtility.ShouldTraceInformation)
  610. {
  611. TraceUtility.TraceEvent(
  612. TraceEventType.Information,
  613. TraceCode.Security,
  614. SR.GetString(SR.TraceAuthorize),
  615. new System.IdentityModel.Diagnostics.AuthorizeTraceRecord(claimsPrincipal, resource.AbsoluteUri, action));
  616. }
  617. bool authorized = credentials.IdentityConfiguration.ClaimsAuthorizationManager.CheckAccess(
  618. new System.Security.Claims.AuthorizationContext(
  619. claimsPrincipal, resource.AbsoluteUri, action
  620. )
  621. );
  622. if (DiagnosticUtility.ShouldTraceInformation)
  623. {
  624. if (authorized)
  625. {
  626. System.IdentityModel.Diagnostics.TraceUtility.TraceString(
  627. TraceEventType.Information,
  628. SR.GetString(SR.TraceOnAuthorizeRequestSucceed));
  629. }
  630. else
  631. {
  632. System.IdentityModel.Diagnostics.TraceUtility.TraceString(
  633. TraceEventType.Information,
  634. SR.GetString(SR.TraceOnAuthorizeRequestFailed));
  635. }
  636. }
  637. return authorized;
  638. }
  639. }
  640. class ClaimStringValueComparer : IEqualityComparer<System.IdentityModel.Claims.Claim>
  641. {
  642. #region IEqualityComparer<System.IdentityModel.Claims.Claim> Members
  643. public bool Equals(System.IdentityModel.Claims.Claim claim1, System.IdentityModel.Claims.Claim claim2)
  644. {
  645. if (ReferenceEquals(claim1, claim2))
  646. {
  647. return true;
  648. }
  649. if (claim1 == null || claim2 == null)
  650. {
  651. return false;
  652. }
  653. if (claim1.ClaimType != claim2.ClaimType || claim1.Right != claim2.Right)
  654. {
  655. return false;
  656. }
  657. return StringComparer.OrdinalIgnoreCase.Equals(claim1.Resource, claim2.Resource);
  658. }
  659. public int GetHashCode(System.IdentityModel.Claims.Claim claim)
  660. {
  661. if (claim == null)
  662. {
  663. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("claim");
  664. }
  665. return claim.ClaimType.GetHashCode() ^ claim.Right.GetHashCode()
  666. ^ ((claim.Resource == null) ? 0 : claim.Resource.GetHashCode());
  667. }
  668. #endregion
  669. }
  670. class SecurityTokenSpecificationEnumerable : IEnumerable<SecurityTokenSpecification>
  671. {
  672. SecurityMessageProperty _securityMessageProperty;
  673. public SecurityTokenSpecificationEnumerable(SecurityMessageProperty securityMessageProperty)
  674. {
  675. if (securityMessageProperty == null)
  676. {
  677. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityMessageProperty");
  678. }
  679. _securityMessageProperty = securityMessageProperty;
  680. }
  681. public IEnumerator<SecurityTokenSpecification> GetEnumerator()
  682. {
  683. if (_securityMessageProperty.InitiatorToken != null)
  684. {
  685. yield return _securityMessageProperty.InitiatorToken;
  686. }
  687. if (_securityMessageProperty.ProtectionToken != null)
  688. {
  689. yield return _securityMessageProperty.ProtectionToken;
  690. }
  691. if (_securityMessageProperty.HasIncomingSupportingTokens)
  692. {
  693. foreach (SecurityTokenSpecification tokenSpecification in _securityMessageProperty.IncomingSupportingTokens)
  694. {
  695. if (tokenSpecification != null)
  696. {
  697. yield return tokenSpecification;
  698. }
  699. }
  700. }
  701. }
  702. IEnumerator IEnumerable.GetEnumerator()
  703. {
  704. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
  705. }
  706. }
  707. }