svn path=/branches/mono-1-1-9/mcs/; revision=51206
[mono.git] / mcs / tools / security / permview.cs
1 //
2 // permview.cs: Managed Permission Viewer for .NET assemblies
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
8 //
9
10 using System;
11 using System.Collections;
12 using System.IO;
13 using System.Reflection;
14 using System.Security;
15 using System.Security.Permissions;
16
17 [assembly: AssemblyTitle ("Mono PermView")]
18 [assembly: AssemblyDescription ("Managed Permission Viewer for .NET assemblies")]
19
20 namespace Mono.Tools {
21
22         // There is no "managed way" to get this information using Fx 1.0/1.1
23         // so we must reflect inside Mono's corlib to find it. This also means
24         // that this won't work under MS runtime. Hopefully this will change
25         // with Fx 2.0 and Mono's PermView 2.0 should be working on both runtime.
26
27         // Notes:
28         // * Oct CTP started to return declarative security attributes with 
29         //   GetCustomAttributes, so this wont work with beta1 or previous 2.0 CTP
30         // * Dec/Nov CTP (and probably Oct CTP too) are bugged and always report 
31         //   LinkDemand as the SecurityAction (reported as FDBK19290)
32
33         class PermView {
34
35                 static private void Help () 
36                 {
37                         Console.WriteLine ("Usage: permview [options] assembly{0}", Environment.NewLine);
38                         Console.WriteLine ("where options are:");
39                         Console.WriteLine (" -output filename  Output information into specified file.");
40                         Console.WriteLine (" -decl             Show declarative security attributes on classes and methods.");
41                         Console.WriteLine (" -help             Show help informations (this text)");
42                         Console.WriteLine ();
43                 }
44
45                 static bool declarative = false;
46
47                 static void ShowPermissionSet (TextWriter tw, string header, PermissionSet ps)
48                 {
49                         if (header != null)
50                                 tw.WriteLine (header);
51
52                         if ((ps == null) || ((ps.Count == 0) && !ps.IsUnrestricted ())) {
53                                 tw.WriteLine ("\tNone");
54                         } else {
55                                 tw.WriteLine (ps.ToString ());
56                         }
57
58                         tw.WriteLine ();
59                 }
60
61 #if NET_2_0
62                 static PermissionSet GetPermissionSet (SecurityAttribute sa)
63                 {
64                         PermissionSet ps = null;
65                         if (sa is PermissionSetAttribute) {
66                                 ps = (sa as PermissionSetAttribute).CreatePermissionSet ();
67                         } else {
68                                 ps = new PermissionSet (PermissionState.None);
69                                 IPermission p = sa.CreatePermission ();
70                                 ps.AddPermission (p);
71                         }
72                         return ps;
73                 }
74 #else
75                 static PermissionSet GetPermissionSet (Assembly a, string name)
76                 {
77                         FieldInfo fi = typeof (Assembly).GetField (name, BindingFlags.Instance | BindingFlags.NonPublic);
78                         if (fi == null)
79                                 throw new NotSupportedException ("Wrong runtime ?");
80                         return (PermissionSet) fi.GetValue (a);
81                 }
82 #endif
83
84                 static bool ProcessAssemblyOnly (TextWriter tw, Assembly a) 
85                 {
86 #if NET_2_0
87                         // This should work for all 2.0 runtime - unless we hit a bug :-(
88                         object[] attrs = a.GetCustomAttributes (false);
89                         foreach (object attr in attrs) {\r
90                                 if (attr is SecurityAttribute) {
91                                         SecurityAttribute sa = (attr as SecurityAttribute);
92                                         switch (sa.Action) {
93                                         case SecurityAction.RequestMinimum:\r
94                                                 ShowPermissionSet (tw, "Minimum Permission Set:", GetPermissionSet (sa));\r
95                                                 break;\r
96                                         case SecurityAction.RequestOptional:\r
97                                                 ShowPermissionSet (tw, "Optional Permission Set:", GetPermissionSet (sa));\r
98                                                 break;\r
99                                         case SecurityAction.RequestRefuse:\r
100                                                 ShowPermissionSet (tw, "Refused Permission Set:", GetPermissionSet (sa));\r
101                                                 break;\r
102                                         default:\r
103                                                 // Bug in VS.NET 2005 Nov CTP - Every action is a LinkDemand :(\r
104                                                 string msg = String.Format ("ERROR {0} Permission Set:", sa.Action);\r
105                                                 ShowPermissionSet (tw, msg, GetPermissionSet (sa));\r
106                                                 break;
107                                         }
108                                 }
109                         }
110 #else
111                         // Note: This will only work using the Mono runtime as we P/Invoke
112                         // into Mono's corlib to get the required informations.
113
114                         Type t = typeof (Assembly);
115
116                         // Minimum, Optional and Refuse permission set are only evaluated
117                         // on demand (delayed as much as possible). A call Resolve will
118                         // trigger their retrieval from the assembly metadata
119                         MethodInfo resolve = t.GetMethod ("Resolve", BindingFlags.Instance | BindingFlags.NonPublic);
120                         if (resolve == null)
121                                 return false;
122                         resolve.Invoke (a, null);
123
124                         ShowPermissionSet (tw, "Minimal Permission Set:", GetPermissionSet (a, "_minimum"));
125                         ShowPermissionSet (tw, "Optional Permission Set:", GetPermissionSet (a, "_optional"));
126                         ShowPermissionSet (tw, "Refused Permission Set:", GetPermissionSet (a, "_refuse"));
127 #endif
128                         return true;
129                 }
130
131 /*              static SecurityAction[] actions = {
132                         SecurityAction.LinkDemand,
133                         SecurityAction.InheritanceDemand,
134                         SecurityAction.Demand,
135                         (SecurityAction) 13,                            // Hack for NonCasDemand
136                         (SecurityAction) 14,                            // Hack for NonCasLinkDemand
137                         (SecurityAction) 15,                            // Hack for NonCasInheritanceDemand
138                         SecurityAction.Assert,
139                         SecurityAction.Deny,
140                         SecurityAction.PermitOnly,
141                 };
142
143                 static MethodInfo method_getdeclsec;
144
145                 static PermissionSet GetDeclarativeSecurity (MethodInfo mi, SecurityAction action)
146                 {
147                         if (method_getdeclsec == null) {
148                                 Type t = typeof (Int32).Assembly.GetType ("System.Reflection.MonoMethod");
149                                 method_getdeclsec = t.GetMethod ("GetDeclarativeSecurity", BindingFlags.Instance | BindingFlags.Public);
150                         }
151                         return (PermissionSet) method_getdeclsec.Invoke (mi, new object [1] { action });
152                 }
153 */
154                 static void ProcessMethod (TextWriter tw, MethodInfo mi) 
155                 {\r
156                         // no need to process methods without security informations\r
157                         if ((mi.Attributes & MethodAttributes.HasSecurity) == MethodAttributes.HasSecurity) {
158 #if NET_2_0\r
159                                 object[] attrs = mi.GetCustomAttributes (false);\r
160                                 foreach (object attr in attrs) {\r
161                                         if (attr is SecurityAttribute) {\r
162                                                 SecurityAttribute sa = (attr as SecurityAttribute);\r
163                                                 tw.WriteLine ("Method {0} {1} Permission Set", mi, sa.Action);\r
164                                                 ShowPermissionSet (tw, null, GetPermissionSet (sa));\r
165                                         }\r
166                                 }
167 #else
168 /*                              foreach (SecurityAction action in actions) {
169                                         PermissionSet ps = GetDeclarativeSecurity (mi, action);
170                                         if (ps != null) {
171                                                 tw.WriteLine ("Method {0} {1} Permission Set", mi, action);
172                                                 ShowPermissionSet (tw, null, ps);
173                                         }
174                                 }*/
175 #endif\r
176                         }\r
177                 }
178
179                 static BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance |
180                         BindingFlags.Static | BindingFlags.GetProperty | BindingFlags.SetProperty;
181
182                 static void ProcessType (TextWriter tw, Type t) 
183                 {\r
184                         // no need to process types without security informations\r
185                         if ((t.Attributes & TypeAttributes.HasSecurity) == TypeAttributes.HasSecurity) {\r
186 #if NET_2_0\r
187                                 object[] attrs = t.GetCustomAttributes (false);
188                                 foreach (object attr in attrs) {\r
189                                         if (attr is SecurityAttribute) {
190                                                 SecurityAttribute sa = (attr as SecurityAttribute);\r
191                                                 tw.WriteLine ("Class {0} {1} Permission Set", t, sa.Action);\r
192                                                 ShowPermissionSet (tw, null, GetPermissionSet (sa));
193                                         }
194                                 }
195 #else
196                                 tw.WriteLine ("Class {0} 'SecurityAction' Permission Set", t);
197                                 // SecurityAction
198                                 ShowPermissionSet (tw, null, null);
199 #endif\r
200                         }\r
201                 }
202
203                 static bool ProcessAssemblyComplete (TextWriter tw, Assembly a) 
204                 {
205 #if NET_2_0\r
206                         string header = "Assembly {0} Permission Set";\r
207                         object [] attrs = a.GetCustomAttributes (false);\r
208                         foreach (object attr in attrs) {\r
209                                 if (attr is SecurityAttribute) {\r
210                                         SecurityAttribute sa = (attr as SecurityAttribute);\r
211                                         // Bug in VS.NET 2005 Nov CTP - Evrything action is a LinkDemand\r
212                                         ShowPermissionSet (tw, String.Format (header, sa.Action.ToString ()), GetPermissionSet (sa));\r
213                                 }\r
214                         }
215 #else
216                         tw.WriteLine ("Currently unsupported");
217                         return false;
218 /*                      Type t = typeof (Assembly);
219                         FieldInfo fi = t.GetField ("_minimum", BindingFlags.Instance | BindingFlags.NonPublic);
220                         if (fi == null)
221                                 return false;
222                         PermissionSet ps = (PermissionSet) fi.GetValue (a);
223                         if (ps != null)
224                                 ShowPermissionSet (tw, "Assembly RequestMinimum Permission Set:", ps);
225                         
226                         fi = t.GetField ("_optional", BindingFlags.Instance | BindingFlags.NonPublic);
227                         if (fi == null)
228                                 return false;
229                         ps = (PermissionSet) fi.GetValue (a);
230                         if (ps != null)
231                                 ShowPermissionSet (tw, "Assembly RequestOptional Permission Set:", ps);
232
233                         fi = t.GetField ("_refuse", BindingFlags.Instance | BindingFlags.NonPublic);
234                         if (fi == null)
235                                 return false;
236                         ps = (PermissionSet) fi.GetValue (a);
237                         if (ps != null)
238                                 ShowPermissionSet (tw, "Assembly RequestRefuse Permission Set:", ps);*/
239 #endif\r
240 /*                      Type [] types = a.GetTypes ();\r
241                         foreach (Type type in types) {\r
242                                 ProcessType (tw, type);\r
243                                 foreach (MethodInfo mi in type.GetMethods (flags)) {\r
244                                         ProcessMethod (tw, mi);\r
245                                 }\r
246                         }
247 \r
248                         return true;*/
249                 }
250
251                 static TextWriter ProcessOptions (string[] args)
252                 {
253                         TextWriter tw = Console.Out;
254                         for (int i=0; i < args.Length - 1; i++) {
255                                 switch (args [i].ToUpper ()) {
256                                 case "/DECL":
257                                 case "-DECL":
258                                 case "--DECL":
259                                         declarative = true;
260                                         break;
261                                 case "/OUTPUT":
262                                 case "-OUTPUT":
263                                 case "--OUTPUT":
264                                         tw = (TextWriter) new StreamWriter (args [++i]);
265                                         break;
266                                 case "/HELP":
267                                 case "/H":
268                                 case "-HELP":
269                                 case "-H":
270                                 case "--HELP":
271                                 case "--H":
272                                 case "-?":
273                                 case "--?":
274                                         Help ();
275                                         return null;
276                                 }
277                         }
278                         return tw;
279                 }
280
281                 [STAThread]
282                 static int Main (string[] args) 
283                 {
284                         try {
285                                 Console.WriteLine (new AssemblyInfo ().ToString ());
286                                 if (args.Length == 0) {
287                                         Help ();
288                                         return 0;
289                                 }
290
291                                 TextWriter tw = ProcessOptions (args);
292                                 if (tw == null)
293                                         return 0;
294
295                                 string assemblyName = args [args.Length - 1];
296                                 Assembly a = Assembly.LoadFile (Path.GetFullPath (assemblyName));
297                                 if (a != null) {
298                                         bool complete = (declarative ?
299                                                 ProcessAssemblyComplete (tw, a) :
300                                                 ProcessAssemblyOnly (tw, a));
301                                         if (!complete) {
302                                                 Console.Error.WriteLine ("Couldn't reflect informations. Wrong runtime ?");
303                                                 return 1;
304                                         }
305                                 } else {
306                                         Console.Error.WriteLine ("Couldn't load assembly '{0}'.", assemblyName);
307                                         return 2;
308                                 }
309                                 tw.Close ();
310                         }
311                         catch (Exception e) {
312                                 Console.Error.WriteLine ("Error: " + e.ToString ());
313                                 Help ();
314                                 return 3;
315                         }
316                         return 0;
317                 }
318         }
319 }