2004-09-03 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / corlib / System.Security.Policy / PolicyLevel.cs
1 //
2 // System.Security.Policy.PolicyLevel.cs
3 //
4 // Authors:
5 //      Nick Drochak (ndrochak@gol.com)
6 //      Duncan Mak (duncan@ximian.com)
7 //      Sebastien Pouliot  <sebastien@ximian.com>
8 //
9 // (C) 2001 Nick Drochak
10 // (C) 2003 Duncan Mak, Ximian Inc.
11 // Portions (C) 2004 Motus Technologies Inc. (http://www.motus.com)
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System.Collections; // for IList
35 using System.Globalization;
36 using System.IO;
37 using System.Reflection;
38 using System.Security;
39 using System.Security.Permissions;
40
41 using Mono.Xml;
42
43 namespace System.Security.Policy {
44
45         [Serializable]
46         public sealed class PolicyLevel {
47
48                 string label;
49                 CodeGroup root_code_group;
50                 private ArrayList full_trust_assemblies;
51                 private ArrayList named_permission_sets;
52                 private string _location;
53                 private PolicyLevelType _type;
54                 private Hashtable fullNames;
55
56                 internal PolicyLevel (string label, PolicyLevelType type)
57                 {
58                         this.label = label;
59                         _type = type;
60                         full_trust_assemblies = new ArrayList ();
61                         named_permission_sets = new ArrayList ();
62                 }
63
64                 internal PolicyLevel (string label, PolicyLevelType type, string filename)
65                         : this (label, type)
66                 {
67                         LoadFromFile (filename);
68                 }
69
70                 internal void LoadFromFile (string filename) 
71                 {
72                         bool loaded = false;
73                         try {
74                                 // check for policy file
75                                 if (!File.Exists (filename)) {
76                                         // if it doesn't exist use the default configuration (like Fx 2.0)
77                                         // ref: http://blogs.msdn.com/shawnfa/archive/2004/04/21/117833.aspx
78                                         string defcfg = filename + ".default";
79                                         if (File.Exists (defcfg)) {
80                                                 // create policy from default file
81                                                 File.Copy (defcfg, filename);
82                                         }
83                                 }
84                                 // load security policy configuration
85                                 if (File.Exists (filename)) {
86                                         using (StreamReader sr = File.OpenText (filename)) {
87                                                 LoadFromString (sr.ReadToEnd ());
88                                         }
89                                         loaded = true;
90                                 }
91                                 else {
92                                         CreateFromHardcodedDefault (_type);
93                                         loaded = true;
94                                         Save ();
95                                 }
96                         }
97 /*                      catch (Exception) {
98                                 // this can fail in many ways include
99                                 // * can't lookup policy (path discovery);
100                                 // * can't copy default file to policy
101                                 // * can't read policy file;
102                                 // * can't save hardcoded policy to filename
103                                 // * can't decode policy file
104                                 if (!loaded)
105                                         CreateFromHardcodedDefault (_type);
106                         }*/
107                         finally {
108                                 _location = filename;
109                         }
110                 }
111
112                 internal void LoadFromString (string xml) 
113                 {
114                         SecurityParser parser = new SecurityParser ();
115                         parser.LoadXml (xml);
116                         // configuration / mscorlib / security / policy / PolicyLevel
117                         SecurityElement configuration = parser.ToXml ();
118                         if (configuration.Tag != "configuration")
119                                 throw new ArgumentException (Locale.GetText ("missing <configuration> root element"));
120                         SecurityElement mscorlib = (SecurityElement) configuration.Children [0];
121                         if (mscorlib.Tag != "mscorlib")
122                                 throw new ArgumentException (Locale.GetText ("missing <mscorlib> tag"));
123                         SecurityElement security = (SecurityElement) mscorlib.Children [0];
124                         if (security.Tag != "security")
125                                 throw new ArgumentException (Locale.GetText ("missing <security> tag"));
126                         SecurityElement policy = (SecurityElement) security.Children [0];
127                         if (policy.Tag != "policy")
128                                 throw new ArgumentException (Locale.GetText ("missing <policy> tag"));
129                         SecurityElement policyLevel = (SecurityElement) policy.Children [0];
130                         FromXml (policyLevel);
131                 }
132
133                 // properties
134
135                 public IList FullTrustAssemblies
136                 {
137                         get { return full_trust_assemblies; }
138                 }
139
140                 public string Label {
141                         get { return label; }
142                 }
143
144                 public IList NamedPermissionSets {
145                         get { return named_permission_sets; }
146                 }
147
148                 public CodeGroup RootCodeGroup {
149                         get { return root_code_group; }
150                         set { 
151                                 if (value == null)
152                                         throw new ArgumentNullException ("value");
153                                 root_code_group = value; 
154                         }
155                 }
156
157                 public string StoreLocation {
158                         get { return _location; }
159                 }
160
161 #if NET_2_0
162                 public PolicyLevelType Type {
163                         get { return _type; }
164                 }
165 #endif
166
167                 // methods
168
169                 public void AddFullTrustAssembly (StrongName sn)
170                 {
171                         if (sn == null)
172                                 throw new ArgumentNullException ("sn");
173
174                         StrongNameMembershipCondition snMC = new StrongNameMembershipCondition(
175                                 sn.PublicKey, sn.Name, sn.Version);
176
177                         AddFullTrustAssembly (snMC);
178                 }
179
180                 public void AddFullTrustAssembly (StrongNameMembershipCondition snMC)
181                 {
182                         if (snMC == null)
183                                 throw new ArgumentNullException ("snMC");
184                         
185                         foreach (StrongNameMembershipCondition sn in full_trust_assemblies) {
186                                 if (sn.Equals (snMC)) {
187                                         throw new ArgumentException (Locale.GetText ("sn already has full trust."));
188                                 }
189                         }
190                         full_trust_assemblies.Add (snMC);
191                 }
192
193                 public void AddNamedPermissionSet (NamedPermissionSet permSet)
194                 {
195                         if (permSet == null)
196                                 throw new ArgumentNullException ("permSet");
197
198                         foreach (NamedPermissionSet n in named_permission_sets) {
199                                 if (permSet.Name == n.Name) {
200                                         throw new ArgumentException (
201                                                 Locale.GetText ("This NamedPermissionSet is the same an existing NamedPermissionSet."));
202                                 }
203                         }
204                         named_permission_sets.Add (permSet.Copy ());
205                 }
206
207                 public NamedPermissionSet ChangeNamedPermissionSet (string name, PermissionSet pSet)
208                 {
209                         if (name == null)
210                                 throw new ArgumentNullException ("name");
211                         if (pSet == null)
212                                 throw new ArgumentNullException ("pSet");
213                         if (IsReserved (name))
214                                 throw new ArgumentException (Locale.GetText ("Reserved name"));
215
216                         foreach (NamedPermissionSet n in named_permission_sets) {
217                                 if (name == n.Name) {
218                                         named_permission_sets.Remove (n);
219                                         AddNamedPermissionSet (new NamedPermissionSet (name, pSet));
220                                         return n;
221                                 }
222                         }
223                         throw new ArgumentException (Locale.GetText ("PermissionSet not found"));
224                 }
225
226                 public static PolicyLevel CreateAppDomainLevel ()
227                 {
228                         NamedPermissionSet fullTrust = new NamedPermissionSet ("FullTrust", PermissionState.Unrestricted);
229                         UnionCodeGroup cg = new UnionCodeGroup (new AllMembershipCondition (), new PolicyStatement (fullTrust));
230                         cg.Name = "All_Code";
231                         PolicyLevel pl = new PolicyLevel ("AppDomain", PolicyLevelType.AppDomain);
232                         pl.RootCodeGroup = cg;
233                         pl.Reset ();
234                         return pl;
235                 }
236
237                 public void FromXml (SecurityElement e)
238                 {
239                         if (e == null)
240                                 throw new ArgumentNullException ("e");
241 // MS doesn't throw an exception for this case
242 //                      if (e.Tag != "PolicyLevel")
243 //                              throw new ArgumentException (Locale.GetText ("Invalid XML"));
244
245                         SecurityElement sc = e.SearchForChildByTag ("SecurityClasses");
246                         if ((sc != null) && (sc.Children != null) && (sc.Children.Count > 0)) {
247                                 fullNames = new Hashtable (sc.Children.Count);
248                                 foreach (SecurityElement se in sc.Children) {
249                                         fullNames.Add (se.Attributes ["Name"], se.Attributes ["Description"]);
250                                 }
251                         }
252
253                         SecurityElement nps = e.SearchForChildByTag ("NamedPermissionSets");
254                         if ((nps != null) && (nps.Children != null) && (nps.Children.Count > 0)) {
255                                 named_permission_sets.Clear ();
256                                 foreach (SecurityElement se in nps.Children) {
257                                         NamedPermissionSet n = new NamedPermissionSet ();
258                                         n.Resolver = this;
259                                         n.FromXml (se);
260                                         named_permission_sets.Add (n);
261                                 }
262                         }
263
264                         SecurityElement cg = e.SearchForChildByTag ("CodeGroup");
265                         if ((cg != null) && (cg.Children != null) && (cg.Children.Count > 0)) {
266                                 root_code_group = CodeGroup.CreateFromXml (cg, this);
267                         }
268                         else
269                                 throw new ArgumentException (Locale.GetText ("Missing Root CodeGroup"));
270
271                         SecurityElement fta = e.SearchForChildByTag ("FullTrustAssemblies");
272                         if ((fta != null) && (fta.Children != null) && (fta.Children.Count > 0)) {
273                                 full_trust_assemblies.Clear ();
274                                 foreach (SecurityElement se in fta.Children) {
275                                         if (se.Tag != "IMembershipCondition")
276                                                 throw new ArgumentException (Locale.GetText ("Invalid XML"));
277                                         string className = se.Attribute ("class");
278                                         if (className.IndexOf ("StrongNameMembershipCondition") < 0)
279                                                 throw new ArgumentException (Locale.GetText ("Invalid XML - must be StrongNameMembershipCondition"));
280                                         // we directly use StrongNameMembershipCondition
281                                         full_trust_assemblies.Add (new StrongNameMembershipCondition (se));
282                                 }
283                         }
284                 }
285
286                 public NamedPermissionSet GetNamedPermissionSet (string name)
287                 {
288                         if (name == null)
289                                 throw new ArgumentNullException ("name");
290
291                         foreach (NamedPermissionSet n in named_permission_sets) {
292                                 if (n.Name == name)
293                                         return (NamedPermissionSet) n.Copy ();
294                         }
295                         return null;
296                 }
297
298                 [MonoTODO]
299                 public void Recover ()
300                 {
301                         throw new NotImplementedException ();
302                 }
303
304                 public void RemoveFullTrustAssembly (StrongName sn)
305                 {
306                         if (sn == null)
307                                 throw new ArgumentNullException ("sn");
308
309                         StrongNameMembershipCondition s = new StrongNameMembershipCondition (sn.PublicKey, sn.Name, sn.Version);
310                         RemoveFullTrustAssembly (s);
311                 }
312
313                 public void RemoveFullTrustAssembly (StrongNameMembershipCondition snMC)
314                 {
315                         if (snMC == null)
316                                 throw new ArgumentNullException ("snMC");
317
318                         if (((IList) full_trust_assemblies).Contains (snMC))
319                                 ((IList) full_trust_assemblies).Remove (snMC);
320
321                         else
322                                 throw new ArgumentException (
323                                         Locale.GetText ("sn does not have full trust."));
324                 }
325
326                 public NamedPermissionSet RemoveNamedPermissionSet (NamedPermissionSet permSet)
327                 {
328                         if (permSet == null)
329                                 throw new ArgumentNullException ("permSet");
330
331                         if (! ((IList )named_permission_sets).Contains (permSet))
332                                 throw new ArgumentException (
333                                         Locale.GetText ("permSet cannot be found."));
334
335                         ((IList) named_permission_sets).Remove (permSet);
336
337                         return permSet;
338                 }
339
340                 [MonoTODO ("Check for reserved names")]
341                 public NamedPermissionSet RemoveNamedPermissionSet (string name)
342                 {
343                         if (name == null)
344                                 throw new ArgumentNullException ("name");
345
346                         foreach (NamedPermissionSet nps in named_permission_sets) {
347                                 if (name == nps.Name) {
348                                         named_permission_sets.Remove (nps);
349                                         return nps;
350                                 }
351                         }
352                         string msg = String.Format (Locale.GetText ("Name '{0}' cannot be found."), name);
353                         throw new ArgumentException (msg, "name");
354                 }
355
356                 public void Reset ()
357                 {
358                         if (fullNames != null)
359                                 fullNames.Clear ();
360                         full_trust_assemblies.Clear ();
361                         named_permission_sets.Clear ();
362
363                         if (_type != PolicyLevelType.AppDomain) {
364                                 // because the policy doesn't exist LoadFromFile will try to
365                                 // 1. use the .default file if existing (like Fx 2.0 does); or
366                                 // 2. use the hard-coded default values
367                                 // and recreate a policy file
368                                 if ((_location != null) && (File.Exists (_location))) {
369                                         try {
370                                                 File.Delete (_location);
371                                         }
372                                         catch {}
373                                 }
374                                 LoadFromFile (_location);
375                         }
376                         else {
377                                 named_permission_sets.Add (new NamedPermissionSet ("LocalIntranet"));
378                                 named_permission_sets.Add (new NamedPermissionSet ("Internet"));
379                                 named_permission_sets.Add (new NamedPermissionSet ("SkipVerification"));
380                                 named_permission_sets.Add (new NamedPermissionSet ("Execution"));
381                                 named_permission_sets.Add (new NamedPermissionSet ("Nothing"));
382                                 named_permission_sets.Add (new NamedPermissionSet ("Everything"));
383                                 named_permission_sets.Add (new NamedPermissionSet ("FullTrust"));
384                         }
385                 }
386
387                 public PolicyStatement Resolve (Evidence evidence)
388                 {
389                         if (evidence == null)
390                                 throw new ArgumentNullException ("evidence");
391
392                         PolicyStatement ps = root_code_group.Resolve (evidence);
393                         return ((ps != null) ? ps : PolicyStatement.Empty ());
394                 }
395
396                 public CodeGroup ResolveMatchingCodeGroups (Evidence evidence)
397                 {
398                         if (evidence == null)
399                                 throw new ArgumentNullException ("evidence");
400
401                         CodeGroup cg = root_code_group.ResolveMatchingCodeGroups (evidence);
402                         // TODO
403                         return ((cg != null) ? cg : null);
404
405                 }
406
407                 public SecurityElement ToXml ()
408                 {
409                         Hashtable fullNames = new Hashtable ();
410                         // only StrongNameMembershipCondition so no need to loop
411                         if (full_trust_assemblies.Count > 0) {
412                                 if (!fullNames.Contains ("StrongNameMembershipCondition")) {
413                                         fullNames.Add ("StrongNameMembershipCondition", typeof (StrongNameMembershipCondition).FullName);
414                                 }
415                         }
416                         
417                         SecurityElement namedPSs = new SecurityElement ("NamedPermissionSets");
418                         foreach (NamedPermissionSet nps in named_permission_sets) {
419                                 SecurityElement se = nps.ToXml ();
420                                 object objectClass = se.Attributes ["class"];
421                                 if (!fullNames.Contains (objectClass)) {
422                                         fullNames.Add (objectClass, nps.GetType ().FullName);
423                                 }
424                                 namedPSs.AddChild (se);
425                         }
426
427                         SecurityElement fta = new SecurityElement ("FullTrustAssemblies");
428                         foreach (StrongNameMembershipCondition snmc in full_trust_assemblies) {
429                                 fta.AddChild (snmc.ToXml (this));
430                         }
431
432                         SecurityElement security_classes = new SecurityElement ("SecurityClasses");
433                         if (fullNames.Count > 0) {
434                                 foreach (DictionaryEntry de in fullNames) {
435                                         SecurityElement sc = new SecurityElement ("SecurityClass");
436                                         sc.AddAttribute ("Name", (string)de.Key);
437                                         sc.AddAttribute ("Description", (string)de.Value);
438                                         security_classes.AddChild (sc);
439                                 }
440                         }
441
442                         SecurityElement element = new SecurityElement (typeof (System.Security.Policy.PolicyLevel).Name);
443                         element.AddAttribute ("version", "1");
444                         element.AddChild (security_classes);
445                         element.AddChild (namedPSs);
446                         if (root_code_group != null) {
447                                 element.AddChild (root_code_group.ToXml (this));
448                         }
449                         element.AddChild (fta);
450
451                         return element;
452                 }
453
454                 // internal stuff
455
456                 internal bool IsReserved (string name) 
457                 {
458                         switch (name) {
459                                 case "FullTrust":
460                                 case "LocalIntranet":
461                                 case "Internet":
462                                 case "SkipVerification":
463                                 case "Execution":
464                                 case "Nothing":
465                                 case "Everything":
466                                         // FIXME: Are there others ?
467                                         return true;
468                                 default:
469                                         return false;
470                         }
471                 }
472
473                 // NOTE: Callers are expected to check for ControlPolicy
474                 internal void Save ()
475                 {
476                         if (_type == PolicyLevelType.AppDomain) {
477                                 throw new PolicyException (Locale.GetText (
478                                         "Can't save AppDomain PolicyLevel"));
479                         }
480
481                         if (_location != null) {
482                                 using (StreamWriter sw = new StreamWriter (_location)) {
483                                         sw.Write (ToXml ().ToString ());
484                                         sw.Close ();
485                                 }
486                         }
487                 }
488
489                 // TODO : hardcode defaults in case 
490                 // (a) the specified policy file doesn't exists; and
491                 // (b) no corresponding default policy file exists
492                 internal void CreateFromHardcodedDefault (PolicyLevelType type) 
493                 {
494                         PolicyStatement psu = new PolicyStatement (new PermissionSet (PermissionState.Unrestricted));
495
496                         switch (type) {
497                         case PolicyLevelType.Machine:
498                                 // by default all stuff is in the machine policy...
499                                 root_code_group = new UnionCodeGroup (new ZoneMembershipCondition (SecurityZone.MyComputer), psu);
500                                 root_code_group.Name = "All_Code";
501                                 break;
502                         case PolicyLevelType.User:
503                         case PolicyLevelType.Enterprise:
504                         case PolicyLevelType.AppDomain:
505                                 // while the other policies don't restrict anything
506                                 root_code_group = new UnionCodeGroup (new AllMembershipCondition (), psu); 
507                                 root_code_group.Name = "All_Code";
508                                 break;
509                         }
510                 }
511
512                 internal string ResolveClassName (string className)
513                 {
514                         if (fullNames != null) {
515                                 object name = fullNames [className];
516                                 if (name != null)
517                                         return (string) name;
518                         }
519                         return className;
520                 }
521         }
522 }