4567c8b35b679b60e4d9d996f8a06d655adc5289
[mono.git] / mcs / class / corlib / Mono.Security / StrongNameManager.cs
1 //
2 // StrongNameManager.cs - StrongName Management
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // (C) 2004 Novell (http://www.novell.com)
8 //
9
10 //
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Collections;
35 using System.Globalization;
36 using System.IO;
37 using System.Reflection;
38 using System.Security;
39 using System.Security.Cryptography;
40 using System.Text;
41
42 using Mono.Security.Cryptography;
43 using Mono.Xml;
44
45 namespace Mono.Security {
46
47         /* RUNTIME
48          *                              yes
49          *      in_gac ---------------------------------\
50          *              |                               |
51          *              | no                            \/
52          *              |                       return true
53          * CLASS LIBRARY|
54          *              |
55          *              |
56          *              |                               
57          *      bool StrongNameManager.MustVerify
58          *              |
59          *              |
60          *              \/              not found       
61          *              Token --------------------------\
62          *              |                               |
63          *              | present ?                     |
64          *              |                               |
65          *              \/              not found       |
66          *      Assembly Name --------------------------|
67          *              |                               |
68          *              | present ?                     |
69          *              | or "*"                        |
70          *              \/              not found       |
71          *              User ---------------------------|
72          *              |                               |
73          *              | present ?                     |
74          *              | or "*"                        |
75          *              \/                              \/
76          *      return false                    return true
77          *      SKIP VERIFICATION               VERIFY ASSEMBLY
78          */
79
80         internal class StrongNameManager {
81
82                 private class Element {
83                         internal Hashtable assemblies;
84
85                         public Element () 
86                         {
87                                 assemblies = new Hashtable ();
88                         }
89
90                         public Element (string assembly, string users) : this ()
91                         {
92                                 assemblies.Add (assembly, users);
93                         }
94
95                         public string GetUsers (string assembly) 
96                         {
97                                 return (string) assemblies [assembly];
98                         }
99                 }
100
101                 static private Hashtable mappings;
102                 static private Hashtable tokens;
103
104                 static StrongNameManager () 
105                 {
106                 }
107
108                 // note: more than one configuration file can be loaded at the 
109                 // same time (e.g. user specific and machine specific config).
110                 static public void LoadConfig (string filename) 
111                 {
112                         if (File.Exists (filename)) {
113                                 SecurityParser sp = new SecurityParser ();
114                                 using (StreamReader sr = new StreamReader (filename)) {
115                                         string xml = sr.ReadToEnd ();
116                                         sp.LoadXml (xml);
117                                 }
118                                 SecurityElement root = sp.ToXml ();
119                                 if ((root != null) && (root.Tag == "configuration")) {
120                                         SecurityElement strongnames  = root.SearchForChildByTag ("strongNames");
121                                         if ((strongnames != null) && (strongnames.Children.Count > 0)) {
122                                                 SecurityElement mapping  = strongnames.SearchForChildByTag ("pubTokenMapping");
123                                                 if ((mapping != null) && (mapping.Children.Count > 0)) {
124                                                         LoadMapping (mapping);
125                                                 }
126
127                                                 SecurityElement settings = strongnames.SearchForChildByTag ("verificationSettings");
128                                                 if ((settings != null) && (settings.Children.Count > 0)) {
129                                                         LoadVerificationSettings (settings);
130                                                 }
131                                         }
132                                 }
133                         }
134                 }
135
136                 static private void LoadMapping (SecurityElement mapping) 
137                 {
138                         if (mappings == null) {
139                                 mappings = new Hashtable ();
140                         }
141
142                         lock (mappings.SyncRoot) {
143                                 foreach (SecurityElement item in mapping.Children) {
144                                         if (item.Tag != "map")
145                                                 continue;
146
147                                         string token = item.Attribute ("Token");
148                                         if ((token == null) || (token.Length != 16))
149                                                 continue; // invalid entry
150                                         token = token.ToUpper (CultureInfo.InvariantCulture);
151
152                                         string publicKey = item.Attribute ("PublicKey");
153                                         if (publicKey == null)
154                                                 continue; // invalid entry
155                                 
156                                         // watch for duplicate entries
157                                         if (mappings [token] == null) {
158                                                 mappings.Add (token, publicKey);
159                                         }
160                                         else {
161                                                 // replace existing mapping
162                                                 mappings [token] = publicKey;
163                                         }
164                                 }
165                         }
166                 }
167
168                 static private void LoadVerificationSettings (SecurityElement settings) 
169                 {
170                         if (tokens == null) {
171                                 tokens = new Hashtable ();
172                         }
173
174                         lock (tokens.SyncRoot) {
175                                 foreach (SecurityElement item in settings.Children) {
176                                         if (item.Tag != "skip")
177                                                 continue;
178
179                                         string token = item.Attribute ("Token");
180                                         if (token == null)
181                                                 continue;       // bad entry
182                                         token = token.ToUpper (CultureInfo.InvariantCulture);
183
184                                         string assembly = item.Attribute ("Assembly");
185                                         if (assembly == null)
186                                                 assembly = "*";
187
188                                         string users = item.Attribute ("Users");
189                                         if (users == null)
190                                                 users = "*";
191
192                                         Element el = (Element) tokens [token];
193                                         if (el == null) {
194                                                 // new token
195                                                 el = new Element (assembly, users);
196                                                 tokens.Add (token, el);
197                                                 continue;
198                                         }
199
200                                         // existing token
201                                         string a = (string) el.assemblies [assembly];
202                                         if (a == null) {
203                                                 // new assembly
204                                                 el.assemblies.Add (assembly, users);
205                                                 continue;
206                                         }
207
208                                         // existing assembly
209                                         if (users == "*") {
210                                                 // all users (drop current users)
211                                                 el.assemblies [assembly] = "*";
212                                                 continue;
213                                         }
214
215                                         // new users, add to existing
216                                         string existing = (string) el.assemblies [assembly];
217                                         string newusers = String.Concat (existing, ",", users);
218                                         el.assemblies [assembly] = newusers;
219                                 }
220                         }
221                 }
222
223                 static public byte[] GetMappedPublicKey (byte[] token) 
224                 {
225                         if ((mappings == null) || (token == null))
226                                 return null;
227
228                         string t = CryptoConvert.ToHex (token);
229                         string pk = (string) mappings [t];
230                         if (pk == null)
231                                 return null;
232
233                         return CryptoConvert.FromHex (pk);
234                 }
235
236                 // it is possible to skip verification for assemblies 
237                 // or a strongname public key using the "sn" tool.
238                 // note: only the runtime checks if the assembly is loaded 
239                 // from the GAC to skip verification
240                 static public bool MustVerify (AssemblyName an)
241                 {
242                         if ((an == null) || (tokens == null))
243                                 return true;
244
245                         string token = CryptoConvert.ToHex (an.GetPublicKeyToken ());
246                         Element el = (Element) tokens [token];
247                         if (el != null) {
248                                 // look for this specific assembly first
249                                 string users = el.GetUsers (an.Name);
250                                 if (users == null) {
251                                         // nothing for the specific assembly
252                                         // so look for "*" assembly
253                                         users = el.GetUsers ("*");
254                                 }
255
256                                 if (users != null) {
257                                         // applicable to any user ?
258                                         if (users == "*")
259                                                 return false;
260                                         // applicable to the current user ?
261                                         return (users.IndexOf (Environment.UserName) < 0);
262                                 }
263                         }
264
265                         // we must check verify the strongname on the assembly
266                         return true;
267                 }
268
269                 public override string ToString () 
270                 {
271                         StringBuilder sb = new StringBuilder ();
272                         sb.Append ("Public Key Token\tAssemblies\t\tUsers");
273                         sb.Append (Environment.NewLine);
274                         foreach (DictionaryEntry token in tokens) {
275                                 sb.Append ((string)token.Key);
276                                 Element t = (Element) token.Value;
277                                 bool first = true;
278                                 foreach (DictionaryEntry assembly in t.assemblies) {
279                                         if (first) {
280                                                 sb.Append ("\t");
281                                                 first = false;
282                                         }
283                                         else {
284                                                 sb.Append ("\t\t\t");
285                                         }
286                                         sb.Append ((string)assembly.Key);
287                                         sb.Append ("\t");
288                                         string users = (string)assembly.Value;
289                                         if (users == "*")
290                                                 users = "All users";
291                                         sb.Append (users);
292                                         sb.Append (Environment.NewLine);
293                                 }
294                         }
295                         return sb.ToString ();
296                 }
297         }
298 }