[w32file] Move MonoIO.Find{First,Next,Close} to managed
[mono.git] / mcs / tools / security / permview.cs
index 64d11813cde27e5a5d1e790d7c15d5a48dc9c25c..257a814925bf1a61a6f2860729f91bf24b241a7a 100644 (file)
 // Author:
 //     Sebastien Pouliot  <sebastien@ximian.com>
 //
-// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004-2007 Novell, Inc (http://www.novell.com)
 //
 
 using System;
 using System.Collections;
+using System.Collections.Generic;
 using System.IO;
 using System.Reflection;
 using System.Security;
-using System.Security.Permissions;
+using SSP = System.Security.Permissions;
+using System.Text;
+
+using Mono.Cecil;
 
 [assembly: AssemblyTitle ("Mono PermView")]
 [assembly: AssemblyDescription ("Managed Permission Viewer for .NET assemblies")]
 
 namespace Mono.Tools {
 
-       // There is no "managed way" to get this information using Fx 1.0/1.1
-       // so we must reflect inside Mono's corlib to find it. This also means
-       // that this won't work under MS runtime. Hopefully this will change
-       // with Fx 2.0 and Mono's PermView 2.0 should be working on both runtime.
+       static class SecurityDeclarationRocks {
 
-       // Notes:
-       // * Oct CTP started to return declarative security attributes with 
-       //   GetCustomAttributes, so this wont work with beta1 or previous 2.0 CTP
-       // * Dec/Nov CTP (and probably Oct CTP too) are bugged and always report 
-       //   LinkDemand as the SecurityAction (reported as FDBK19290)
+               public static PermissionSet ToPermissionSet (this SecurityDeclaration self)
+               {
+                       if (self == null)
+                               throw new ArgumentNullException ("self");
 
-       class PermView {
+                       PermissionSet set;
+                       if (TryProcessPermissionSetAttribute (self, out set))
+                               return set;
 
-               static private void Help () 
-               {
-                       Console.WriteLine ("Usage: permview [options] assembly{0}", Environment.NewLine);
-                       Console.WriteLine ("where options are:");
-                       Console.WriteLine (" -output filename  Output information into specified file.");
-                       Console.WriteLine (" -decl             Show declarative security attributes on classes and methods.");
-                       Console.WriteLine (" -help             Show help informations (this text)");
-                       Console.WriteLine ();
+                       return CreatePermissionSet (self);
                }
 
-               static bool declarative = false;
+               static bool TryProcessPermissionSetAttribute (SecurityDeclaration declaration, out PermissionSet set)
+               {
+                       set = null;
+
+                       if (!declaration.HasSecurityAttributes && declaration.SecurityAttributes.Count != 1)
+                               return false;
+
+                       var security_attribute = declaration.SecurityAttributes [0];
+                       var attribute_type = security_attribute.AttributeType;
+
+                       if (attribute_type.Name != "PermissionSetAttribute" || attribute_type.Namespace != "System.Security.Permissions")
+                               return false;
+
+                       var named_argument = security_attribute.Properties [0];
+                       if (named_argument.Name != "XML")
+                               throw new NotSupportedException ();
+
+                       var attribute = new SSP.PermissionSetAttribute ((SSP.SecurityAction) declaration.Action);
+                       attribute.XML = (string) named_argument.Argument.Value;
 
-               static void ShowPermissionSet (TextWriter tw, string header, PermissionSet ps)
+                       set = attribute.CreatePermissionSet ();
+                       return true;
+               }
+
+               static PermissionSet CreatePermissionSet (SecurityDeclaration declaration)
                {
-                       if (header != null)
-                               tw.WriteLine (header);
+                       var set = new PermissionSet (SSP.PermissionState.None);
 
-                       if ((ps == null) || ((ps.Count == 0) && !ps.IsUnrestricted ())) {
-                               tw.WriteLine ("\tNone");
-                       } else {
-                               tw.WriteLine (ps.ToString ());
+                       foreach (var attribute in declaration.SecurityAttributes) {
+                               var permission = CreatePermission (declaration, attribute);
+                               set.AddPermission (permission);
                        }
 
-                       tw.WriteLine ();
+                       return set;
                }
 
-#if NET_2_0
-               static PermissionSet GetPermissionSet (SecurityAttribute sa)
+               static IPermission CreatePermission (SecurityDeclaration declaration, SecurityAttribute attribute)
                {
-                       PermissionSet ps = null;
-                       if (sa is PermissionSetAttribute) {
-                               ps = (sa as PermissionSetAttribute).CreatePermissionSet ();
-                       } else {
-                               ps = new PermissionSet (PermissionState.None);
-                               IPermission p = sa.CreatePermission ();
-                               ps.AddPermission (p);
-                       }
-                       return ps;
+                       var attribute_type = Type.GetType (attribute.AttributeType.FullName);
+                       if (attribute_type == null)
+                               throw new ArgumentException ();
+
+                       var security_attribute = CreateSecurityAttribute (attribute_type, declaration);
+                       if (security_attribute == null)
+                               throw new InvalidOperationException ();
+
+                       CompleteSecurityAttribute (security_attribute, attribute);
+
+                       return security_attribute.CreatePermission ();
                }
-#else
-               static PermissionSet GetPermissionSet (Assembly a, string name)
+
+               static void CompleteSecurityAttribute (SSP.SecurityAttribute security_attribute, SecurityAttribute attribute)
                {
-                       FieldInfo fi = typeof (Assembly).GetField (name, BindingFlags.Instance | BindingFlags.NonPublic);
-                       if (fi == null)
-                               throw new NotSupportedException ("Wrong runtime ?");
-                       return (PermissionSet) fi.GetValue (a);
+                       if (attribute.HasFields)
+                               CompleteSecurityAttributeFields (security_attribute, attribute);
+
+                       if (attribute.HasProperties)
+                               CompleteSecurityAttributeProperties (security_attribute, attribute);
                }
-#endif
 
-               static bool ProcessAssemblyOnly (TextWriter tw, Assembly a) 
+               static void CompleteSecurityAttributeFields (SSP.SecurityAttribute security_attribute, SecurityAttribute attribute)
                {
-#if NET_2_0
-                       // This should work for all 2.0 runtime - unless we hit a bug :-(
-                       object[] attrs = a.GetCustomAttributes (false);
-                       foreach (object attr in attrs) {\r
-                               if (attr is SecurityAttribute) {
-                                       SecurityAttribute sa = (attr as SecurityAttribute);
-                                       switch (sa.Action) {
-                                       case SecurityAction.RequestMinimum:\r
-                                               ShowPermissionSet (tw, "Minimum Permission Set:", GetPermissionSet (sa));\r
-                                               break;\r
-                                       case SecurityAction.RequestOptional:\r
-                                               ShowPermissionSet (tw, "Optional Permission Set:", GetPermissionSet (sa));\r
-                                               break;\r
-                                       case SecurityAction.RequestRefuse:\r
-                                               ShowPermissionSet (tw, "Refused Permission Set:", GetPermissionSet (sa));\r
-                                               break;\r
-                                       default:\r
-                                               // Bug in VS.NET 2005 Nov CTP - Every action is a LinkDemand :(\r
-                                               string msg = String.Format ("ERROR {0} Permission Set:", sa.Action);\r
-                                               ShowPermissionSet (tw, msg, GetPermissionSet (sa));\r
-                                               break;
-                                       }
-                               }
-                       }
-#else
-                       // Note: This will only work using the Mono runtime as we P/Invoke
-                       // into Mono's corlib to get the required informations.
+                       var type = security_attribute.GetType ();
 
-                       Type t = typeof (Assembly);
+                       foreach (var named_argument in attribute.Fields)
+                               type.GetField (named_argument.Name).SetValue (security_attribute, named_argument.Argument.Value);
+               }
 
-                       // Minimum, Optional and Refuse permission set are only evaluated
-                       // on demand (delayed as much as possible). A call Resolve will
-                       // trigger their retrieval from the assembly metadata
-                       MethodInfo resolve = t.GetMethod ("Resolve", BindingFlags.Instance | BindingFlags.NonPublic);
-                       if (resolve == null)
-                               return false;
-                       resolve.Invoke (a, null);
+               static void CompleteSecurityAttributeProperties (SSP.SecurityAttribute security_attribute, SecurityAttribute attribute)
+               {
+                       var type = security_attribute.GetType ();
 
-                       ShowPermissionSet (tw, "Minimal Permission Set:", GetPermissionSet (a, "_minimum"));
-                       ShowPermissionSet (tw, "Optional Permission Set:", GetPermissionSet (a, "_optional"));
-                       ShowPermissionSet (tw, "Refused Permission Set:", GetPermissionSet (a, "_refuse"));
-#endif
-                       return true;
+                       foreach (var named_argument in attribute.Properties)
+                               type.GetProperty (named_argument.Name).SetValue (security_attribute, named_argument.Argument.Value, null);
                }
 
-/*             static SecurityAction[] actions = {
-                       SecurityAction.LinkDemand,
-                       SecurityAction.InheritanceDemand,
-                       SecurityAction.Demand,
-                       (SecurityAction) 13,                            // Hack for NonCasDemand
-                       (SecurityAction) 14,                            // Hack for NonCasLinkDemand
-                       (SecurityAction) 15,                            // Hack for NonCasInheritanceDemand
-                       SecurityAction.Assert,
-                       SecurityAction.Deny,
-                       SecurityAction.PermitOnly,
-               };
-
-               static MethodInfo method_getdeclsec;
-
-               static PermissionSet GetDeclarativeSecurity (MethodInfo mi, SecurityAction action)
+               static SSP.SecurityAttribute CreateSecurityAttribute (Type attribute_type, SecurityDeclaration declaration)
                {
-                       if (method_getdeclsec == null) {
-                               Type t = typeof (Int32).Assembly.GetType ("System.Reflection.MonoMethod");
-                               method_getdeclsec = t.GetMethod ("GetDeclarativeSecurity", BindingFlags.Instance | BindingFlags.Public);
+                       SSP.SecurityAttribute security_attribute;
+                       try {
+                               security_attribute = (SSP.SecurityAttribute) Activator.CreateInstance (
+                                       attribute_type, new object [] { (SSP.SecurityAction) declaration.Action });
+                       } catch (MissingMethodException) {
+                               security_attribute = (SSP.SecurityAttribute) Activator.CreateInstance (attribute_type, new object [0]);
                        }
-                       return (PermissionSet) method_getdeclsec.Invoke (mi, new object [1] { action });
-               }
-*/
-               static void ProcessMethod (TextWriter tw, MethodInfo mi) 
-               {\r
-                       // no need to process methods without security informations\r
-                       if ((mi.Attributes & MethodAttributes.HasSecurity) == MethodAttributes.HasSecurity) {
-#if NET_2_0\r
-                               object[] attrs = mi.GetCustomAttributes (false);\r
-                               foreach (object attr in attrs) {\r
-                                       if (attr is SecurityAttribute) {\r
-                                               SecurityAttribute sa = (attr as SecurityAttribute);\r
-                                               tw.WriteLine ("Method {0} {1} Permission Set", mi, sa.Action);\r
-                                               ShowPermissionSet (tw, null, GetPermissionSet (sa));\r
-                                       }\r
-                               }
-#else
-/*                             foreach (SecurityAction action in actions) {
-                                       PermissionSet ps = GetDeclarativeSecurity (mi, action);
-                                       if (ps != null) {
-                                               tw.WriteLine ("Method {0} {1} Permission Set", mi, action);
-                                               ShowPermissionSet (tw, null, ps);
-                                       }
-                               }*/
-#endif\r
-                       }\r
-               }
 
-               static BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance |
-                       BindingFlags.Static | BindingFlags.GetProperty | BindingFlags.SetProperty;
-
-               static void ProcessType (TextWriter tw, Type t) 
-               {\r
-                       // no need to process types without security informations\r
-                       if ((t.Attributes & TypeAttributes.HasSecurity) == TypeAttributes.HasSecurity) {\r
-#if NET_2_0\r
-                               object[] attrs = t.GetCustomAttributes (false);
-                               foreach (object attr in attrs) {\r
-                                       if (attr is SecurityAttribute) {
-                                               SecurityAttribute sa = (attr as SecurityAttribute);\r
-                                               tw.WriteLine ("Class {0} {1} Permission Set", t, sa.Action);\r
-                                               ShowPermissionSet (tw, null, GetPermissionSet (sa));
-                                       }
-                               }
-#else
-                               tw.WriteLine ("Class {0} 'SecurityAction' Permission Set", t);
-                               // SecurityAction
-                               ShowPermissionSet (tw, null, null);
-#endif\r
-                       }\r
+                       return security_attribute;
                }
+       }
+
+       class SecurityElementComparer : IComparer {
 
-               static bool ProcessAssemblyComplete (TextWriter tw, Assembly a) 
+               public int Compare (object x, object y)
                {
-#if NET_2_0\r
-                       string header = "Assembly {0} Permission Set";\r
-                       object [] attrs = a.GetCustomAttributes (false);\r
-                       foreach (object attr in attrs) {\r
-                               if (attr is SecurityAttribute) {\r
-                                       SecurityAttribute sa = (attr as SecurityAttribute);\r
-                                       // Bug in VS.NET 2005 Nov CTP - Evrything action is a LinkDemand\r
-                                       ShowPermissionSet (tw, String.Format (header, sa.Action.ToString ()), GetPermissionSet (sa));\r
-                               }\r
-                       }
-#else
-                       tw.WriteLine ("Currently unsupported");
-                       return false;
-/*                     Type t = typeof (Assembly);
-                       FieldInfo fi = t.GetField ("_minimum", BindingFlags.Instance | BindingFlags.NonPublic);
-                       if (fi == null)
-                               return false;
-                       PermissionSet ps = (PermissionSet) fi.GetValue (a);
-                       if (ps != null)
-                               ShowPermissionSet (tw, "Assembly RequestMinimum Permission Set:", ps);
-                       
-                       fi = t.GetField ("_optional", BindingFlags.Instance | BindingFlags.NonPublic);
-                       if (fi == null)
-                               return false;
-                       ps = (PermissionSet) fi.GetValue (a);
-                       if (ps != null)
-                               ShowPermissionSet (tw, "Assembly RequestOptional Permission Set:", ps);
+                       SecurityElement sx = (x as SecurityElement);
+                       SecurityElement sy = (y as SecurityElement);
+                       if (sx == null)
+                               return (sy == null) ? 0 : -1;
+                       else if (sy == null)
+                               return 1;
 
-                       fi = t.GetField ("_refuse", BindingFlags.Instance | BindingFlags.NonPublic);
-                       if (fi == null)
-                               return false;
-                       ps = (PermissionSet) fi.GetValue (a);
-                       if (ps != null)
-                               ShowPermissionSet (tw, "Assembly RequestRefuse Permission Set:", ps);*/
-#endif\r
-/*                     Type [] types = a.GetTypes ();\r
-                       foreach (Type type in types) {\r
-                               ProcessType (tw, type);\r
-                               foreach (MethodInfo mi in type.GetMethods (flags)) {\r
-                                       ProcessMethod (tw, mi);\r
-                               }\r
-                       }
-\r
-                       return true;*/
+                       // compare by name (type name, method name, action name)
+                       return String.Compare (sx.Attribute ("Name"), sy.Attribute ("Name"));
                }
+       }
+
+       class PermView {
+
+               private const string NotSpecified = "\tNot specified.";
+
+               static private void Help () 
+               {
+                       Console.WriteLine ("Usage: permview [options] assembly{0}", Environment.NewLine);
+                       Console.WriteLine ("where options are:");
+                       Console.WriteLine (" -output filename  Output information into specified file.");
+                       Console.WriteLine (" -decl             Show declarative security attributes on classes and methods.");
+                       Console.WriteLine (" -xml              Output in XML format");
+                       Console.WriteLine (" -help             Show help informations (this text)");
+                       Console.WriteLine ();
+               }
+
+               static bool declarative = false;
+               static bool xmloutput = false;
 
                static TextWriter ProcessOptions (string[] args)
                {
@@ -263,6 +176,11 @@ namespace Mono.Tools {
                                case "--OUTPUT":
                                        tw = (TextWriter) new StreamWriter (args [++i]);
                                        break;
+                               case "/XML":
+                               case "-XML":
+                               case "--XML":
+                                       xmloutput = true;
+                                       break;
                                case "/HELP":
                                case "/H":
                                case "-HELP":
@@ -278,6 +196,170 @@ namespace Mono.Tools {
                        return tw;
                }
 
+               static bool ProcessAssemblyOnly (TextWriter tw, AssemblyDefinition ad) 
+               {
+                       bool result = true;
+                       string minimal = NotSpecified + Environment.NewLine;
+                       string optional = NotSpecified + Environment.NewLine;
+                       string refused = NotSpecified + Environment.NewLine;
+
+                       foreach (SecurityDeclaration decl in ad.SecurityDeclarations) {
+                               switch (decl.Action) {
+                               case Mono.Cecil.SecurityAction.RequestMinimum:
+                                       minimal = decl.ToPermissionSet ().ToString ();
+                                       break;
+                               case Mono.Cecil.SecurityAction.RequestOptional:
+                                       optional = decl.ToPermissionSet ().ToString ();
+                                       break;
+                               case Mono.Cecil.SecurityAction.RequestRefuse:
+                                       refused = decl.ToPermissionSet ().ToString ();
+                                       break;
+                               default:
+                                       tw.WriteLine ("Invalid assembly level declaration {0}{1}{2}",
+                                               decl.Action, Environment.NewLine, decl.ToPermissionSet ());
+                                       result = false;
+                                       break;
+                               }
+                       }
+
+                       tw.WriteLine ("Minimal Permission Set:");
+                       tw.WriteLine (minimal);
+                       tw.WriteLine ("Optional Permission Set:");
+                       tw.WriteLine (optional);
+                       tw.WriteLine ("Refused Permission Set:");
+                       tw.WriteLine (refused);
+                       return result;
+               }
+
+               static void ShowSecurity (TextWriter tw, string header, IEnumerable<SecurityDeclaration> declarations)
+               {
+                       foreach (SecurityDeclaration declsec in declarations) {
+                               tw.WriteLine ("{0} {1} Permission Set:{2}{3}", header,
+                                       declsec.Action, Environment.NewLine, declsec.ToPermissionSet ());
+                       }
+               }
+
+               static bool ProcessAssemblyComplete (TextWriter tw, AssemblyDefinition ad)
+               {
+                       if (ad.SecurityDeclarations.Count > 0) {
+                               ShowSecurity (tw, "Assembly", ad.SecurityDeclarations);
+                       }
+
+                       foreach (ModuleDefinition module in ad.Modules) {
+
+                               foreach (TypeDefinition type in module.Types) {
+
+                                       if (type.SecurityDeclarations.Count > 0) {
+                                               ShowSecurity (tw, "Class " + type.ToString (), ad.SecurityDeclarations);
+                                       }
+
+                                       foreach (MethodDefinition method in type.Methods) {
+                                               if (method.SecurityDeclarations.Count > 0) {
+                                                       ShowSecurity (tw, "Method " + method.ToString (), method.SecurityDeclarations);
+                                               }
+                                       }
+                               }
+                       }
+                       return true;
+               }
+
+               static void AddAttribute (SecurityElement se, string attr, string value)
+               {
+                       value = value.Replace ("&", "&amp;");
+                       se.AddAttribute (attr, value);
+               }
+
+               static SecurityElement AddSecurityXml (IEnumerable<SecurityDeclaration> declarations)
+               {
+                       ArrayList list = new ArrayList ();
+                       foreach (SecurityDeclaration declsec in declarations) {
+                               SecurityElement child = new SecurityElement ("Action");
+                               AddAttribute (child, "Name", declsec.Action.ToString ());
+                               child.AddChild (declsec.ToPermissionSet ().ToXml ());
+                               list.Add (child);
+                       }
+                       // sort actions
+                       list.Sort (Comparer);
+
+                       SecurityElement se = new SecurityElement ("Actions");
+                       foreach (SecurityElement child in list) {
+                               se.AddChild (child);
+                       }
+                       return se;
+               }
+
+               static SecurityElementComparer comparer;
+               static IComparer Comparer {
+                       get {
+                               if (comparer == null)
+                                       comparer = new SecurityElementComparer ();
+                               return comparer;
+                       }
+               }
+
+               static bool ProcessAssemblyXml (TextWriter tw, AssemblyDefinition ad)
+               {
+                       SecurityElement se = new SecurityElement ("Assembly");
+                       se.AddAttribute ("Name", ad.Name.FullName);
+
+                       if (ad.SecurityDeclarations.Count > 0) {
+                               se.AddChild (AddSecurityXml (ad.SecurityDeclarations));
+                       }
+
+                       ArrayList tlist = new ArrayList ();
+                       ArrayList mlist = new ArrayList ();
+
+                       foreach (ModuleDefinition module in ad.Modules) {
+
+                               foreach (TypeDefinition type in module.Types) {
+
+                                       SecurityElement klass = new SecurityElement ("Class");
+                                       SecurityElement methods = new SecurityElement ("Methods");
+
+                                       SecurityElement typelem = null;
+                                       if (type.SecurityDeclarations.Count > 0) {
+                                               typelem = AddSecurityXml (type.SecurityDeclarations);
+                                       }
+
+                                       if (mlist.Count > 0)
+                                               mlist.Clear ();
+
+                                       foreach (MethodDefinition method in type.Methods) {
+                                               if (method.SecurityDeclarations.Count > 0) {
+                                                       SecurityElement meth = new SecurityElement ("Method");
+                                                       AddAttribute (meth, "Name", method.ToString ());
+                                                       meth.AddChild (AddSecurityXml (method.SecurityDeclarations));
+                                                       mlist.Add (meth);
+                                               }
+                                       }
+
+                                       // sort methods
+                                       mlist.Sort (Comparer);
+                                       foreach (SecurityElement method in mlist) {
+                                               methods.AddChild (method);
+                                       }
+
+                                       if ((typelem != null) || ((methods.Children != null) && (methods.Children.Count > 0))) {
+                                               AddAttribute (klass, "Name", type.ToString ());
+                                               if (typelem != null)
+                                                       klass.AddChild (typelem);
+                                               if ((methods.Children != null) && (methods.Children.Count > 0))
+                                                       klass.AddChild (methods);
+                                               tlist.Add (klass);
+                                       }
+                               }
+
+                               // sort types
+                               tlist.Sort (Comparer);
+                               foreach (SecurityElement type in tlist) {
+                                       se.AddChild (type);
+                               }
+                       }
+
+                       tw.WriteLine (se.ToString ());
+                       return true;
+               }
+
                [STAThread]
                static int Main (string[] args) 
                {
@@ -293,18 +375,29 @@ namespace Mono.Tools {
                                        return 0;
 
                                string assemblyName = args [args.Length - 1];
-                               Assembly a = Assembly.LoadFile (Path.GetFullPath (assemblyName));
-                               if (a != null) {
-                                       bool complete = (declarative ?
-                                               ProcessAssemblyComplete (tw, a) :
-                                               ProcessAssemblyOnly (tw, a));
-                                       if (!complete) {
-                                               Console.Error.WriteLine ("Couldn't reflect informations. Wrong runtime ?");
-                                               return 1;
+                               using (AssemblyDefinition ad = AssemblyDefinition.ReadAssembly (assemblyName)) {
+                                       if (ad != null) {
+                                               bool complete = false;
+
+                                               if (declarative) {
+                                                       // full output (assembly+classes+methods)
+                                                       complete = ProcessAssemblyComplete (tw, ad);
+                                               } else if (xmloutput) {
+                                                       // full output in XML (for easier diffs after c14n)
+                                                       complete = ProcessAssemblyXml (tw, ad);
+                                               } else {
+                                                       // default (assembly only)
+                                                       complete = ProcessAssemblyOnly (tw, ad);
+                                               }
+
+                                               if (!complete) {
+                                                       Console.Error.WriteLine ("Couldn't reflect informations.");
+                                                       return 1;
+                                               }
+                                       } else {
+                                               Console.Error.WriteLine ("Couldn't load assembly '{0}'.", assemblyName);
+                                               return 2;
                                        }
-                               } else {
-                                       Console.Error.WriteLine ("Couldn't load assembly '{0}'.", assemblyName);
-                                       return 2;
                                }
                                tw.Close ();
                        }