2009-02-18 Jb Evain <jbevain@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
33 using Mono.Linker;
34 using Mono.Linker.Steps;
35
36 using Mono.Cecil;
37
38 namespace Mono.Tuner {
39
40         public class InjectSecurityAttributes : BaseStep {
41
42                 enum TargetKind {
43                         Type,
44                         Method,
45                 }
46
47                 enum AttributeType {
48                         Critical,
49                         SafeCritical,
50                 }
51
52                 const string _safe_critical = "System.Security.SecuritySafeCriticalAttribute";
53                 const string _critical = "System.Security.SecurityCriticalAttribute";
54
55                 const string sec_attr_folder = "secattrs";
56
57                 AssemblyDefinition _assembly;
58
59                 MethodDefinition _safe_critical_ctor;
60                 MethodDefinition _critical_ctor;
61
62                 string data_folder;
63
64                 protected override bool ConditionToProcess ()
65                 {
66                         if (!Context.HasParameter (sec_attr_folder))
67                                 return false;
68
69                         data_folder = Context.GetParameter (sec_attr_folder);
70                         return true;
71                 }
72
73                 protected override void ProcessAssembly (AssemblyDefinition assembly)
74                 {
75                         if (Annotations.GetAction (assembly) != AssemblyAction.Link)
76                                 return;
77
78                         string secattr_file = Path.Combine (
79                                 data_folder,
80                                 assembly.Name.Name + ".secattr");
81
82                         if (!File.Exists (secattr_file))
83                                 return;
84
85                         _assembly = assembly;
86
87                         // remove existing [SecurityCritical] and [SecuritySafeCritical]
88                         RemoveSecurityAttributes ();
89
90                         // add [SecurityCritical] and [SecuritySafeCritical] from the data file
91                         ProcessSecurityAttributeFile (secattr_file);
92                 }
93
94                 void RemoveSecurityAttributes ()
95                 {
96                         foreach (TypeDefinition type in _assembly.MainModule.Types) {
97                                 RemoveSecurityAttributes (type);
98
99                                 if (type.HasConstructors)
100                                         foreach (MethodDefinition ctor in type.Constructors)
101                                                 RemoveSecurityAttributes (ctor);
102
103                                 if (type.HasMethods)
104                                         foreach (MethodDefinition method in type.Methods)
105                                                 RemoveSecurityAttributes (method);
106                         }
107                 }
108
109                 static void RemoveSecurityAttributes (ICustomAttributeProvider provider)
110                 {
111                         if (!provider.HasCustomAttributes)
112                                 return;
113
114                         CustomAttributeCollection attributes = provider.CustomAttributes;
115                         for (int i = 0; i < attributes.Count; i++) {
116                                 CustomAttribute attribute = attributes [i];
117                                 switch (attribute.Constructor.DeclaringType.FullName) {
118                                 case _safe_critical:
119                                 case _critical:
120                                         attributes.RemoveAt (i--);
121                                         break;
122                                 }
123                         }
124                 }
125
126                 void ProcessSecurityAttributeFile (string file)
127                 {
128                         using (StreamReader reader = File.OpenText (file)) {
129                                 string line;
130                                 while ((line = reader.ReadLine ()) != null)
131                                         ProcessLine (line);
132                         }
133                 }
134
135                 void ProcessLine (string line)
136                 {
137                         if (line == null || line.Length < 6)
138                                 return;
139
140                         int sep = line.IndexOf (": ");
141                         if (sep == -1)
142                                 return;
143
144                         string marker = line.Substring (0, sep);
145                         string target = line.Substring (sep + 2);
146
147                         ProcessSecurityAttributeEntry (
148                                 DecomposeAttributeType (marker),
149                                 DecomposeTargetKind (marker),
150                                 target);
151                 }
152
153                 static AttributeType DecomposeAttributeType (string marker)
154                 {
155                         if (marker.StartsWith ("SC"))
156                                 return AttributeType.Critical;
157                         else if (marker.StartsWith ("SSC"))
158                                 return AttributeType.SafeCritical;
159                         else
160                                 throw new ArgumentException ();
161                 }
162
163                 static TargetKind DecomposeTargetKind (string marker)
164                 {
165                         switch (marker [marker.Length - 1]) {
166                         case 'T':
167                                 return TargetKind.Type;
168                         case 'M':
169                                 return TargetKind.Method;
170                         default:
171                                 throw new ArgumentException ();
172                         }
173                 }
174
175                 void ProcessSecurityAttributeEntry (AttributeType type, TargetKind kind, string target)
176                 {
177                         ICustomAttributeProvider provider = GetTarget (kind, target);
178                         if (provider == null)
179                                 return;
180
181                         switch (type) {
182                         case AttributeType.Critical:
183                                 AddCriticalAttribute (provider);
184                                 break;
185                         case AttributeType.SafeCritical:
186                                 AddSafeCriticalAttribute (provider);
187                                 break;
188                         }
189                 }
190
191                 void AddCriticalAttribute (ICustomAttributeProvider provider)
192                 {
193                         // a [SecurityCritical] replaces a [SecuritySafeCritical]
194                         if (HasSecurityAttribute (provider, AttributeType.SafeCritical))
195                                 RemoveSecurityAttributes (provider);
196
197                         AddSecurityAttribute (provider, AttributeType.Critical);
198                 }
199
200                 void AddSafeCriticalAttribute (ICustomAttributeProvider provider)
201                 {
202                         // a [SecuritySafeCritical] is ignored if a [SecurityCritical] is present
203                         if (HasSecurityAttribute (provider, AttributeType.Critical))
204                                 return;
205
206                         AddSecurityAttribute (provider, AttributeType.SafeCritical);
207                 }
208
209                 void AddSecurityAttribute (ICustomAttributeProvider provider, AttributeType type)
210                 {
211                         if (HasSecurityAttribute (provider, type))
212                                 return;
213
214                         CustomAttributeCollection attributes = provider.CustomAttributes;
215                         switch (type) {
216                         case AttributeType.Critical:
217                                 attributes.Add (CreateCriticalAttribute ());
218                                 break;
219                         case AttributeType.SafeCritical:
220                                 attributes.Add (CreateSafeCriticalAttribute ());
221                                 break;
222                         }
223                 }
224
225                 static bool HasSecurityAttribute (ICustomAttributeProvider provider, AttributeType type)
226                 {
227                         if (!provider.HasCustomAttributes)
228                                 return false;
229
230                         foreach (CustomAttribute attribute in provider.CustomAttributes) {
231                                 switch (attribute.Constructor.DeclaringType.Name) {
232                                 case _critical:
233                                         if (type == AttributeType.Critical)
234                                                 return true;
235
236                                         break;
237                                 case _safe_critical:
238                                         if (type == AttributeType.SafeCritical)
239                                                 return true;
240
241                                         break;
242                                 }
243                         }
244
245                         return false;
246                 }
247
248                 ICustomAttributeProvider GetTarget (TargetKind kind, string target)
249                 {
250                         switch (kind) {
251                         case TargetKind.Type:
252                                 return GetType (target);
253                         case TargetKind.Method:
254                                 return GetMethod (target);
255                         default:
256                                 throw new ArgumentException ();
257                         }
258                 }
259
260                 TypeDefinition GetType (string fullname)
261                 {
262                         return _assembly.MainModule.Types [fullname];
263                 }
264
265                 MethodDefinition GetMethod (string signature)
266                 {
267                         int pos = signature.IndexOf (" ");
268                         if (pos == -1)
269                                 throw new ArgumentException ();
270
271                         string tmp = signature.Substring (pos + 1);
272
273                         pos = tmp.IndexOf ("::");
274                         if (pos == -1)
275                                 throw new ArgumentException ();
276
277                         string type_name = tmp.Substring (0, pos);
278
279                         int parpos = tmp.IndexOf ("(");
280                         if (parpos == -1)
281                                 throw new ArgumentException ();
282
283                         string method_name = tmp.Substring (pos + 2, parpos - pos - 2);
284
285                         TypeDefinition type = GetType (type_name);
286                         if (type == null)
287                                 return null;
288
289                         return method_name.StartsWith (".c") ?
290                                 GetMethod (type.Constructors, signature) :
291                                 GetMethod (type.Methods.GetMethod (method_name), signature);
292                 }
293
294                 static MethodDefinition GetMethod (IEnumerable methods, string signature)
295                 {
296                         foreach (MethodDefinition method in methods)
297                                 if (method.ToString () == signature)
298                                         return method;
299
300                         return null;
301                 }
302
303                 static MethodDefinition GetDefaultConstructor (TypeDefinition type)
304                 {
305                         foreach (MethodDefinition ctor in type.Constructors)
306                                 if (ctor.Parameters.Count == 0)
307                                         return ctor;
308
309                         return null;
310                 }
311
312                 MethodDefinition GetSafeCriticalCtor ()
313                 {
314                         if (_safe_critical_ctor != null)
315                                 return _safe_critical_ctor;
316
317                         _safe_critical_ctor = GetDefaultConstructor (Context.GetType (_safe_critical));
318                         return _safe_critical_ctor;
319                 }
320
321                 MethodDefinition GetCriticalCtor ()
322                 {
323                         if (_critical_ctor != null)
324                                 return _critical_ctor;
325
326                         _critical_ctor = GetDefaultConstructor (Context.GetType (_critical));
327                         return _critical_ctor;
328                 }
329
330                 MethodReference Import (MethodDefinition method)
331                 {
332                         return _assembly.MainModule.Import (method);
333                 }
334
335                 CustomAttribute CreateSafeCriticalAttribute ()
336                 {
337                         return new CustomAttribute (Import (GetSafeCriticalCtor ()));
338                 }
339
340                 CustomAttribute CreateCriticalAttribute ()
341                 {
342                         return new CustomAttribute (Import (GetCriticalCtor ()));
343                 }
344         }
345 }