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 using System;
11 using System.Collections;
12 using System.Globalization;
13 using System.IO;
14 using System.Reflection;
15 using System.Security;
16 using System.Security.Cryptography;
17 using System.Text;
18 
19 using Mono.Security.Cryptography;
20 using Mono.Xml;
21 
22 namespace Mono.Security {
23 
24 	/* RUNTIME
25 	 *				yes
26 	 *	in_gac ---------------------------------\
27 	 *		|				|
28 	 *		| no				\/
29 	 *		|			return true
30 	 * CLASS LIBRARY|
31 	 *		|
32 	 * 		|
33 	 *		|
34 	 *	bool StrongNameManager.MustVerify
35 	 *		|
36 	 *		|
37 	 *		\/		not found
38 	 *		Token --------------------------\
39 	 *		|				|
40 	 *		| present ?			|
41 	 *		|				|
42 	 *		\/		not found	|
43 	 *	Assembly Name --------------------------|
44 	 *		|				|
45 	 *		| present ?			|
46 	 *		| or "*"			|
47 	 *		\/		not found	|
48 	 *		User ---------------------------|
49 	 *		|				|
50 	 *		| present ?			|
51 	 *		| or "*"			|
52 	 *		\/				\/
53 	 *	return false			return true
54 	 *	SKIP VERIFICATION		VERIFY ASSEMBLY
55 	 */
56 
57 	internal class StrongNameManager {
58 
59 		private class Element {
60 			internal Hashtable assemblies;
61 
Element()62 			public Element ()
63 			{
64 				assemblies = new Hashtable ();
65 			}
66 
Element(string assembly, string users)67 			public Element (string assembly, string users) : this ()
68 			{
69 				assemblies.Add (assembly, users);
70 			}
71 
GetUsers(string assembly)72 			public string GetUsers (string assembly)
73 			{
74 				return (string) assemblies [assembly];
75 			}
76 		}
77 
78 		static private Hashtable mappings;
79 		static private Hashtable tokens;
80 
StrongNameManager()81 		static StrongNameManager ()
82 		{
83 		}
84 
85 		// note: more than one configuration file can be loaded at the
86 		// same time (e.g. user specific and machine specific config).
LoadConfig(string filename)87 		static public void LoadConfig (string filename)
88 		{
89 			if (File.Exists (filename)) {
90 				SecurityParser sp = new SecurityParser ();
91 				using (StreamReader sr = new StreamReader (filename)) {
92 					string xml = sr.ReadToEnd ();
93 					sp.LoadXml (xml);
94 				}
95 				SecurityElement root = sp.ToXml ();
96 				if ((root != null) && (root.Tag == "configuration")) {
97 					SecurityElement strongnames  = root.SearchForChildByTag ("strongNames");
98 					if ((strongnames != null) && (strongnames.Children.Count > 0)) {
99 						SecurityElement mapping  = strongnames.SearchForChildByTag ("pubTokenMapping");
100 						if ((mapping != null) && (mapping.Children.Count > 0)) {
101 							LoadMapping (mapping);
102 						}
103 
104 						SecurityElement settings = strongnames.SearchForChildByTag ("verificationSettings");
105 						if ((settings != null) && (settings.Children.Count > 0)) {
106 							LoadVerificationSettings (settings);
107 						}
108 					}
109 				}
110 			}
111 		}
112 
LoadMapping(SecurityElement mapping)113 		static private void LoadMapping (SecurityElement mapping)
114 		{
115 			if (mappings == null) {
116 				mappings = new Hashtable ();
117 			}
118 
119 			lock (mappings.SyncRoot) {
120 				foreach (SecurityElement item in mapping.Children) {
121 					if (item.Tag != "map")
122 						continue;
123 
124 					string token = item.Attribute ("Token");
125 					if ((token == null) || (token.Length != 16))
126 						continue; // invalid entry
127 					token = token.ToUpper (CultureInfo.InvariantCulture);
128 
129 					string publicKey = item.Attribute ("PublicKey");
130 					if (publicKey == null)
131 						continue; // invalid entry
132 
133 					// watch for duplicate entries
134 					if (mappings [token] == null) {
135 						mappings.Add (token, publicKey);
136 					}
137 					else {
138 						// replace existing mapping
139 						mappings [token] = publicKey;
140 					}
141 				}
142 			}
143 		}
144 
LoadVerificationSettings(SecurityElement settings)145 		static private void LoadVerificationSettings (SecurityElement settings)
146 		{
147 			if (tokens == null) {
148 				tokens = new Hashtable ();
149 			}
150 
151 			lock (tokens.SyncRoot) {
152 				foreach (SecurityElement item in settings.Children) {
153 					if (item.Tag != "skip")
154 						continue;
155 
156 					string token = item.Attribute ("Token");
157 					if (token == null)
158 						continue;	// bad entry
159 					token = token.ToUpper (CultureInfo.InvariantCulture);
160 
161 					string assembly = item.Attribute ("Assembly");
162 					if (assembly == null)
163 						assembly = "*";
164 
165 					string users = item.Attribute ("Users");
166 					if (users == null)
167 						users = "*";
168 
169 					Element el = (Element) tokens [token];
170 					if (el == null) {
171 						// new token
172 						el = new Element (assembly, users);
173 						tokens.Add (token, el);
174 						continue;
175 					}
176 
177 					// existing token
178 					string a = (string) el.assemblies [assembly];
179 					if (a == null) {
180 						// new assembly
181 						el.assemblies.Add (assembly, users);
182 						continue;
183 					}
184 
185 					// existing assembly
186 					if (users == "*") {
187 						// all users (drop current users)
188 						el.assemblies [assembly] = "*";
189 						continue;
190 					}
191 
192 					// new users, add to existing
193 					string existing = (string) el.assemblies [assembly];
194 					string newusers = String.Concat (existing, ",", users);
195 					el.assemblies [assembly] = newusers;
196 				}
197 			}
198 		}
199 
GetMappedPublicKey(byte[] token)200 		static public byte[] GetMappedPublicKey (byte[] token)
201 		{
202 			if ((mappings == null) || (token == null))
203 				return null;
204 
205 			string t = CryptoConvert.ToHex (token);
206 			string pk = (string) mappings [t];
207 			if (pk == null)
208 				return null;
209 
210 			return CryptoConvert.FromHex (pk);
211 		}
212 
213 		// it is possible to skip verification for assemblies
214 		// or a strongname public key using the "sn" tool.
215 		// note: only the runtime checks if the assembly is loaded
216 		// from the GAC to skip verification
MustVerify(AssemblyName an)217 		static public bool MustVerify (AssemblyName an)
218 		{
219 			if ((an == null) || (tokens == null))
220 				return true;
221 
222 			string token = CryptoConvert.ToHex (an.GetPublicKeyToken ());
223 			Element el = (Element) tokens [token];
224 			if (el != null) {
225 				// look for this specific assembly first
226 				string users = el.GetUsers (an.Name);
227 				if (users == null) {
228 					// nothing for the specific assembly
229 					// so look for "*" assembly
230 					users = el.GetUsers ("*");
231 				}
232 
233 				if (users != null) {
234 					// applicable to any user ?
235 					if (users == "*")
236 						return false;
237 					// applicable to the current user ?
238 					return (users.IndexOf (Environment.UserName) < 0);
239 				}
240 			}
241 
242 			// we must check verify the strongname on the assembly
243 			return true;
244 		}
245 
ToString()246 		public override string ToString ()
247 		{
248 			StringBuilder sb = new StringBuilder ();
249 			sb.Append ("Public Key Token\tAssemblies\t\tUsers");
250 			sb.Append (Environment.NewLine);
251 			if (tokens == null) {
252 				sb.Append ("none");
253 				return sb.ToString ();
254 			}
255 
256 			foreach (DictionaryEntry token in tokens) {
257 				sb.Append ((string)token.Key);
258 				Element t = (Element) token.Value;
259 				bool first = true;
260 				foreach (DictionaryEntry assembly in t.assemblies) {
261 					if (first) {
262 						sb.Append ("\t");
263 						first = false;
264 					}
265 					else {
266 						sb.Append ("\t\t\t");
267 					}
268 					sb.Append ((string)assembly.Key);
269 					sb.Append ("\t");
270 					string users = (string)assembly.Value;
271 					if (users == "*")
272 						users = "All users";
273 					sb.Append (users);
274 					sb.Append (Environment.NewLine);
275 				}
276 			}
277 			return sb.ToString ();
278 		}
279 	}
280 }
281