//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.ServiceModel.Security { using System; using System.ServiceModel; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.ServiceModel.Security.Tokens; using System.IO; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Net.Mail; using System.Xml; using System.Runtime.Serialization; using System.IdentityModel.Claims; using System.IdentityModel.Policy; using System.Security.Principal; static class SctClaimSerializer { static void SerializeSid(SecurityIdentifier sid, SctClaimDictionary dictionary, XmlDictionaryWriter writer) { byte[] sidBytes = new byte[sid.BinaryLength]; sid.GetBinaryForm(sidBytes, 0); writer.WriteBase64(sidBytes, 0, sidBytes.Length); } static void WriteRightAttribute(Claim claim, SctClaimDictionary dictionary, XmlDictionaryWriter writer) { if (Rights.PossessProperty.Equals(claim.Right)) return; writer.WriteAttributeString(dictionary.Right, dictionary.EmptyString, claim.Right); } static string ReadRightAttribute(XmlDictionaryReader reader, SctClaimDictionary dictionary) { string right = reader.GetAttribute(dictionary.Right, dictionary.EmptyString); return String.IsNullOrEmpty(right) ? Rights.PossessProperty : right; } static void WriteSidAttribute(SecurityIdentifier sid, SctClaimDictionary dictionary, XmlDictionaryWriter writer) { byte[] sidBytes = new byte[sid.BinaryLength]; sid.GetBinaryForm(sidBytes, 0); writer.WriteAttributeString(dictionary.Sid, dictionary.EmptyString, Convert.ToBase64String(sidBytes)); } static SecurityIdentifier ReadSidAttribute(XmlDictionaryReader reader, SctClaimDictionary dictionary) { byte[] sidBytes = Convert.FromBase64String(reader.GetAttribute(dictionary.Sid, dictionary.EmptyString)); return new SecurityIdentifier(sidBytes, 0); } public static void SerializeClaim(Claim claim, SctClaimDictionary dictionary, XmlDictionaryWriter writer, XmlObjectSerializer serializer) { // the order in which known claim types are checked is optimized for use patterns if (claim == null) { writer.WriteElementString(dictionary.NullValue, dictionary.EmptyString, string.Empty); return; } else if (ClaimTypes.Sid.Equals(claim.ClaimType)) { writer.WriteStartElement(dictionary.WindowsSidClaim, dictionary.EmptyString); WriteRightAttribute(claim, dictionary, writer); SerializeSid((SecurityIdentifier)claim.Resource, dictionary, writer); writer.WriteEndElement(); return; } else if (ClaimTypes.DenyOnlySid.Equals(claim.ClaimType)) { writer.WriteStartElement(dictionary.DenyOnlySidClaim, dictionary.EmptyString); WriteRightAttribute(claim, dictionary, writer); SerializeSid((SecurityIdentifier)claim.Resource, dictionary, writer); writer.WriteEndElement(); return; } else if (ClaimTypes.X500DistinguishedName.Equals(claim.ClaimType)) { writer.WriteStartElement(dictionary.X500DistinguishedNameClaim, dictionary.EmptyString); WriteRightAttribute(claim, dictionary, writer); byte[] rawData = ((X500DistinguishedName)claim.Resource).RawData; writer.WriteBase64(rawData, 0, rawData.Length); writer.WriteEndElement(); return; } else if (ClaimTypes.Thumbprint.Equals(claim.ClaimType)) { writer.WriteStartElement(dictionary.X509ThumbprintClaim, dictionary.EmptyString); WriteRightAttribute(claim, dictionary, writer); byte[] thumbprint = (byte[])claim.Resource; writer.WriteBase64(thumbprint, 0, thumbprint.Length); writer.WriteEndElement(); return; } else if (ClaimTypes.Name.Equals(claim.ClaimType)) { writer.WriteStartElement(dictionary.NameClaim, dictionary.EmptyString); WriteRightAttribute(claim, dictionary, writer); writer.WriteString((string)claim.Resource); writer.WriteEndElement(); return; } else if (ClaimTypes.Dns.Equals(claim.ClaimType)) { writer.WriteStartElement(dictionary.DnsClaim, dictionary.EmptyString); WriteRightAttribute(claim, dictionary, writer); writer.WriteString((string)claim.Resource); writer.WriteEndElement(); return; } else if (ClaimTypes.Rsa.Equals(claim.ClaimType)) { writer.WriteStartElement(dictionary.RsaClaim, dictionary.EmptyString); WriteRightAttribute(claim, dictionary, writer); writer.WriteString(((RSA)claim.Resource).ToXmlString(false)); writer.WriteEndElement(); return; } else if (ClaimTypes.Email.Equals(claim.ClaimType)) { writer.WriteStartElement(dictionary.MailAddressClaim, dictionary.EmptyString); WriteRightAttribute(claim, dictionary, writer); writer.WriteString(((MailAddress)claim.Resource).Address); writer.WriteEndElement(); return; } else if (claim == Claim.System) { writer.WriteElementString(dictionary.SystemClaim, dictionary.EmptyString, string.Empty); return; } else if (ClaimTypes.Hash.Equals(claim.ClaimType)) { writer.WriteStartElement(dictionary.HashClaim, dictionary.EmptyString); WriteRightAttribute(claim, dictionary, writer); byte[] hash = (byte[])claim.Resource; writer.WriteBase64(hash, 0, hash.Length); writer.WriteEndElement(); return; } else if (ClaimTypes.Spn.Equals(claim.ClaimType)) { writer.WriteStartElement(dictionary.SpnClaim, dictionary.EmptyString); WriteRightAttribute(claim, dictionary, writer); writer.WriteString((string)claim.Resource); writer.WriteEndElement(); return; } else if (ClaimTypes.Upn.Equals(claim.ClaimType)) { writer.WriteStartElement(dictionary.UpnClaim, dictionary.EmptyString); WriteRightAttribute(claim, dictionary, writer); writer.WriteString((string)claim.Resource); writer.WriteEndElement(); return; } else if (ClaimTypes.Uri.Equals(claim.ClaimType)) { writer.WriteStartElement(dictionary.UrlClaim, dictionary.EmptyString); WriteRightAttribute(claim, dictionary, writer); writer.WriteString(((Uri)claim.Resource).AbsoluteUri); writer.WriteEndElement(); return; } else { // this is an extensible claim... need to delegate to xml object serializer serializer.WriteObject(writer, claim); } } public static void SerializeClaimSet(ClaimSet claimSet, SctClaimDictionary dictionary, XmlDictionaryWriter writer, XmlObjectSerializer serializer, XmlObjectSerializer claimSerializer) { if (claimSet is X509CertificateClaimSet) { X509CertificateClaimSet x509ClaimSet = (X509CertificateClaimSet)claimSet; writer.WriteStartElement(dictionary.X509CertificateClaimSet, dictionary.EmptyString); byte[] rawData = x509ClaimSet.X509Certificate.RawData; writer.WriteBase64(rawData, 0, rawData.Length); writer.WriteEndElement(); } else if (claimSet == ClaimSet.System) { writer.WriteElementString(dictionary.SystemClaimSet, dictionary.EmptyString, String.Empty); } else if (claimSet == ClaimSet.Windows) { writer.WriteElementString(dictionary.WindowsClaimSet, dictionary.EmptyString, String.Empty); } else if (claimSet == ClaimSet.Anonymous) { writer.WriteElementString(dictionary.AnonymousClaimSet, dictionary.EmptyString, String.Empty); } else if (claimSet is WindowsClaimSet || claimSet is DefaultClaimSet) { writer.WriteStartElement(dictionary.ClaimSet, dictionary.EmptyString); writer.WriteStartElement(dictionary.PrimaryIssuer, dictionary.EmptyString); if (claimSet.Issuer == claimSet) { writer.WriteElementString(dictionary.NullValue, dictionary.EmptyString, string.Empty); } else { SerializeClaimSet(claimSet.Issuer, dictionary, writer, serializer, claimSerializer); } writer.WriteEndElement(); foreach (Claim claim in claimSet) { writer.WriteStartElement(dictionary.Claim, dictionary.EmptyString); SerializeClaim(claim, dictionary, writer, claimSerializer); writer.WriteEndElement(); } writer.WriteEndElement(); } else { serializer.WriteObject(writer, claimSet); } } public static Claim DeserializeClaim(XmlDictionaryReader reader, SctClaimDictionary dictionary, XmlObjectSerializer serializer) { if (reader.IsStartElement(dictionary.NullValue, dictionary.EmptyString)) { reader.ReadElementString(); return null; } else if (reader.IsStartElement(dictionary.WindowsSidClaim, dictionary.EmptyString)) { string right = ReadRightAttribute(reader, dictionary); reader.ReadStartElement(); byte[] sidBytes = reader.ReadContentAsBase64(); reader.ReadEndElement(); return new Claim(ClaimTypes.Sid, new SecurityIdentifier(sidBytes, 0), right); } else if (reader.IsStartElement(dictionary.DenyOnlySidClaim, dictionary.EmptyString)) { string right = ReadRightAttribute(reader, dictionary); reader.ReadStartElement(); byte[] sidBytes = reader.ReadContentAsBase64(); reader.ReadEndElement(); return new Claim(ClaimTypes.DenyOnlySid, new SecurityIdentifier(sidBytes, 0), right); } else if (reader.IsStartElement(dictionary.X500DistinguishedNameClaim, dictionary.EmptyString)) { string right = ReadRightAttribute(reader, dictionary); reader.ReadStartElement(); byte[] rawData = reader.ReadContentAsBase64(); reader.ReadEndElement(); return new Claim(ClaimTypes.X500DistinguishedName, new X500DistinguishedName(rawData), right); } else if (reader.IsStartElement(dictionary.X509ThumbprintClaim, dictionary.EmptyString)) { string right = ReadRightAttribute(reader, dictionary); reader.ReadStartElement(); byte[] thumbprint = reader.ReadContentAsBase64(); reader.ReadEndElement(); return new Claim(ClaimTypes.Thumbprint, thumbprint, right); } else if (reader.IsStartElement(dictionary.NameClaim, dictionary.EmptyString)) { string right = ReadRightAttribute(reader, dictionary); reader.ReadStartElement(); string name = reader.ReadString(); reader.ReadEndElement(); return new Claim(ClaimTypes.Name, name, right); } else if (reader.IsStartElement(dictionary.DnsClaim, dictionary.EmptyString)) { string right = ReadRightAttribute(reader, dictionary); reader.ReadStartElement(); string dns = reader.ReadString(); reader.ReadEndElement(); return new Claim(ClaimTypes.Dns, dns, right); } else if (reader.IsStartElement(dictionary.RsaClaim, dictionary.EmptyString)) { string right = ReadRightAttribute(reader, dictionary); reader.ReadStartElement(); string rsaXml = reader.ReadString(); reader.ReadEndElement(); System.Security.Cryptography.RSACryptoServiceProvider rsa = new System.Security.Cryptography.RSACryptoServiceProvider(); rsa.FromXmlString(rsaXml); return new Claim(ClaimTypes.Rsa, rsa, right); } else if (reader.IsStartElement(dictionary.MailAddressClaim, dictionary.EmptyString)) { string right = ReadRightAttribute(reader, dictionary); reader.ReadStartElement(); string address = reader.ReadString(); reader.ReadEndElement(); return new Claim(ClaimTypes.Email, new System.Net.Mail.MailAddress(address), right); } else if (reader.IsStartElement(dictionary.SystemClaim, dictionary.EmptyString)) { reader.ReadElementString(); return Claim.System; } else if (reader.IsStartElement(dictionary.HashClaim, dictionary.EmptyString)) { string right = ReadRightAttribute(reader, dictionary); reader.ReadStartElement(); byte[] hash = reader.ReadContentAsBase64(); reader.ReadEndElement(); return new Claim(ClaimTypes.Hash, hash, right); } else if (reader.IsStartElement(dictionary.SpnClaim, dictionary.EmptyString)) { string right = ReadRightAttribute(reader, dictionary); reader.ReadStartElement(); string spn = reader.ReadString(); reader.ReadEndElement(); return new Claim(ClaimTypes.Spn, spn, right); } else if (reader.IsStartElement(dictionary.UpnClaim, dictionary.EmptyString)) { string right = ReadRightAttribute(reader, dictionary); reader.ReadStartElement(); string upn = reader.ReadString(); reader.ReadEndElement(); return new Claim(ClaimTypes.Upn, upn, right); } else if (reader.IsStartElement(dictionary.UrlClaim, dictionary.EmptyString)) { string right = ReadRightAttribute(reader, dictionary); reader.ReadStartElement(); string url = reader.ReadString(); reader.ReadEndElement(); return new Claim(ClaimTypes.Uri, new Uri(url), right); } else { return (Claim)serializer.ReadObject(reader); } } public static ClaimSet DeserializeClaimSet(XmlDictionaryReader reader, SctClaimDictionary dictionary, XmlObjectSerializer serializer, XmlObjectSerializer claimSerializer) { if (reader.IsStartElement(dictionary.NullValue, dictionary.EmptyString)) { reader.ReadElementString(); return null; } else if (reader.IsStartElement(dictionary.X509CertificateClaimSet, dictionary.EmptyString)) { reader.ReadStartElement(); byte[] rawData = reader.ReadContentAsBase64(); reader.ReadEndElement(); return new X509CertificateClaimSet(new X509Certificate2(rawData), false); } else if (reader.IsStartElement(dictionary.SystemClaimSet, dictionary.EmptyString)) { reader.ReadElementString(); return ClaimSet.System; } else if (reader.IsStartElement(dictionary.WindowsClaimSet, dictionary.EmptyString)) { reader.ReadElementString(); return ClaimSet.Windows; } else if (reader.IsStartElement(dictionary.AnonymousClaimSet, dictionary.EmptyString)) { reader.ReadElementString(); return ClaimSet.Anonymous; } else if (reader.IsStartElement(dictionary.ClaimSet, dictionary.EmptyString)) { ClaimSet issuer = null; List claims = new List(); reader.ReadStartElement(); if (reader.IsStartElement(dictionary.PrimaryIssuer, dictionary.EmptyString)) { reader.ReadStartElement(); issuer = DeserializeClaimSet(reader, dictionary, serializer, claimSerializer); reader.ReadEndElement(); } while (reader.IsStartElement()) { reader.ReadStartElement(); claims.Add(DeserializeClaim(reader, dictionary, claimSerializer)); reader.ReadEndElement(); } reader.ReadEndElement(); return issuer != null ? new DefaultClaimSet(issuer, claims) : new DefaultClaimSet(claims); } else { return (ClaimSet)serializer.ReadObject(reader); } } public static void SerializeIdentities(AuthorizationContext authContext, SctClaimDictionary dictionary, XmlDictionaryWriter writer, XmlObjectSerializer serializer) { object obj; IList identities; if (authContext.Properties.TryGetValue(SecurityUtils.Identities, out obj)) { identities = obj as IList; if (identities != null && identities.Count > 0) { writer.WriteStartElement(dictionary.Identities, dictionary.EmptyString); for (int i = 0; i < identities.Count; ++i) { SerializePrimaryIdentity(identities[i], dictionary, writer, serializer); } writer.WriteEndElement(); } } } static void SerializePrimaryIdentity(IIdentity identity, SctClaimDictionary dictionary, XmlDictionaryWriter writer, XmlObjectSerializer serializer) { if (identity != null && identity != SecurityUtils.AnonymousIdentity) { writer.WriteStartElement(dictionary.PrimaryIdentity, dictionary.EmptyString); if (identity is WindowsIdentity) { WindowsIdentity wid = (WindowsIdentity)identity; writer.WriteStartElement(dictionary.WindowsSidIdentity, dictionary.EmptyString); WriteSidAttribute(wid.User, dictionary, writer); // This is to work around WOW64 bug Windows OS 1491447 string authenticationType = null; using (WindowsIdentity self = WindowsIdentity.GetCurrent()) { // is owner or admin? AuthenticationType could throw un-authorized exception if ((self.User == wid.Owner) || (wid.Owner != null && self.Groups.Contains(wid.Owner)) || (wid.Owner != SecurityUtils.AdministratorsSid && self.Groups.Contains(SecurityUtils.AdministratorsSid))) { authenticationType = wid.AuthenticationType; } } if (!String.IsNullOrEmpty(authenticationType)) writer.WriteAttributeString(dictionary.AuthenticationType, dictionary.EmptyString, authenticationType); writer.WriteString(wid.Name); writer.WriteEndElement(); } else if (identity is WindowsSidIdentity) { WindowsSidIdentity wsid = (WindowsSidIdentity)identity; writer.WriteStartElement(dictionary.WindowsSidIdentity, dictionary.EmptyString); WriteSidAttribute(wsid.SecurityIdentifier, dictionary, writer); if (!String.IsNullOrEmpty(wsid.AuthenticationType)) writer.WriteAttributeString(dictionary.AuthenticationType, dictionary.EmptyString, wsid.AuthenticationType); writer.WriteString(wsid.Name); writer.WriteEndElement(); } else if (identity is GenericIdentity) { GenericIdentity genericIdentity = (GenericIdentity)identity; writer.WriteStartElement(dictionary.GenericIdentity, dictionary.EmptyString); if (!String.IsNullOrEmpty(genericIdentity.AuthenticationType)) writer.WriteAttributeString(dictionary.AuthenticationType, dictionary.EmptyString, genericIdentity.AuthenticationType); writer.WriteString(genericIdentity.Name); writer.WriteEndElement(); } else { serializer.WriteObject(writer, identity); } writer.WriteEndElement(); } } public static IList DeserializeIdentities(XmlDictionaryReader reader, SctClaimDictionary dictionary, XmlObjectSerializer serializer) { List identities = null; if (reader.IsStartElement(dictionary.Identities, dictionary.EmptyString)) { identities = new List(); reader.ReadStartElement(); while (reader.IsStartElement(dictionary.PrimaryIdentity, dictionary.EmptyString)) { IIdentity identity = DeserializePrimaryIdentity(reader, dictionary, serializer); if (identity != null && identity != SecurityUtils.AnonymousIdentity) { identities.Add(identity); } } reader.ReadEndElement(); } return identities; } static IIdentity DeserializePrimaryIdentity(XmlDictionaryReader reader, SctClaimDictionary dictionary, XmlObjectSerializer serializer) { IIdentity identity = null; if (reader.IsStartElement(dictionary.PrimaryIdentity, dictionary.EmptyString)) { reader.ReadStartElement(); if (reader.IsStartElement(dictionary.WindowsSidIdentity, dictionary.EmptyString)) { SecurityIdentifier sid = ReadSidAttribute(reader, dictionary); string authenticationType = reader.GetAttribute(dictionary.AuthenticationType, dictionary.EmptyString); reader.ReadStartElement(); string name = reader.ReadContentAsString(); identity = new WindowsSidIdentity(sid, name, authenticationType ?? String.Empty); reader.ReadEndElement(); } else if (reader.IsStartElement(dictionary.GenericIdentity, dictionary.EmptyString)) { string authenticationType = reader.GetAttribute(dictionary.AuthenticationType, dictionary.EmptyString); reader.ReadStartElement(); string name = reader.ReadContentAsString(); identity = SecurityUtils.CreateIdentity(name, authenticationType ?? String.Empty); reader.ReadEndElement(); } else { identity = (IIdentity)serializer.ReadObject(reader); } reader.ReadEndElement(); } return identity; } } }