// // permview.cs: Managed Permission Viewer for .NET assemblies // // Author: // Sebastien Pouliot // // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) // using System; using System.Collections; using System.IO; using System.Reflection; using System.Security; using System.Security.Permissions; [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. // 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) class PermView { 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 (); } static bool declarative = false; static void ShowPermissionSet (TextWriter tw, string header, PermissionSet ps) { if (header != null) tw.WriteLine (header); if ((ps == null) || ((ps.Count == 0) && !ps.IsUnrestricted ())) { tw.WriteLine ("\tNone"); } else { tw.WriteLine (ps.ToString ()); } tw.WriteLine (); } #if NET_2_0 static PermissionSet GetPermissionSet (SecurityAttribute sa) { 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; } #else static PermissionSet GetPermissionSet (Assembly a, string name) { FieldInfo fi = typeof (Assembly).GetField (name, BindingFlags.Instance | BindingFlags.NonPublic); if (fi == null) throw new NotSupportedException ("Wrong runtime ?"); return (PermissionSet) fi.GetValue (a); } #endif static bool ProcessAssemblyOnly (TextWriter tw, Assembly a) { #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) { if (attr is SecurityAttribute) { SecurityAttribute sa = (attr as SecurityAttribute); switch (sa.Action) { case SecurityAction.RequestMinimum: ShowPermissionSet (tw, "Minimum Permission Set:", GetPermissionSet (sa)); break; case SecurityAction.RequestOptional: ShowPermissionSet (tw, "Optional Permission Set:", GetPermissionSet (sa)); break; case SecurityAction.RequestRefuse: ShowPermissionSet (tw, "Refused Permission Set:", GetPermissionSet (sa)); break; default: // Bug in VS.NET 2005 Nov CTP - Every action is a LinkDemand :( string msg = String.Format ("ERROR {0} Permission Set:", sa.Action); ShowPermissionSet (tw, msg, GetPermissionSet (sa)); break; } } } #else // Note: This will only work using the Mono runtime as we P/Invoke // into Mono's corlib to get the required informations. Type t = typeof (Assembly); // 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); 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; } /* 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) { if (method_getdeclsec == null) { Type t = typeof (Int32).Assembly.GetType ("System.Reflection.MonoMethod"); method_getdeclsec = t.GetMethod ("GetDeclarativeSecurity", BindingFlags.Instance | BindingFlags.Public); } return (PermissionSet) method_getdeclsec.Invoke (mi, new object [1] { action }); } */ static void ProcessMethod (TextWriter tw, MethodInfo mi) { // no need to process methods without security informations if ((mi.Attributes & MethodAttributes.HasSecurity) == MethodAttributes.HasSecurity) { #if NET_2_0 object[] attrs = mi.GetCustomAttributes (false); foreach (object attr in attrs) { if (attr is SecurityAttribute) { SecurityAttribute sa = (attr as SecurityAttribute); tw.WriteLine ("Method {0} {1} Permission Set", mi, sa.Action); ShowPermissionSet (tw, null, GetPermissionSet (sa)); } } #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 } } static BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.GetProperty | BindingFlags.SetProperty; static void ProcessType (TextWriter tw, Type t) { // no need to process types without security informations if ((t.Attributes & TypeAttributes.HasSecurity) == TypeAttributes.HasSecurity) { #if NET_2_0 object[] attrs = t.GetCustomAttributes (false); foreach (object attr in attrs) { if (attr is SecurityAttribute) { SecurityAttribute sa = (attr as SecurityAttribute); tw.WriteLine ("Class {0} {1} Permission Set", t, sa.Action); ShowPermissionSet (tw, null, GetPermissionSet (sa)); } } #else tw.WriteLine ("Class {0} 'SecurityAction' Permission Set", t); // SecurityAction ShowPermissionSet (tw, null, null); #endif } } static bool ProcessAssemblyComplete (TextWriter tw, Assembly a) { #if NET_2_0 string header = "Assembly {0} Permission Set"; object [] attrs = a.GetCustomAttributes (false); foreach (object attr in attrs) { if (attr is SecurityAttribute) { SecurityAttribute sa = (attr as SecurityAttribute); // Bug in VS.NET 2005 Nov CTP - Evrything action is a LinkDemand ShowPermissionSet (tw, String.Format (header, sa.Action.ToString ()), GetPermissionSet (sa)); } } #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); 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 /* Type [] types = a.GetTypes (); foreach (Type type in types) { ProcessType (tw, type); foreach (MethodInfo mi in type.GetMethods (flags)) { ProcessMethod (tw, mi); } } return true;*/ } static TextWriter ProcessOptions (string[] args) { TextWriter tw = Console.Out; for (int i=0; i < args.Length - 1; i++) { switch (args [i].ToUpper ()) { case "/DECL": case "-DECL": case "--DECL": declarative = true; break; case "/OUTPUT": case "-OUTPUT": case "--OUTPUT": tw = (TextWriter) new StreamWriter (args [++i]); break; case "/HELP": case "/H": case "-HELP": case "-H": case "--HELP": case "--H": case "-?": case "--?": Help (); return null; } } return tw; } [STAThread] static int Main (string[] args) { try { Console.WriteLine (new AssemblyInfo ().ToString ()); if (args.Length == 0) { Help (); return 0; } TextWriter tw = ProcessOptions (args); if (tw == null) 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; } } else { Console.Error.WriteLine ("Couldn't load assembly '{0}'.", assemblyName); return 2; } tw.Close (); } catch (Exception e) { Console.Error.WriteLine ("Error: " + e.ToString ()); Help (); return 3; } return 0; } } }