Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Possible security problem with VSTO loader / XML Serialization

0 views
Skip to first unread message

Cyril Mathey

unread,
Nov 18, 2003, 6:53:37 AM11/18/03
to
Hello,

I am trying to use the built-in XML serialization features of the .NET
framework from within a Smart Document/VSTO solution.

I use the following piece of code to deserialize an XML file stored into the
file system:

------------------------------------------------------------------
using Xml = System.Xml;

....
Xml.XmlTextReader reader = null;
try {
string path = @"C:\tmp\Product.xml";
reader = new Xml.XmlTextReader(path);
Type type = typeof(TestSerialization.Product);
TestSerialization.Product product =
new Xml.Serialization.XmlSerializer(type).Deserialize(reader) as
TestSerialization.Product;
Trace.WriteLine("Product: " + product.Name + " --> " + product.Price);
} catch (System.Exception x) {
Trace.WriteLine("*** Cannot deserialize: " + Environment.NewLine + x);
} finally {
if (reader == null)
reader.Close();
}
....
------------------------------------------------------------------

The call to XmlSerializer.Deserialiwe fails with the following exception:

------------------------------------------------------------------
System.InvalidOperationException: There is an error in XML document (0,
0). ---> System.Security.SecurityException: Request for the permission of
type System.Security.Permissions.FileIOPermission, mscorlib,
Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 failed.
at System.Security.CodeAccessSecurityEngine.CheckHelper(PermissionSet
grantedSet, PermissionSet deniedSet, CodeAccessPermission demand,
PermissionToken permToken)
at System.Security.CodeAccessSecurityEngine.Check(PermissionToken permToken,
CodeAccessPermission demand, StackCrawlMark& stackMark, Int32 checkFrames,
Int32 unrestrictedOverride)
at System.Security.CodeAccessSecurityEngine.Check(CodeAccessPermission cap,
StackCrawlMark& stackMark)
at System.Security.CodeAccessPermission.Demand()
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access,
FileShare share, Int32 bufferSize, Boolean useAsync, String msgPath, Boolean
bFromProxy)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access,
FileShare share)
at System.Xml.XmlDownloadManager.GetStream(Uri uri, ICredentials
credentials)
at System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type
ofObjectToReturn)
at System.Xml.XmlTextReader.CreateScanner()
at System.Xml.XmlTextReader.Init()
at System.Xml.XmlTextReader.Read()
at System.Xml.XmlReader.MoveToContent()
at
Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReader1.Read4_
Product()
The state of the failed permission was:
<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib,
Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Read="C:\tmp\Product.xml"/>
--- End of inner exception stack trace ---
at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader,
String encodingStyle)
at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader)
------------------------------------------------------------------

The problem happens either in a 'Code-Behind' assembly or in a Smart
Document. Of course, the code works fine in a 'regular' .NET application.

I began wondering about the difference introduced by VSTO. Then I remembered
that the XML serialization mechanism dynamically compiles code for class
readers. I also learned how the VSTO loader modifies the default policy
levels of the CAS to restrict local 'All Code' code groups (Cf. Peter Torr's
blog about VSTO security on GotDotnet:
http://blogs.gotdotnet.com/ptorr/PermaLink.aspx/4b6e308b-c244-4043-ad92-331e5a97016f).

I do not know whether the dynamic assemblies are loaded into the application
domain used by VSTO or another one but it seems that they are not granted
the 'correct' permission sets since the loader overrides the default ones
and there is no other explicit policy defined.

I am only guessing here as I am not a .NET security guru (and the problem
may not be related at all to the VSTO loader ....).

I hope someone from the VSTO team can have a look into this.

The workaround that I currently use is to read the content of the XML file
into a string, and then build a string reader for the Deserializer. However,
this solution will not be practical if the xml file contains external
references and needs to resolve URIs.


Thanks

--
Cyril


Peter Torr (MS)

unread,
Nov 19, 2003, 3:30:43 AM11/19/03
to
"Cyril Mathey" <dotnet...@ilog.fr> wrote in message
news:e1gzbqcr...@tk2msftngp13.phx.gbl...

> I do not know whether the dynamic assemblies are loaded into the
application
> domain used by VSTO or another one but it seems that they are not granted
> the 'correct' permission sets since the loader overrides the default ones
> and there is no other explicit policy defined.
>
> I am only guessing here as I am not a .NET security guru (and the problem
> may not be related at all to the VSTO loader ....).
>
> I hope someone from the VSTO team can have a look into this.

Hi Cyril,

Yes, you are coming across a problem with the restricted policy in VSTO. We
have a work-around for Web Service proxies in the loader (it generates
assemblies with silly evidence) but it looks like you've found another
instance of this happening.

If you check one of my other entries (thanks for reading <g>) it will point
you to another newsgroup post that will show you how to figure out what
assembly is being loaded and with what evidence:

http://blogs.gotdotnet.com/ptorr/PermaLink.aspx/fc6c7061-5943-435b-9da6-5646b5cc77ab

Then (hopefully) you will be able to add a rule to policy that grants
permission to that assembly. If it only gets URL evidence based on its
random file name, we're in trouble...

I guess I should post an addendum to that VSTO security model entry to point
out the couple of other tweaks we make to get things working... but the
GotDotNet blog server is currently experiencing problems, so it will have to
wait a few days.

Peter

--
Peter Torr - http://blogs.gotdotnet.com/ptorr/
This posting is provided "AS IS" with no warranties, and confers no rights
Samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm


Cyril Mathey

unread,
Nov 19, 2003, 4:27:40 AM11/19/03
to
Hello Peter,

Thanks for the answer. As a matter if fact I was wondering why Web Services
seemed to work and not simple XML serialization as they are based on the
same core technology.
Now I understand better.
I will try to implement the workaround you suggest and I will post any
interesting result to the NG.

--
Cyril

Cyril Mathey

unread,
Nov 21, 2003, 10:24:36 AM11/21/03
to
Hello Peter,

I am getting back to you about this problem with some results about my
experiments, and some questions :)

My first idea was the following:

- Hook an AssemblyLoad event handler to the application handler to be
notified when a dynamic assembly is loaded.
- In the event handler, gather the evidence of the assembly. At this stage,
it turns out that the only evidence that we can use is the url. The url that
I get is in fact the codebase of System.dll in the GAC. I guess that's
because System.dll is the managed host of the dynamic assembly, and thus the
dynamic assembly inherits the url evidence since it has none of its own. The
problem here is that I cannot control whether the assembly is generated on
disk since I am not the one who is compiling it.
- With this evidence, update the AppDomain policy level by creating a code
group under the My_Computer_Zone group. This code group grants Full Trust
and has an url membership condition that is computed at the preceding stage.

I wanted to use the Security API of the .NET Framework to implement a
generic solution that did not require any external modification of the CAS
policy for the following reasons:
- no specific setup was required
- I assumed that I may have to deal with multiple assemblies that I could
not accurately determine in advance.
- all the modifications were performed at the app domain level, and I thus
did not need to tidy up my modifications, which is good since It is tricky
to determine the opportune time to restore the default policy.

So now I have this nice piece of code and I learned quite some stuff
pertaining to .NET security.

However it does not work and I guess that you know why (well, if my
understanding of the problem is correct, then you do know why :)

It does not work because there is an explicit code group named 'System.dll'
at Machine level that grants the 'Execution' permission set. Unless I am
completely wrong, this code group is added when VSTO is installed to enable
Web Services support (this is what you were talking about in your previous
post). The problem is that this entry prevents me from granting any new
permission in the app-domain (XML serialization needs more that 'Execution'
permissions)

so ... at last my questions are:

- Is there any reason why the permissions are set to 'Execution' instead of
'Full Trust' ? Again, if I have well understood how the CAS works, granting
'Full Trust' would not be a problem and would not allow any untrusted
assemblies to perform malicious operations.
- If 'Full Trust' is not a problem, would you consider changing this in the
next VSTO version ?
- If the answer to the second question is 'yes', do you think that in the
meantime I can safely perform this modification on the user computers that
will use my Office Solution ?

Thanks a lot for help

--
Cyril

"Peter Torr (MS)" <pt...@microsoft.com> wrote in message
news:ulgGudnr...@TK2MSFTNGP10.phx.gbl...

Cyril Mathey

unread,
Nov 21, 2003, 11:56:44 AM11/21/03
to
Peter,

I realized I have made a mistake.
The real problem seems to be that I must modify the CAS before the assembly
is loaded (regardless of the 'System.dll' code Group).
Hmm. I am a bit lost. I will need the Week-End to think over this one.
Anyway, I guess that my questions still hold since if the ''System.dll'
group is granted 'Full Trust', I do not need to do anything to make XML
serialization work.

Thanks

--
Cyril

Peter Torr (MS)

unread,
Nov 21, 2003, 12:40:01 PM11/21/03
to
"Cyril Mathey" <dotnet...@ilog.fr> wrote in message
news:uzQ6TOEs...@TK2MSFTNGP12.phx.gbl...

> I am getting back to you about this problem with some results about my
> experiments, and some questions :)

Cool, thanks for reporting back :-)

> It does not work because there is an explicit code group named
'System.dll'
> at Machine level that grants the 'Execution' permission set. Unless I am
> completely wrong, this code group is added when VSTO is installed to
enable
> Web Services support (this is what you were talking about in your previous
> post). The problem is that this entry prevents me from granting any new
> permission in the app-domain (XML serialization needs more that
'Execution'
> permissions)

Oh dear. There are no modifications made to machine policy when you install
VSTO (there were in the Beta, but not in the RTM release). Even so, this is
immaterial -- a normal machine will grant System.dll FullTrust by virtue of
it being in the MyComputer Zone. Remember that an assembly can match more
than one code group in a given policy level, and it will be granted the
union of all those permissions ("Exclusive" groups notwithstanding).

You can definitely add another rule to Machine (or even User) level that
adds FullTrust for the System assembly and it will work. See my reply to
your next post for why you can't do it at the AppDomain level.

> - Is there any reason why the permissions are set to 'Execution' instead
of
> 'Full Trust' ? Again, if I have well understood how the CAS works,
granting
> 'Full Trust' would not be a problem and would not allow any untrusted
> assemblies to perform malicious operations.

<sigh>This is what we get for being conservative with our
permissions</sigh>.

You may have heard of the rule of "run with least privilege." Well, since we
were only aware of Web Service calls using this bogus evidence, and web
service proxies only needed Execution permission, we decided that is what
we'd give them. No point in giving them permissions that they don't need,
*just in case* there's some way to expose a web service contract that
exploits some weird code-gen bug in WSDL (we fixed a bunch of problems like
that before shipping...). But now you've found some other construct that
does need more permission (SerializationFormatter, no doubt).

> - If 'Full Trust' is not a problem, would you consider changing this in
the
> next VSTO version ?

We're hoping that the underlying infrastructure will be changed so that we
can remove the hack altogether, but if not then we'll definitely look at
doing this. The essential problem is that the assembly is loaded with "bad"
evidence, but because most people build, test, and run their applications
under default CLR policy, they never notice the consequences of using this
bogus evidence.

> - If the answer to the second question is 'yes', do you think that in the
> meantime I can safely perform this modification on the user computers that
> will use my Office Solution ?

Yes, you can do that. I'd recommend a user-level change.

Peter

--
Please post questions to the newsgroup - everyone benefits.
This post is provided "AS IS" with no warranties, and confers no rights
Sample code subject to http://www.microsoft.com/info/cpyright.htm
Bore yourself to tears -- http://blogs.gotdotnet.com/ptorr


Peter Torr (MS)

unread,
Nov 21, 2003, 12:44:23 PM11/21/03
to
"Cyril Mathey" <dotnet...@ilog.fr> wrote in message
news:OgAyzBF...@TK2MSFTNGP11.phx.gbl...

> The real problem seems to be that I must modify the CAS before the
assembly
> is loaded (regardless of the 'System.dll' code Group).

Unfortunately you won't be able to do that unless you create a new AppDomain
(which will probably make your life much harder). Once AppDomain policy is
set (by our loader), it cannot be changed.

> Anyway, I guess that my questions still hold since if the ''System.dll'
> group is granted 'Full Trust', I do not need to do anything to make XML
> serialization work.

That's the theory. I'd stick with modifying user policy for now. You can
certainly change it at the Machine / Enterprise level if your users have
permission to do that (ie, they're running as Admins) but there's no
requirement that it be at either of those levels. User is good enough.

Cyril Mathey

unread,
Nov 24, 2003, 8:51:05 AM11/24/03
to
Hello Peter,

> "Cyril Mathey" <dotnet...@ilog.fr> wrote in message
> news:uzQ6TOEs...@TK2MSFTNGP12.phx.gbl...
> > I am getting back to you about this problem with some results about my
> > experiments, and some questions :)
>
> Cool, thanks for reporting back :-)
>
> > It does not work because there is an explicit code group named
> 'System.dll'
> > at Machine level that grants the 'Execution' permission set. Unless I am
> > completely wrong, this code group is added when VSTO is installed to
> enable
> > Web Services support (this is what you were talking about in your
previous
> > post). The problem is that this entry prevents me from granting any new
> > permission in the app-domain (XML serialization needs more that
> 'Execution'
> > permissions)
>
> Oh dear. There are no modifications made to machine policy when you
install
> VSTO (there were in the Beta, but not in the RTM release).

You're right, I was running the Beta2 version (I would have sweared that I
had correctly updated it but I was wrong). I have now installed the RTM
version. The 'System.dll' code group is no longer there.

> Even so, this is
> immaterial -- a normal machine will grant System.dll FullTrust by virtue
of
> it being in the MyComputer Zone. Remember that an assembly can match more
> than one code group in a given policy level, and it will be granted the
> union of all those permissions ("Exclusive" groups notwithstanding).

Yes, I know this. However, the problem is not about the 'real' System.dll,
which is indeed -and hopefully- granted Full Trust (through its membership
to the Microsoft_Strong_Name or the ECMA_Strong_Name group I guess). The
problem is about the bogus evidence -as you describe it- of the dynamic
assembly. This evidence seems to rely solely on the codebase of System.dll.
This is why we need to explitly add a code group with an URL membership
condition that matches it (because VSTO purposedfully overrides the
'FullTrust' grant of the My_Computer_Zone group).

>
> You can definitely add another rule to Machine (or even User) level that
> adds FullTrust for the System assembly and it will work. See my reply to
> your next post for why you can't do it at the AppDomain level.

See my answer's to your answer too :)

>
> > - Is there any reason why the permissions are set to 'Execution' instead
> of
> > 'Full Trust' ? Again, if I have well understood how the CAS works,
> granting
> > 'Full Trust' would not be a problem and would not allow any untrusted
> > assemblies to perform malicious operations.
>
> <sigh>This is what we get for being conservative with our
> permissions</sigh>.

This was a '.NET security newcomer' question and not a criticism for you
being conservative :)
I understand that there is no point in granting more privileges than what
seems appropriate.

>
> You may have heard of the rule of "run with least privilege." Well, since
we
> were only aware of Web Service calls using this bogus evidence, and web
> service proxies only needed Execution permission, we decided that is what
> we'd give them. No point in giving them permissions that they don't need,
> *just in case* there's some way to expose a web service contract that
> exploits some weird code-gen bug in WSDL (we fixed a bunch of problems
like
> that before shipping...). But now you've found some other construct that
> does need more permission (SerializationFormatter, no doubt).

Note:
I do not use the 'standard' Serialization mechanism of the .NET framework
(i.e. I do not use the BinaryFormatter or SoapFormatter classes).
Instead, I use the 'xml' serialization framework (i.e. the
System.Xml.Serialization.XmlSerializer class).

>
> > - If 'Full Trust' is not a problem, would you consider changing this in
> the
> > next VSTO version ?
>
> We're hoping that the underlying infrastructure will be changed so that we
> can remove the hack altogether, but if not then we'll definitely look at
> doing this. The essential problem is that the assembly is loaded with
"bad"
> evidence, but because most people build, test, and run their applications
> under default CLR policy, they never notice the consequences of using this
> bogus evidence.

Sure, this makes perfect sense.

>
> > - If the answer to the second question is 'yes', do you think that in
the
> > meantime I can safely perform this modification on the user computers
that
> > will use my Office Solution ?
>
> Yes, you can do that. I'd recommend a user-level change.

OK thanks. But see my other reply about app-domain-level policy ... it seems
to work (?)

--
Cyril

Cyril Mathey

unread,
Nov 24, 2003, 9:11:46 AM11/24/03
to
Hello again Peter,

> "Cyril Mathey" <dotnet...@ilog.fr> wrote in message
> news:OgAyzBF...@TK2MSFTNGP11.phx.gbl...
> > The real problem seems to be that I must modify the CAS before the
> assembly
> > is loaded (regardless of the 'System.dll' code Group).
>
> Unfortunately you won't be able to do that unless you create a new
AppDomain
> (which will probably make your life much harder). Once AppDomain policy is
> set (by our loader), it cannot be changed.

Hmmm. This is weird because it seems that I can modify it and that it solves
my problem. My mistake is that I was trying to update the CAS in an
AssemblyLoad event. If I perform the update before the dynamic assembly is
compiled and loaded (e.g. before my call to XmlSerializer.Deserialize), then
the changes to the app-domain-level policy seem to be effective.

FYI, here is the result of displaying the matching code groups of an
Evidence built with the URL of System.dll:

o- Level: Enterprise
UnionCodeGroup['All_Code']:FullTrust
o- Level: Machine
UnionCodeGroup['All_Code']:Nothing
o- Level: User
UnionCodeGroup['All_Code']:FullTrust
o- Level: AppDomain
UnionCodeGroup['AppDomain_All_Code']:Nothing
UnionCodeGroup['All_Code']:Nothing
UnionCodeGroup['All_Code']:Nothing
UnionCodeGroup['All_Code']:Nothing
UnionCodeGroup['System.dll']:Execution

Here is the app-domain-level after I perform my modification:

o- Level: AppDomain
UnionCodeGroup['AppDomain_All_Code']:Nothing
UnionCodeGroup['All_Code']:Nothing
UnionCodeGroup['All_Code']:Nothing
UnionCodeGroup['All_Code']:Nothing
UnionCodeGroup['System.dll']:Execution
UnionCodeGroup['Dynamic Assemblies']:FullTrust

See the new 'Dynamic Assemblies' code group that I have inserted. I use the
following code (SecurityUtil is a class of mine that provides
security-related utility methods):

using Policy = System.Security.Policy;
using Security = System.Security;

...

string url =
file://C:/windows/assembly/gac/system/1.0.5000.0__b77a5c561934e089/system.dll;
Policy.PolicyLevel level =
SecurityUtil.GetPolicyLevel(Security.PolicyLevelType.AppDomain);
if (level != null) {
Policy.CodeGroup parentGroup = level.RootCodeGroup;
Security.PermissionSet pSet = level.GetNamedPermissionSet("FullTrust");
Policy.PolicyStatement pStatement =
new Policy.PolicyStatement(pSet,
Policy.PolicyStatementAttribute.Nothing);
Policy.UrlMembershipCondition mship = new
Policy.UrlMembershipCondition(url);
Policy.UnionCodeGroup codeGroup = new Policy.UnionCodeGroup(mship,
pStatement);
codeGroup.Name = "Dynamic Assemblies";
// Add the new code group under the root code group
parentGroup.AddChild(codeGroup);
try {
Security.SecurityManager.SavePolicy();
} catch (Exception x) {
Trace.WriteLine("*** Cannot update security policy: " + x);
}
}

Well, it may work out of pure luck (or because of my profile/settings), but
I would be interested to know why I couldn't theorically modify the
appdomain policy ?
In all my recent readings about security, I have not read anything about a
predefined limitation, so either I have missed something or your appdomain
policy is explicitly configured in such a way that it cannot be modified
afterwards (how do you enforce this restriction ?)

>
> > Anyway, I guess that my questions still hold since if the ''System.dll'
> > group is granted 'Full Trust', I do not need to do anything to make XML
> > serialization work.
>
> That's the theory. I'd stick with modifying user policy for now. You can
> certainly change it at the Machine / Enterprise level if your users have
> permission to do that (ie, they're running as Admins) but there's no
> requirement that it be at either of those levels. User is good enough.

Sure. 'Lowest' level is the best. So if AppDomain-level does not work I will
use User-level. However AppDomain seems to be ok .....

Thanks again for your help.

--
Cyril


Peter Torr (MS)

unread,
Nov 25, 2003, 4:42:50 PM11/25/03
to
"Cyril Mathey" <dotnet...@ilog.fr> wrote in message
news:%23KUpnTp...@TK2MSFTNGP11.phx.gbl...

> Hmmm. This is weird because it seems that I can modify it and that it
solves
> my problem.

Interesting; I wonder if this is a bug...

When I said you couldn't change it, I was thinking of calling
SetAppDomainPolicy, which won't work twice:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemappdomainclasssetappdomainpolicytopic.asp

I had not considered directly modifying policy and then SavePolicy()-ing it.

> Well, it may work out of pure luck (or because of my profile/settings),
but
> I would be interested to know why I couldn't theorically modify the
> appdomain policy ?

I wonder if SavePolicy is supposed to update in-memory policy or not? It
seems that if you can't set policy once it has been set, you shouldn't be
able to change it dynamically either. I've pinged the security team about
that. It's not really a security hole -- you still need ControlPolicy to do
it -- but it sounds antithetical to the design of the system, and it enables
people to accidentally write unpredictable / unreliable / insecure / etc.
code.

It's definitely not an approach I would recommend for general purpose use;
if it is indeed a bug we may fix it in a future release. I'd go with
changing on-disk policy if that is at all possible.

> In all my recent readings about security, I have not read anything about a
> predefined limitation, so either I have missed something or your appdomain
> policy is explicitly configured in such a way that it cannot be modified
> afterwards (how do you enforce this restriction ?)

The URL above (and the implementation :-) ) tells you that you can't _set_
policy twice. There are problems with dynamically changing policy. For
example, let's say you do this:

1) Office creates the VSTO AppDomain and sets policy
2) You load your code and set new policy, granting permision XYZ to assembly
foo.dll
3) You load foo.dll, which is granted XYZ

This works fine, but what happens if foo.dll gets loaded before your policy
changes? In this particular case of System.dll where you "open up" policy,
the worst thing that can happen is that the assembly will fail to work as
expected. You've got unreliable software. It's a bummer, but not the end of
the world.

Now lets assume that your policy changes "lock down" policy even further.
You want to load foo.dll with less permissions that it would ordinarily be
loaded with, because (for whatever bizarre reason) that happens to be your
scenario and you think this is a good idea ;-). In this case foo.dll has
already been loaded with the increased permissions, so any protection you
thought you were getting has gone. You quite possibly have a security hole
in your product now.

So the moral of the story is "don't rely on changing policy once it has been
set."

Cyril Mathey

unread,
Nov 26, 2003, 3:42:54 AM11/26/03
to
Hello Peter,

Everything is clear now. I will go ahead and use a 'static' user-level
policy (I will do the update through an msi).

Thanks for taking the time to answer my questions.

--
Cyril


"Peter Torr (MS)" <pt...@microsoft.com> wrote in message

news:Oz4KS05s...@TK2MSFTNGP12.phx.gbl...

0 new messages