[xbuild] Update printed version/copyright
[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-2007 Novell, Inc (http://www.novell.com)
8 //
9
10 using System;
11 using System.Collections;
12 using System.Collections.Generic;
13 using System.IO;
14 using System.Reflection;
15 using System.Security;
16 using SSP = System.Security.Permissions;
17 using System.Text;
18
19 using Mono.Cecil;
20
21 [assembly: AssemblyTitle ("Mono PermView")]
22 [assembly: AssemblyDescription ("Managed Permission Viewer for .NET assemblies")]
23
24 namespace Mono.Tools {
25
26         static class SecurityDeclarationRocks {
27
28                 public static PermissionSet ToPermissionSet (this SecurityDeclaration self)
29                 {
30                         if (self == null)
31                                 throw new ArgumentNullException ("self");
32
33                         PermissionSet set;
34                         if (TryProcessPermissionSetAttribute (self, out set))
35                                 return set;
36
37                         return CreatePermissionSet (self);
38                 }
39
40                 static bool TryProcessPermissionSetAttribute (SecurityDeclaration declaration, out PermissionSet set)
41                 {
42                         set = null;
43
44                         if (!declaration.HasSecurityAttributes && declaration.SecurityAttributes.Count != 1)
45                                 return false;
46
47                         var security_attribute = declaration.SecurityAttributes [0];
48                         var attribute_type = security_attribute.AttributeType;
49
50                         if (attribute_type.Name != "PermissionSetAttribute" || attribute_type.Namespace != "System.Security.Permissions")
51                                 return false;
52
53                         var named_argument = security_attribute.Properties [0];
54                         if (named_argument.Name != "XML")
55                                 throw new NotSupportedException ();
56
57                         var attribute = new SSP.PermissionSetAttribute ((SSP.SecurityAction) declaration.Action);
58                         attribute.XML = (string) named_argument.Argument.Value;
59
60                         set = attribute.CreatePermissionSet ();
61                         return true;
62                 }
63
64                 static PermissionSet CreatePermissionSet (SecurityDeclaration declaration)
65                 {
66                         var set = new PermissionSet (SSP.PermissionState.None);
67
68                         foreach (var attribute in declaration.SecurityAttributes) {
69                                 var permission = CreatePermission (declaration, attribute);
70                                 set.AddPermission (permission);
71                         }
72
73                         return set;
74                 }
75
76                 static IPermission CreatePermission (SecurityDeclaration declaration, SecurityAttribute attribute)
77                 {
78                         var attribute_type = Type.GetType (attribute.AttributeType.FullName);
79                         if (attribute_type == null)
80                                 throw new ArgumentException ();
81
82                         var security_attribute = CreateSecurityAttribute (attribute_type, declaration);
83                         if (security_attribute == null)
84                                 throw new InvalidOperationException ();
85
86                         CompleteSecurityAttribute (security_attribute, attribute);
87
88                         return security_attribute.CreatePermission ();
89                 }
90
91                 static void CompleteSecurityAttribute (SSP.SecurityAttribute security_attribute, SecurityAttribute attribute)
92                 {
93                         if (attribute.HasFields)
94                                 CompleteSecurityAttributeFields (security_attribute, attribute);
95
96                         if (attribute.HasProperties)
97                                 CompleteSecurityAttributeProperties (security_attribute, attribute);
98                 }
99
100                 static void CompleteSecurityAttributeFields (SSP.SecurityAttribute security_attribute, SecurityAttribute attribute)
101                 {
102                         var type = security_attribute.GetType ();
103
104                         foreach (var named_argument in attribute.Fields)
105                                 type.GetField (named_argument.Name).SetValue (security_attribute, named_argument.Argument.Value);
106                 }
107
108                 static void CompleteSecurityAttributeProperties (SSP.SecurityAttribute security_attribute, SecurityAttribute attribute)
109                 {
110                         var type = security_attribute.GetType ();
111
112                         foreach (var named_argument in attribute.Properties)
113                                 type.GetProperty (named_argument.Name).SetValue (security_attribute, named_argument.Argument.Value, null);
114                 }
115
116                 static SSP.SecurityAttribute CreateSecurityAttribute (Type attribute_type, SecurityDeclaration declaration)
117                 {
118                         SSP.SecurityAttribute security_attribute;
119                         try {
120                                 security_attribute = (SSP.SecurityAttribute) Activator.CreateInstance (
121                                         attribute_type, new object [] { (SSP.SecurityAction) declaration.Action });
122                         } catch (MissingMethodException) {
123                                 security_attribute = (SSP.SecurityAttribute) Activator.CreateInstance (attribute_type, new object [0]);
124                         }
125
126                         return security_attribute;
127                 }
128         }
129
130         class SecurityElementComparer : IComparer {
131
132                 public int Compare (object x, object y)
133                 {
134                         SecurityElement sx = (x as SecurityElement);
135                         SecurityElement sy = (y as SecurityElement);
136                         if (sx == null)
137                                 return (sy == null) ? 0 : -1;
138                         else if (sy == null)
139                                 return 1;
140
141                         // compare by name (type name, method name, action name)
142                         return String.Compare (sx.Attribute ("Name"), sy.Attribute ("Name"));
143                 }
144         }
145
146         class PermView {
147
148                 private const string NotSpecified = "\tNot specified.";
149
150                 static private void Help () 
151                 {
152                         Console.WriteLine ("Usage: permview [options] assembly{0}", Environment.NewLine);
153                         Console.WriteLine ("where options are:");
154                         Console.WriteLine (" -output filename  Output information into specified file.");
155                         Console.WriteLine (" -decl             Show declarative security attributes on classes and methods.");
156                         Console.WriteLine (" -xml              Output in XML format");
157                         Console.WriteLine (" -help             Show help informations (this text)");
158                         Console.WriteLine ();
159                 }
160
161                 static bool declarative = false;
162                 static bool xmloutput = false;
163
164                 static TextWriter ProcessOptions (string[] args)
165                 {
166                         TextWriter tw = Console.Out;
167                         for (int i=0; i < args.Length - 1; i++) {
168                                 switch (args [i].ToUpper ()) {
169                                 case "/DECL":
170                                 case "-DECL":
171                                 case "--DECL":
172                                         declarative = true;
173                                         break;
174                                 case "/OUTPUT":
175                                 case "-OUTPUT":
176                                 case "--OUTPUT":
177                                         tw = (TextWriter) new StreamWriter (args [++i]);
178                                         break;
179                                 case "/XML":
180                                 case "-XML":
181                                 case "--XML":
182                                         xmloutput = true;
183                                         break;
184                                 case "/HELP":
185                                 case "/H":
186                                 case "-HELP":
187                                 case "-H":
188                                 case "--HELP":
189                                 case "--H":
190                                 case "-?":
191                                 case "--?":
192                                         Help ();
193                                         return null;
194                                 }
195                         }
196                         return tw;
197                 }
198
199                 static bool ProcessAssemblyOnly (TextWriter tw, AssemblyDefinition ad) 
200                 {
201                         bool result = true;
202                         string minimal = NotSpecified + Environment.NewLine;
203                         string optional = NotSpecified + Environment.NewLine;
204                         string refused = NotSpecified + Environment.NewLine;
205
206                         foreach (SecurityDeclaration decl in ad.SecurityDeclarations) {
207                                 switch (decl.Action) {
208                                 case Mono.Cecil.SecurityAction.RequestMinimum:
209                                         minimal = decl.ToPermissionSet ().ToString ();
210                                         break;
211                                 case Mono.Cecil.SecurityAction.RequestOptional:
212                                         optional = decl.ToPermissionSet ().ToString ();
213                                         break;
214                                 case Mono.Cecil.SecurityAction.RequestRefuse:
215                                         refused = decl.ToPermissionSet ().ToString ();
216                                         break;
217                                 default:
218                                         tw.WriteLine ("Invalid assembly level declaration {0}{1}{2}",
219                                                 decl.Action, Environment.NewLine, decl.ToPermissionSet ());
220                                         result = false;
221                                         break;
222                                 }
223                         }
224
225                         tw.WriteLine ("Minimal Permission Set:");
226                         tw.WriteLine (minimal);
227                         tw.WriteLine ("Optional Permission Set:");
228                         tw.WriteLine (optional);
229                         tw.WriteLine ("Refused Permission Set:");
230                         tw.WriteLine (refused);
231                         return result;
232                 }
233
234                 static void ShowSecurity (TextWriter tw, string header, IEnumerable<SecurityDeclaration> declarations)
235                 {
236                         foreach (SecurityDeclaration declsec in declarations) {
237                                 tw.WriteLine ("{0} {1} Permission Set:{2}{3}", header,
238                                         declsec.Action, Environment.NewLine, declsec.ToPermissionSet ());
239                         }
240                 }
241
242                 static bool ProcessAssemblyComplete (TextWriter tw, AssemblyDefinition ad)
243                 {
244                         if (ad.SecurityDeclarations.Count > 0) {
245                                 ShowSecurity (tw, "Assembly", ad.SecurityDeclarations);
246                         }
247
248                         foreach (ModuleDefinition module in ad.Modules) {
249
250                                 foreach (TypeDefinition type in module.Types) {
251
252                                         if (type.SecurityDeclarations.Count > 0) {
253                                                 ShowSecurity (tw, "Class " + type.ToString (), ad.SecurityDeclarations);
254                                         }
255
256                                         foreach (MethodDefinition method in type.Methods) {
257                                                 if (method.SecurityDeclarations.Count > 0) {
258                                                         ShowSecurity (tw, "Method " + method.ToString (), method.SecurityDeclarations);
259                                                 }
260                                         }
261                                 }
262                         }
263                         return true;
264                 }
265
266                 static void AddAttribute (SecurityElement se, string attr, string value)
267                 {
268                         value = value.Replace ("&", "&amp;");
269                         se.AddAttribute (attr, value);
270                 }
271
272                 static SecurityElement AddSecurityXml (IEnumerable<SecurityDeclaration> declarations)
273                 {
274                         ArrayList list = new ArrayList ();
275                         foreach (SecurityDeclaration declsec in declarations) {
276                                 SecurityElement child = new SecurityElement ("Action");
277                                 AddAttribute (child, "Name", declsec.Action.ToString ());
278                                 child.AddChild (declsec.ToPermissionSet ().ToXml ());
279                                 list.Add (child);
280                         }
281                         // sort actions
282                         list.Sort (Comparer);
283
284                         SecurityElement se = new SecurityElement ("Actions");
285                         foreach (SecurityElement child in list) {
286                                 se.AddChild (child);
287                         }
288                         return se;
289                 }
290
291                 static SecurityElementComparer comparer;
292                 static IComparer Comparer {
293                         get {
294                                 if (comparer == null)
295                                         comparer = new SecurityElementComparer ();
296                                 return comparer;
297                         }
298                 }
299
300                 static bool ProcessAssemblyXml (TextWriter tw, AssemblyDefinition ad)
301                 {
302                         SecurityElement se = new SecurityElement ("Assembly");
303                         se.AddAttribute ("Name", ad.Name.FullName);
304
305                         if (ad.SecurityDeclarations.Count > 0) {
306                                 se.AddChild (AddSecurityXml (ad.SecurityDeclarations));
307                         }
308
309                         ArrayList tlist = new ArrayList ();
310                         ArrayList mlist = new ArrayList ();
311
312                         foreach (ModuleDefinition module in ad.Modules) {
313
314                                 foreach (TypeDefinition type in module.Types) {
315
316                                         SecurityElement klass = new SecurityElement ("Class");
317                                         SecurityElement methods = new SecurityElement ("Methods");
318
319                                         SecurityElement typelem = null;
320                                         if (type.SecurityDeclarations.Count > 0) {
321                                                 typelem = AddSecurityXml (type.SecurityDeclarations);
322                                         }
323
324                                         if (mlist.Count > 0)
325                                                 mlist.Clear ();
326
327                                         foreach (MethodDefinition method in type.Methods) {
328                                                 if (method.SecurityDeclarations.Count > 0) {
329                                                         SecurityElement meth = new SecurityElement ("Method");
330                                                         AddAttribute (meth, "Name", method.ToString ());
331                                                         meth.AddChild (AddSecurityXml (method.SecurityDeclarations));
332                                                         mlist.Add (meth);
333                                                 }
334                                         }
335
336                                         // sort methods
337                                         mlist.Sort (Comparer);
338                                         foreach (SecurityElement method in mlist) {
339                                                 methods.AddChild (method);
340                                         }
341
342                                         if ((typelem != null) || ((methods.Children != null) && (methods.Children.Count > 0))) {
343                                                 AddAttribute (klass, "Name", type.ToString ());
344                                                 if (typelem != null)
345                                                         klass.AddChild (typelem);
346                                                 if ((methods.Children != null) && (methods.Children.Count > 0))
347                                                         klass.AddChild (methods);
348                                                 tlist.Add (klass);
349                                         }
350                                 }
351
352                                 // sort types
353                                 tlist.Sort (Comparer);
354                                 foreach (SecurityElement type in tlist) {
355                                         se.AddChild (type);
356                                 }
357                         }
358
359                         tw.WriteLine (se.ToString ());
360                         return true;
361                 }
362
363                 [STAThread]
364                 static int Main (string[] args) 
365                 {
366                         try {
367                                 Console.WriteLine (new AssemblyInfo ().ToString ());
368                                 if (args.Length == 0) {
369                                         Help ();
370                                         return 0;
371                                 }
372
373                                 TextWriter tw = ProcessOptions (args);
374                                 if (tw == null)
375                                         return 0;
376
377                                 string assemblyName = args [args.Length - 1];
378                                 AssemblyDefinition ad = AssemblyDefinition.ReadAssembly (assemblyName);
379                                 if (ad != null) {
380                                         bool complete = false;
381                                         
382                                         if (declarative) {
383                                                 // full output (assembly+classes+methods)
384                                                 complete = ProcessAssemblyComplete (tw, ad);
385                                         } else if (xmloutput) {
386                                                 // full output in XML (for easier diffs after c14n)
387                                                 complete = ProcessAssemblyXml (tw, ad);
388                                         } else {
389                                                 // default (assembly only)
390                                                 complete = ProcessAssemblyOnly (tw, ad);
391                                         }
392
393                                         if (!complete) {
394                                                 Console.Error.WriteLine ("Couldn't reflect informations.");
395                                                 return 1;
396                                         }
397                                 } else {
398                                         Console.Error.WriteLine ("Couldn't load assembly '{0}'.", assemblyName);
399                                         return 2;
400                                 }
401                                 tw.Close ();
402                         }
403                         catch (Exception e) {
404                                 Console.Error.WriteLine ("Error: " + e.ToString ());
405                                 Help ();
406                                 return 3;
407                         }
408                         return 0;
409                 }
410         }
411 }