2009-05-28 Andrés G. Aragoneses <aaragoneses@novell.com>
[mono.git] / mcs / tools / tuner / Mono.Tuner / InjectSecurityAttributes.cs
1 //
2 // InjectSecurityAttributes.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@novell.com)
6 //
7 // (C) 2009 Novell, Inc.
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Collections;
31 using System.IO;
32 using System.Text;
33
34 using Mono.Linker;
35 using Mono.Linker.Steps;
36
37 using Mono.Cecil;
38
39 namespace Mono.Tuner {
40
41         public class InjectSecurityAttributes : BaseStep {
42
43                 enum TargetKind {
44                         Type,
45                         Method,
46                 }
47
48                 enum AttributeType {
49                         Critical,
50                         SafeCritical,
51                 }
52
53                 const string _safe_critical = "System.Security.SecuritySafeCriticalAttribute";
54                 const string _critical = "System.Security.SecurityCriticalAttribute";
55
56                 const string sec_attr_folder = "secattrs";
57
58                 AssemblyDefinition _assembly;
59
60                 MethodDefinition _safe_critical_ctor;
61                 MethodDefinition _critical_ctor;
62
63                 string data_folder;
64
65                 protected override bool ConditionToProcess ()
66                 {
67                         if (!Context.HasParameter (sec_attr_folder)) {
68                                 Console.Error.WriteLine ("Warning: no secattrs folder specified.");
69                                 return false;
70                         }
71
72                         data_folder = Context.GetParameter (sec_attr_folder);
73                         return true;
74                 }
75
76                 protected override void ProcessAssembly (AssemblyDefinition assembly)
77                 {
78                         if (Annotations.GetAction (assembly) != AssemblyAction.Link)
79                                 return;
80
81                         string secattr_file = Path.Combine (
82                                 data_folder,
83                                 assembly.Name.Name + ".secattr");
84
85                         if (!File.Exists (secattr_file)) {
86                                 Console.Error.WriteLine ("Warning: file '{0}' not found, skipping.", secattr_file);
87                                 return;
88                         }
89
90                         _assembly = assembly;
91
92                         // remove existing [SecurityCritical] and [SecuritySafeCritical]
93                         RemoveSecurityAttributes ();
94
95                         // add [SecurityCritical] and [SecuritySafeCritical] from the data file
96                         ProcessSecurityAttributeFile (secattr_file);
97                 }
98
99                 void RemoveSecurityAttributes ()
100                 {
101                         foreach (TypeDefinition type in _assembly.MainModule.Types) {
102                                 RemoveSecurityAttributes (type);
103
104                                 if (type.HasConstructors)
105                                         foreach (MethodDefinition ctor in type.Constructors)
106                                                 RemoveSecurityAttributes (ctor);
107
108                                 if (type.HasMethods)
109                                         foreach (MethodDefinition method in type.Methods)
110                                                 RemoveSecurityAttributes (method);
111                         }
112                 }
113
114                 static void RemoveSecurityDeclarations (IHasSecurity provider)
115                 {
116                         // also remove already existing CAS security declarations
117
118                         if (provider == null)
119                                 return;
120
121                         if (!provider.HasSecurityDeclarations)
122                                 return;
123
124                         provider.SecurityDeclarations.Clear ();
125                 }
126
127                 static void RemoveSecurityAttributes (ICustomAttributeProvider provider)
128                 {
129                         RemoveSecurityDeclarations (provider as IHasSecurity);
130
131                         if (!provider.HasCustomAttributes)
132                                 return;
133
134                         CustomAttributeCollection attributes = provider.CustomAttributes;
135                         for (int i = 0; i < attributes.Count; i++) {
136                                 CustomAttribute attribute = attributes [i];
137                                 switch (attribute.Constructor.DeclaringType.FullName) {
138                                 case _safe_critical:
139                                 case _critical:
140                                         attributes.RemoveAt (i--);
141                                         break;
142                                 }
143                         }
144                 }
145
146                 void ProcessSecurityAttributeFile (string file)
147                 {
148                         using (StreamReader reader = File.OpenText (file)) {
149                                 string line;
150                                 while ((line = reader.ReadLine ()) != null)
151                                         ProcessLine (line);
152                         }
153                 }
154
155                 void ProcessLine (string line)
156                 {
157                         if (line == null || line.Length < 6)
158                                 return;
159
160                         int sep = line.IndexOf (": ");
161                         if (sep == -1)
162                                 return;
163
164                         string marker = line.Substring (0, sep);
165                         string target = line.Substring (sep + 2);
166
167                         ProcessSecurityAttributeEntry (
168                                 DecomposeAttributeType (marker),
169                                 DecomposeTargetKind (marker),
170                                 target);
171                 }
172
173                 static AttributeType DecomposeAttributeType (string marker)
174                 {
175                         if (marker.StartsWith ("SC"))
176                                 return AttributeType.Critical;
177                         else if (marker.StartsWith ("SSC"))
178                                 return AttributeType.SafeCritical;
179                         else
180                                 throw new ArgumentException ();
181                 }
182
183                 static TargetKind DecomposeTargetKind (string marker)
184                 {
185                         switch (marker [marker.Length - 1]) {
186                         case 'T':
187                                 return TargetKind.Type;
188                         case 'M':
189                                 return TargetKind.Method;
190                         default:
191                                 throw new ArgumentException ();
192                         }
193                 }
194
195                 void ProcessSecurityAttributeEntry (AttributeType type, TargetKind kind, string target)
196                 {
197                         ICustomAttributeProvider provider = GetTarget (kind, target);
198                         if (provider == null)
199                                 return;
200
201                         switch (type) {
202                         case AttributeType.Critical:
203                                 AddCriticalAttribute (provider);
204                                 break;
205                         case AttributeType.SafeCritical:
206                                 AddSafeCriticalAttribute (provider);
207                                 break;
208                         }
209                 }
210
211                 void AddCriticalAttribute (ICustomAttributeProvider provider)
212                 {
213                         // a [SecurityCritical] replaces a [SecuritySafeCritical]
214                         if (HasSecurityAttribute (provider, AttributeType.SafeCritical))
215                                 RemoveSecurityAttributes (provider);
216
217                         AddSecurityAttribute (provider, AttributeType.Critical);
218                 }
219
220                 void AddSafeCriticalAttribute (ICustomAttributeProvider provider)
221                 {
222                         // a [SecuritySafeCritical] is ignored if a [SecurityCritical] is present
223                         if (HasSecurityAttribute (provider, AttributeType.Critical))
224                                 return;
225
226                         AddSecurityAttribute (provider, AttributeType.SafeCritical);
227                 }
228
229                 void AddSecurityAttribute (ICustomAttributeProvider provider, AttributeType type)
230                 {
231                         if (HasSecurityAttribute (provider, type))
232                                 return;
233
234                         CustomAttributeCollection attributes = provider.CustomAttributes;
235                         switch (type) {
236                         case AttributeType.Critical:
237                                 attributes.Add (CreateCriticalAttribute ());
238                                 break;
239                         case AttributeType.SafeCritical:
240                                 attributes.Add (CreateSafeCriticalAttribute ());
241                                 break;
242                         }
243                 }
244
245                 static bool HasSecurityAttribute (ICustomAttributeProvider provider, AttributeType type)
246                 {
247                         if (!provider.HasCustomAttributes)
248                                 return false;
249
250                         foreach (CustomAttribute attribute in provider.CustomAttributes) {
251                                 switch (attribute.Constructor.DeclaringType.Name) {
252                                 case _critical:
253                                         if (type == AttributeType.Critical)
254                                                 return true;
255
256                                         break;
257                                 case _safe_critical:
258                                         if (type == AttributeType.SafeCritical)
259                                                 return true;
260
261                                         break;
262                                 }
263                         }
264
265                         return false;
266                 }
267
268                 ICustomAttributeProvider GetTarget (TargetKind kind, string target)
269                 {
270                         switch (kind) {
271                         case TargetKind.Type:
272                                 return GetType (target);
273                         case TargetKind.Method:
274                                 return GetMethod (target);
275                         default:
276                                 throw new ArgumentException ();
277                         }
278                 }
279
280                 TypeDefinition GetType (string fullname)
281                 {
282                         return _assembly.MainModule.Types [fullname];
283                 }
284
285                 MethodDefinition GetMethod (string signature)
286                 {
287                         int pos = signature.IndexOf (" ");
288                         if (pos == -1)
289                                 throw new ArgumentException ();
290
291                         string tmp = signature.Substring (pos + 1);
292
293                         pos = tmp.IndexOf ("::");
294                         if (pos == -1)
295                                 throw new ArgumentException ();
296
297                         string type_name = tmp.Substring (0, pos);
298
299                         int parpos = tmp.IndexOf ("(");
300                         if (parpos == -1)
301                                 throw new ArgumentException ();
302
303                         string method_name = tmp.Substring (pos + 2, parpos - pos - 2);
304
305                         TypeDefinition type = GetType (type_name);
306                         if (type == null)
307                                 return null;
308
309                         return method_name.StartsWith (".c") ?
310                                 GetMethod (type.Constructors, signature) :
311                                 GetMethod (type.Methods, signature);
312                 }
313
314                 static MethodDefinition GetMethod (IEnumerable methods, string signature)
315                 {
316                         foreach (MethodDefinition method in methods)
317                                 if (GetFullName (method) == signature)
318                                         return method;
319
320                         return null;
321                 }
322
323                 static string GetFullName (MethodReference method)
324                 {
325                         int sentinel = method.GetSentinel ();
326
327                         StringBuilder sb = new StringBuilder ();
328                         sb.Append (method.ReturnType.ReturnType.FullName);
329                         sb.Append (" ");
330                         sb.Append (method.DeclaringType.FullName);
331                         sb.Append ("::");
332                         sb.Append (method.Name);
333                         if (method.HasGenericParameters) {
334                                 sb.Append ("<");
335                                 for (int i = 0; i < method.GenericParameters.Count; i++ ) {
336                                         if (i > 0)
337                                                 sb.Append (",");
338                                         sb.Append (method.GenericParameters [i].Name);
339                                 }
340                                 sb.Append (">");
341                         }
342                         sb.Append ("(");
343                         if (method.HasParameters) {
344                                 for (int i = 0; i < method.Parameters.Count; i++) {
345                                         if (i > 0)
346                                                 sb.Append (",");
347
348                                         if (i == sentinel)
349                                                 sb.Append ("...,");
350
351                                         sb.Append (method.Parameters [i].ParameterType.FullName);
352                                 }
353                         }
354                         sb.Append (")");
355                         return sb.ToString ();
356                 }
357
358                 static MethodDefinition GetDefaultConstructor (TypeDefinition type)
359                 {
360                         foreach (MethodDefinition ctor in type.Constructors)
361                                 if (ctor.Parameters.Count == 0)
362                                         return ctor;
363
364                         return null;
365                 }
366
367                 MethodDefinition GetSafeCriticalCtor ()
368                 {
369                         if (_safe_critical_ctor != null)
370                                 return _safe_critical_ctor;
371
372                         _safe_critical_ctor = GetDefaultConstructor (Context.GetType (_safe_critical));
373                         return _safe_critical_ctor;
374                 }
375
376                 MethodDefinition GetCriticalCtor ()
377                 {
378                         if (_critical_ctor != null)
379                                 return _critical_ctor;
380
381                         _critical_ctor = GetDefaultConstructor (Context.GetType (_critical));
382                         return _critical_ctor;
383                 }
384
385                 MethodReference Import (MethodDefinition method)
386                 {
387                         return _assembly.MainModule.Import (method);
388                 }
389
390                 CustomAttribute CreateSafeCriticalAttribute ()
391                 {
392                         return new CustomAttribute (Import (GetSafeCriticalCtor ()));
393                 }
394
395                 CustomAttribute CreateCriticalAttribute ()
396                 {
397                         return new CustomAttribute (Import (GetCriticalCtor ()));
398                 }
399         }
400 }