while working on an updated version of mono.merge using cecil 0.9, I'm
having a hard time cloning SecurityDeclarations on methods (maybe on
other objects, too). I'm merging a 3rd party assembly with a
SecurityDeclaration on a method with the following ildasm layout:
.permissionset linkcheck "<PermissionSet
class=\"System.Security.PermissionSet\"\r\n version=\"1\">\r\n
<IPermission
class=\"System.Security.Permissions.StrongNameIdentityPermission,
mscorlib, Version=1.0.5000.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089\"\r\n version=\"1\"\r\n
PublicKeyBlob=\"002400000480000094000000060200000024000052534131000400000100010071E9D3E8FB4B521B04EA8F76D94C5FE657793FF51E88DD9DD1AD6D056525454770A74B63478A1B63ED2AD979E65BEE25DE44BA686242CE430F0C7DF1475C18BEB5467555F740961A969E5E411CA4567C73B471F32815041356C9A106309D46EF9F232CB4BB0B0746475B5DB8CE1D4FCF17D99A80A0F7205DFD03D43B109583C2\"/>\r\n</PermissionSet>\r\n"
but after merging, I get this:
.permissionset linkcheck = {class
'System.Security.Permissions.PermissionSetAttribute' = {property string
'XML' = string('<PermissionSet
class=\"System.Security.PermissionSet\"\r\n version=\"1\">\r\n
<IPermission
class=\"System.Security.Permissions.StrongNameIdentityPermission,
mscorlib, Version=1.0.5000.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089\"\r\n version=\"1\"\r\n
PublicKeyBlob=\"002400000480000094000000060200000024000052534131000400000100010071E9D3E8FB4B521B04EA8F76D94C5FE657793FF51E88DD9DD1AD6D056525454770A74B63478A1B63ED2AD979E65BEE25DE44BA686242CE430F0C7DF1475C18BEB5467555F740961A969E5E411CA4567C73B471F32815041356C9A106309D46EF9F232CB4BB0B0746475B5DB8CE1D4FCF17D99A80A0F7205DFD03D43B109583C2\"/>\r\n</PermissionSet>\r\n')}}
So it seems like cecil has problems with copying such
SecurityDeclarations. What I do in the merge process is simply copying
the SecurityDeclaration by cloning it (creating a new one and adding all
fields and properties, which are cloned, too). However, the above result
is the same when just copying the SecurityDeclaration from the original
assembly.
Pure roundtripping (read assembly with ReadingMode.Immediate, write to
another location) works, but I guess cecil does not touch the
SecurityDeclarations in this case?
If you need a repro, let me know. Any help greatly appreciated :-)
Simon
As a prelude, you have to know that the ECMA-335 describes two ways to
serialize security declarations.
The first one is just a blob of XML, and is usually found on
assemblies prior to .net 2.0.
The second one looks like a CustomAttribute, and is usually found on
assemblies on and after .net 2.0.
Currently, Cecil is able to read the former and the later, but only
writes the later. (Which is an issue if you're roundtripping an
assembly which is supposed to run on .net 1.1).
On Sun, Feb 13, 2011 at 6:06 PM, gold...@gmx.de <gold...@gmx.de> wrote:
> .permissionset linkcheck "<PermissionSet
> class=\"System.Security.PermissionSet\"\r\n version=\"1\">\r\n <IPermission
> class=\"System.Security.Permissions.StrongNameIdentityPermission, mscorlib,
> Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\"\r\n
> version=\"1\"\r\n
> PublicKeyBlob=\"002400000480000094000000060200000024000052534131000400000100010071E9D3E8FB4B521B04EA8F76D94C5FE657793FF51E88DD9DD1AD6D056525454770A74B63478A1B63ED2AD979E65BEE25DE44BA686242CE430F0C7DF1475C18BEB5467555F740961A969E5E411CA4567C73B471F32815041356C9A106309D46EF9F232CB4BB0B0746475B5DB8CE1D4FCF17D99A80A0F7205DFD03D43B109583C2\"/>\r\n</PermissionSet>\r\n"
So this is a old XML permission set.
> .permissionset linkcheck = {class
> 'System.Security.Permissions.PermissionSetAttribute' = {property string
> 'XML' = string('<PermissionSet class=\"System.Security.PermissionSet\"\r\n
> version=\"1\">\r\n <IPermission
> class=\"System.Security.Permissions.StrongNameIdentityPermission, mscorlib,
> Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\"\r\n
> version=\"1\"\r\n
> PublicKeyBlob=\"002400000480000094000000060200000024000052534131000400000100010071E9D3E8FB4B521B04EA8F76D94C5FE657793FF51E88DD9DD1AD6D056525454770A74B63478A1B63ED2AD979E65BEE25DE44BA686242CE430F0C7DF1475C18BEB5467555F740961A969E5E411CA4567C73B471F32815041356C9A106309D46EF9F232CB4BB0B0746475B5DB8CE1D4FCF17D99A80A0F7205DFD03D43B109583C2\"/>\r\n</PermissionSet>\r\n')}}
Which gets written as a 2.0 security declaration.
> So it seems like cecil has problems with copying such SecurityDeclarations.
> What I do in the merge process is simply copying the SecurityDeclaration by
> cloning it (creating a new one and adding all fields and properties, which
> are cloned, too). However, the above result is the same when just copying
> the SecurityDeclaration from the original assembly.
And even if they look different, the output assembly should work, doesn't it?
> Pure roundtripping (read assembly with ReadingMode.Immediate, write to
> another location) works, but I guess cecil does not touch the
> SecurityDeclarations in this case?
Indeed, it just copies the blob.
Jb
Any chance we can write old-style permission sets? Copy the blob?
Thanks for the fast help.
Simon
If you're using .net 1.1 then it makes sense.
> Any chance we can write old-style permission sets? Copy the blob?
I'll have to add support for that. As a very sad workaround, you can
go back to Cecil 0.6.x as found in Mono 2.8.
Jb
Simon
The current implementation is somewhat incomplete in that it
a) doesn't allow to write permissionsets for .NET 1.1 assemblies and
b) writes blob-style permissionsets including XML strings when modifying
.NET 1.1 assemblies (which I'm not sure .NET 2.0 and above can handle)
Any chance of this going in to the main sources? I'll attach my code
below. The downside is it adds a dependency to System.Xml.dll.
Thanks for the help on this (pointing me in the right direction), I
really thought there was something wrong with our merge process :-)
Simon
--
AssemblyReader.cs:
SecurityAttribute ParseXmlSecurityDeclaration(string xmlString)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlString);
XmlNode permissionSet = xmlDoc.SelectSingleNode("/PermissionSet");
if (permissionSet == null)
return null;
XmlNode permissionSetClass =
permissionSet.SelectSingleNode("@class"); // check version?
if (permissionSetClass == null)
return null;
if (permissionSetClass.Value != "System.Security.PermissionSet")
return null;
XmlNode iPermission = permissionSet.SelectSingleNode("IPermission");
if (iPermission == null)
return null;
XmlNode iPermissionClass = iPermission.SelectSingleNode("@class");
// check version?
if (iPermissionClass == null)
return null;
// Create Namespace & Name from FullName, AssemblyName can be
ignored since we look it up.
Collection<string> classNamespace = new
Collection<string>(iPermissionClass.Value.Split(',')[0].Split('.'));
string className = classNamespace[classNamespace.Count - 1];
classNamespace.RemoveAt(classNamespace.Count - 1);
SecurityAttribute attribute = new
SecurityAttribute(module.TypeSystem.LookupType(string.Join(".",
classNamespace.ToArray()), className));
attribute.properties = new Collection<CustomAttributeNamedArgument>(1);
foreach (XmlAttribute xmlAttr in iPermission.Attributes)
{
if ((xmlAttr.Name != "class") && (xmlAttr.Name != "version"))
{
attribute.properties.Add(new CustomAttributeNamedArgument(
xmlAttr.Name,
new CustomAttributeArgument(
module.TypeSystem.String,
xmlAttr.Value)));
}
}
return attribute;
}
void ReadXmlSecurityDeclaration(uint signature, SecurityDeclaration
declaration)
{
var blob = ReadBlob(signature);
string xmlString = Encoding.Unicode.GetString(blob, 0, blob.Length);
var attributes = new Collection<SecurityAttribute>(1);
var attribute = ParseXmlSecurityDeclaration(xmlString);
attributes.Add (attribute);
declaration.security_attributes = attributes;
}
AssemblyWriter.cs:
SignatureWriter GetXmlSecurityDeclarationSignature(SecurityDeclaration
declaration)
{
SecurityAttribute sa = declaration.SecurityAttributes[0];
TypeReference attrType = sa.AttributeType;
AssemblyNameReference attrAsm = (AssemblyNameReference)attrType.Scope;
string className = attrType.FullName + ", " + attrAsm.FullName;
XmlDocument xmlDoc = new XmlDocument();
XmlElement permissionSet = xmlDoc.CreateElement("PermissionSet");
permissionSet.SetAttribute("class", "System.Security.PermissionSet");
permissionSet.SetAttribute("version", "1");
XmlElement iPermission = xmlDoc.CreateElement("IPermission");
iPermission.SetAttribute("class", className);
iPermission.SetAttribute("version", "1");
foreach (var arg in sa.Properties)
{
iPermission.SetAttribute(arg.Name, arg.Argument.Value.ToString());
}
permissionSet.AppendChild(iPermission);
xmlDoc.AppendChild(permissionSet);
string xmlDocString = xmlDoc.InnerXml;
byte[] bytes = Encoding.Unicode.GetBytes(xmlDocString);
var signature = CreateSignatureWriter();
signature.WriteBytes(bytes);
return signature;
}
SignatureWriter GetSecurityDeclarationSignature(SecurityDeclaration
declaration)
{
var signature = CreateSignatureWriter();
if (!declaration.resolved)
{
signature.WriteBytes(declaration.GetBlob());
return signature;
}
if (module.Runtime <= TargetRuntime.Net_1_1)
{
return GetXmlSecurityDeclarationSignature(declaration);
}
//... old non-1.1 code as before
}