Jump to navigation

Poupou's Corner of the Web

Looking for perfect security? Try a wireless brick.
Otherwise you may find some unperfect stuff here...


Being ignored

Code Access Security (CAS) work in progressing. Right now I'm looking into executing declarative security attributes, starting with SecurityAction.LinkDemand. LinkDemand is different because it is a JIT time action (and my first time hacking into mini). This means that any SecurityException will be thrown when the JIT compiles IL into native code.

In order to test this I have created a DebugPermission and it's associated DebugPermissionAttribute. This isn't a CAS permission, i.e. DebugPermission doesn't derive from CodeAccessPermission but directly implements IPermission (just like PrincipalPermission does) - and the JIT doesn't have to care about this. The important thing is that this allows the permission to provide it's own Demand implementation - required for my testing/debugging purpose. Anyway LinkDemand doesn't involve a stack walk, so I ain't loosing nothing.

The cool thing about DebugPermission is that is shows when and how compilers and the JIT use the permission. Much cooler is that this indirectly shows what's being ignored!

// This sample tries to use as much different LinkDemand as possible (from a // valid C# compiler point of view) without actually triggering the JIT to // call Demand. // // The problem is that there is no relation between an AttributeTarget // (enforced by the compiler) and a SecurityAction (enforced by the JIT, // runtime or security manager). // // Commented LinkDemand cause compilation failures (CS1577, CS0647) // Non commented LinkDemand are ignored by the JIT using System; using System.Security; using System.Security.Permissions; using Mono.Permissions; // CS1577: Assembly generation failed -- SecurityAction type invalid on assembly. //[assembly: DebugPermission (SecurityAction.LinkDemand, Fail=true, Message="assembly")] [DebugPermission (SecurityAction.LinkDemand, Fail=true, Message="interface")] public interface Display { [DebugPermission (SecurityAction.LinkDemand, Fail=true, Message="interface method")] int Show (string message); } [DebugPermission (SecurityAction.LinkDemand, Fail=true, Message="enum")] public enum Kind { // CS0647 - Security custom attribute attached to invalid parent. // [DebugPermission (SecurityAction.LinkDemand, Fail=true, Message="enum member")] WithLinkDemand, WithoutLinkDemand } [DebugPermission (SecurityAction.LinkDemand, Fail=true, Message="struct")] public struct Data { // CS0647 - Security custom attribute attached to invalid parent. // [DebugPermission (SecurityAction.LinkDemand, Fail=true, Message="struct member")] public string Message; } public delegate int DisplayHandler (string msg); public class Program : Display { // CS0647 - Security custom attribute attached to invalid parent. // [DebugPermission (SecurityAction.LinkDemand, Fail=true, Message="static field")] static string ep; static Data data; // CS0647 - Security custom attribute attached to invalid parent. // [DebugPermission (SecurityAction.LinkDemand, Fail=true, Message="field")] string product = "Mono"; // CS0647 - Security custom attribute attached to invalid parent. // [DebugPermission (SecurityAction.LinkDemand, Fail=true, Message="event")] public event DisplayHandler OnShow; [DebugPermission (SecurityAction.LinkDemand, Fail=true, Message="static constructor")] static Program () { ep = "entrypoint"; data = new Data (); data.Message = "Hello {0}!"; } // CS0647 - Security custom attribute attached to invalid parent. // [return: DebugPermission (SecurityAction.LinkDemand, Fail=true, Message="return value")] public int Show (string message) { return OnShow (message); } public int Display ( // CS0647 - Security custom attribute attached to invalid parent. // [DebugPermission (SecurityAction.LinkDemand, Fail=true, Message="parameter")] string message) { Console.WriteLine (message); return 0; } [DebugPermission (SecurityAction.LinkDemand, Fail=true, Message="entrypoint")] static public void Main (string [] args) { Console.WriteLine ("{0}.starts", ep); Program p = new Program (); p.OnShow += new DisplayHandler (p.Display); p.Show (String.Format (data.Message, p.product)); Console.WriteLine ("{0}.end", ep); } }

First thing we notice is that even if DebugPermissionAttribute supports all targets, AttributeTargets.All, the compiler will reject some usages. This is because there's is no relation between AttributeTargets and SecurityAction (neither in design nor documented AFAIK).

Second thing we see is that DebugPermission is very verbose about its usage and shows a nice (but too long to be included here) log when compiled by CSC. This means the compiler is actually doing work for all, non-commented, security attributes in the sample (actually it call IsSubsetOf and, if true is returned, ToXml). However when we execute the sample we get... nothing from DebugPermission.

# linkdemand_ignored.exe entrypoint.starts Hello Mono! entrypoint.end

Nothing because the JIT ignores them! So if you ever used LinkDemand in your code I hope you tested them well enough because getting them compiled wasn't enough ;-)

Note: Please email me any other ignored case I may have missed in my sample, as this will ends up in the Mono regression testing.

9/30/2004 10:15:37 | Comments | Permalink

Now that's debugging in '04!

That short MSDN article, Create a Debugger Visualizer Using Visual Studio 2005 Beta 1, really got my attention.

Like many I have done countless hours of debugging and, most of the time, I'm as much angry at myself as I'm angry with the debugger when I found the flaw. Why ? Because, let's face it, debuggers still do a very bad job at interpreting complex data. The bug is rarely the data itself but without understanding the data you're simply working in the dark.

Personally I often deal with this complex data. Many security structures, like X.509 certificatres, are encoded in ASN.1 (and may be part of a SSL communication session). I also have a long history of binary protocols, like smartcards (ISO 7816 APDU's), lots of communication with strange hardware, etc. The best developers I have seen working (well debugging) such data where the one that could decode, at full or in parts, the data in their head. To be fair it's not that bad, considering they got their job because of their head anyway (or more precisely because of what they had in it) but it's still a big waste of brain power.

Ok so what's so good about this article ? IDE plugins ain't new ?

Yeah, I know, plugins aren't new. But somehow implementing half a dozen COM interface to get a string didn't look a big timesaver either. And generally debugging debugger plugins isn't considered pure joy either (but YMMV).

What was missing was an easy way to get data out of a debugging session. By easy I mean right here, right now, that @$#^ ASN.1 blob looks wrong. It's too late to modify the code, it's too big to be decoded by hand (or it's also to late to do so ;-).

So something easy would be writing an assembly consisting of a single class with a single method to interpret the data, compiling it and, without even restarting the IDE, using the new code as an help to debug the current problem.

We're not there yet but we're getting close!

Actually with Visual Studio .NET 2005 the step are:

  1. Create a new assembly, referencing the debugging assembly;
  2. Implement this single method, Show, in a class implementing IDebugVisualizer;
  3. Add an assembly-level attribute, DebuggerVisualizer, to map the debugging extension with a type;
  4. Compile the assembly [*];
  5. Copy the assembly to a pre-determined location (which can be automated with the compile step);
  6. and use it without restarting Visual Studio!

[*] However compiling this new assembly does require to stop your current debugging session (so hopefully the bug is easily replicable).

There are a few more limitations: debugged types must be [Serializable] (sadly XmlNode isn't) and it doesn't seems to work on array (e.g. byte[]). Those aren't big problems because most types can easily be transformed into strings in the watch list using one of their property, like XmlNode.OuterXml, or another method, like BitConveter.ToString (array).

Here my first try to save strings into files (with different encodings):

using System; using System.Diagnostics; using System.IO; using System.Text; using System.Windows.Forms; [assembly: DebuggerVisualizer (typeof (VisualizerObjectSource), typeof (Poupou.DebugViz.Common.SaveTextToFile), VisualizerUIType.Modal, Target = typeof (string), Description = "Save as text...")] namespace Poupou.DebugViz.Common { public class SaveTextToFile : IDebugVisualizer { private const string filter = "ASCII Text file (*.txt)|*.txt|" + "UTF-8 Text file (*.txt)|*.txt|" + "Unicode Text file (*.txt)|*.txt|" + "UTF-7 Text file (*.txt)|*.txt|" + "UTF-32 Text file (*.txt)|*.txt|" + "Big Endian Unicode Text file (*.txt)|*.txt|" + "All files (*.*)|*.*"; private string GetStringFromObject (object obj) { if (obj == null) return null; // extend as needed - class must be [Serializable] // to work with VisualizerObjectSource if (obj is string) return (obj as string); return null; } private byte[] Encode (string s, int filterIndex) { switch (filterIndex) { case 1: return Encoding.ASCII.GetBytes (s); case 2: return Encoding.UTF8.GetBytes (s); case 3: return Encoding.Unicode.GetBytes (s); case 4: return Encoding.UTF7.GetBytes (s); case 5: return Encoding.UTF32.GetBytes (s); case 6: return Encoding.BigEndianUnicode.GetBytes (s); default: return Encoding.Default.GetBytes (s); } } void IDebugVisualizer.Show (IServiceProvider windowService, IVisualizerObjectProvider objectProvider, VisualizerUIType uiType) { if (objectProvider == null) return; string str = GetStringFromObject (objectProvider.GetObject ()); if (str == null) return; SaveFileDialog dlg = new SaveFileDialog (); dlg.Filter = filter; if (dlg.ShowDialog () == DialogResult.OK) { using (Stream s = dlg.OpenFile ()) { if (s != null) { byte[] array = Encode (str, dlg.FilterIndex); s.Write (array, 0, array.Length); s.Close (); } } } } } }

It's easy to see the possibilities - I, for one, would like to deal with the three X509Certificate classes :-). However the biggest advantage is that because it's simple there no need to do them before you need them. Most of the plugins will probably save you time the first time you use them - at least if you use them wisely ;-).

I sure hope to see something similar, from an simplicity point of view, in other IDEs soon :-)

9/19/2004 23:04:11 | Comments | Permalink

It's time to move on...

As many of you must have heard MD5, and a lot of older hash algorithms, has been broken (link with nice "try it at home" instructions :-). That is MD5 isn't collision free (actually so is SHA-0 but most people don't even know it existed so... ;-).

This can look very bad when know all (well parts) of the software that relies on MD5 (e.g. SSL/TLS). However not being collision free doesn't means an hash algorithm is useless - and when that property is really important good protocols will protect themselves. For example SSL use a dual concatenated hash (MD5-SHA1) while TLS use an HMAC (with inner and outer hash). You can still worry about SSL/TLS as you like but don't use MD5 as an easy excuse ;-)

There is a lot of techno-talk (and FUD) about this. Actually this isn't big news as MD5 had been known to be weak for quite some time. RSA even advised not to use the algorithm in new design, requiring collision resistance, since summer 1996. For those interested to understand more in a single page I suggest the recent article from Bruce Schneier.

Right now the main alternative is to use SHA-1 or one of the newer SHA-2 family algorithms. In this .NET users are lucky as all SHA algorithms are available - except for the newer SHA-224 variant. This late member was added to fill a gap in the security level NIST is promoting.

Sadly it doesn't seems we'll get SHA-224 support in .NET Framework 2.0 (but feel free to vote for it ;-). It's even more sad considering that SHA-224 is a subset of SHA-256 (like SHA-384 is a subset of SHA-512) - i.e. except for it's initial values (eight 32 bits values) it's the same algorithm as SHA-256, where the last 4 bytes are discarded (and yes that also means that it is computationally similar to SHA-256).

What does this means for Mono:: ?

Well, not much for Mono itself. But, as a framework, Mono has a kind of responsability to provide the right tools and documentation for it's developers. So the first step was to update Monodoc (online version may not be up to date right now) with this new information. This is similar to the warnings we already had for older hash algorithms: MD2 and MD4 in Mono.Security.

The second step was to add support for SHA-224 in Mono.Security.dll assembly so it can become part of Mono in its next release. This way we both retain API compatibility with Fx 2.0 and let Windows/MS runtime users the flexibility to use SHA-224 is they want to.

We can't really say goodbye to MD5. This algorithm will stays with us for many (many!) years - witness the need to implement MD2 and MD4 in Mono as proof of this. But we should welcome the newer one as soon as we design ;-).

9/18/2004 20:55:12 | Comments | Permalink

Much more CAS samples

Ok, I promise it's the last one on samples, but it's worth it as I finally got around most MSDN library samples about CAS.

A got a few surprises (i.e. really not anticipated), like PermissionSet.IsEmpty() returning true even if PermissionSet.Count > 1 - but only when the included permissions are themself "somewhat" empty (as there is no IPermission.IsEmpty() method).

I also had a few "non" surprises (i.e. I could/should have anticipated them). Many were due to the fact that IPermission.Intersect can return null or an empty set (new PermissionSet (PermissionState.None)) as an empty result (which resulted in a number of NullReferenceException). Support for more wildcards in permissions, like in StrongName "names" (that's one I should have seen coming).

And finally some, always subtle, differences between Fx 1.1 and 2.0 beta 1. Anyway I suspect I'll get more (and probably less subtle) differences in the next 2.0 betas - like unrestricted identity permissions.

This time I won't bore you with all the gory step-by-step details - as a summary should be good enough ;-). So here are the current results using CVS...

In System.Security namespace:

  • PermissionSet - Fully working
  • NamedPermissionSet - not fully working [1]
  • SecurityElement - Fully working
  • SecurityManager - not fully working [1]

[1] Permissions classes outside mscorlib.dll aren't completed. This is a case where throwing NotImplementedException isn't the best solution.

In System.Security.Permissions namespace:

  • EnvironmentPermission - Fully working
  • FileDialogPermission - Fully working
  • FileIOPermission - Fully working [2]
  • IsolatedStorageFilePermission - not fully working [3]
  • PublisherPermission - Fully working
  • ReflectionPermission - Fully working
  • RegistryPermission - Fully working
  • SecurityPermission - Fully working
  • SiteIdentityPermission - Fully working
  • StrongNameIdentityPermission-1 - not fully working [4]
  • StrongNameIdentityPermission-2 - Fully working
  • UIPermission - Fully working
  • UrlIdentityPermission - Fully working
  • ZoneIdentityPermission - Fully working

[2] Some filenames had to be changed in order to run under Linux.
[3] Everything works except Deny and PermitOnly (not implemented yet). Other samples do not use them.
[4] Requires declarative permission support (not implemented yet).

In System.Security.Policy namespace:

  • Evidence - Fully working [5]
  • FileMatchCodeGroup - Fully working [2]
  • FirstMatchCodeGroup - Fully working [2]
  • PolicyLevel - not fully working [6]
  • PolicyStatement - Fully working

[5] Commented Fx 2.0 specific stuff.
[6] FullTrustAssemblies aren't implemented. It also need more love in resolution.

9/6/2004 18:23:57 | Comments | Permalink

The views expressed on this website/weblog are mine alone and do not necessarily reflect the views of my employer.