2005-06-01 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-2005 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.Runtime.InteropServices;
39 using System.Security.Permissions;
40
41 using Mono.Xml;
42
43 namespace System.Security.Policy {
44
45         [Serializable]
46 #if NET_2_0
47         [ComVisible (true)]
48 #endif
49         public sealed class PolicyLevel {
50
51                 string label;
52                 CodeGroup root_code_group;
53                 private ArrayList full_trust_assemblies;
54                 private ArrayList named_permission_sets;
55                 private string _location;
56                 private PolicyLevelType _type;
57                 private Hashtable fullNames;
58                 private bool loaded;
59                 private SecurityElement xml;
60
61                 internal PolicyLevel (string label, PolicyLevelType type)
62                 {
63                         this.label = label;
64                         _type = type;
65                         full_trust_assemblies = new ArrayList ();
66                         named_permission_sets = new ArrayList ();
67                 }
68
69                 internal void LoadFromFile (string filename)
70                 {
71                         try {
72                                 loaded = false;
73                                 // check for policy file
74                                 if (!File.Exists (filename)) {
75                                         // if it doesn't exist use the default configuration (like Fx 2.0)
76                                         // ref: http://blogs.msdn.com/shawnfa/archive/2004/04/21/117833.aspx
77                                         string defcfg = filename + ".default";
78                                         if (File.Exists (defcfg)) {
79                                                 // create policy from default file
80                                                 File.Copy (defcfg, filename);
81                                         }
82                                 }
83                                 // load security policy configuration
84                                 if (File.Exists (filename)) {
85                                         using (StreamReader sr = File.OpenText (filename)) {
86                                                 xml = FromString (sr.ReadToEnd ());
87                                                 FromXml1 (xml);
88                                         }
89                                         loaded = true;
90                                 } else {
91                                         CreateDefaultLevel (_type);
92                                 }
93                         }
94                         catch {
95                                 // this can fail in many ways including...
96                                 // * can't lookup policy (path discovery);
97                                 // * can't copy default file to policy
98                                 // * can't read policy file;
99                                 // * can't decode policy file
100                                 if (!loaded)
101                                         CreateDefaultLevel (_type);
102                         }
103                         finally {
104                                 _location = filename;
105                         }
106                 }
107
108                 internal void Initialize ()
109                 {
110                         if (loaded) {
111                                 FromXml2 (xml);
112                         } else {
113                                 CreateDefaultNamedPermissionSets ();
114                                 try {
115                                         Save ();
116                                 }
117                                 catch {
118                                         // this can fail in many ways including...
119                                         // * can't save hardcoded policy to filename
120                                 }
121                         }
122                 }
123
124                 internal void LoadFromString (string xml) 
125                 {
126                         FromXml (FromString (xml));
127                 }
128
129                 private SecurityElement FromString (string xml) 
130                 {
131                         SecurityParser parser = new SecurityParser ();
132                         parser.LoadXml (xml);
133                         // configuration / mscorlib / security / policy / PolicyLevel
134                         SecurityElement configuration = parser.ToXml ();
135                         if (configuration.Tag != "configuration")
136                                 throw new ArgumentException (Locale.GetText ("missing <configuration> root element"));
137                         SecurityElement mscorlib = (SecurityElement) configuration.Children [0];
138                         if (mscorlib.Tag != "mscorlib")
139                                 throw new ArgumentException (Locale.GetText ("missing <mscorlib> tag"));
140                         SecurityElement security = (SecurityElement) mscorlib.Children [0];
141                         if (security.Tag != "security")
142                                 throw new ArgumentException (Locale.GetText ("missing <security> tag"));
143                         SecurityElement policy = (SecurityElement) security.Children [0];
144                         if (policy.Tag != "policy")
145                                 throw new ArgumentException (Locale.GetText ("missing <policy> tag"));
146                         SecurityElement policyLevel = (SecurityElement) policy.Children [0];
147                         return policyLevel;
148                 }
149
150                 // properties
151
152                 public IList FullTrustAssemblies {
153                         get { return full_trust_assemblies; }
154                 }
155
156                 public string Label {
157                         get { return label; }
158                 }
159
160                 public IList NamedPermissionSets {
161                         get { return named_permission_sets; }
162                 }
163
164                 public CodeGroup RootCodeGroup {
165                         get { return root_code_group; }
166                         set { 
167                                 if (value == null)
168                                         throw new ArgumentNullException ("value");
169                                 root_code_group = value; 
170                         }
171                 }
172
173                 public string StoreLocation {
174                         get { return _location; }
175                 }
176
177 #if NET_2_0
178                 [ComVisible (false)]
179                 public PolicyLevelType Type {
180                         get { return _type; }
181                 }
182 #endif
183
184                 // methods
185
186                 public void AddFullTrustAssembly (StrongName sn)
187                 {
188                         if (sn == null)
189                                 throw new ArgumentNullException ("sn");
190
191                         StrongNameMembershipCondition snMC = new StrongNameMembershipCondition(
192                                 sn.PublicKey, sn.Name, sn.Version);
193
194                         AddFullTrustAssembly (snMC);
195                 }
196
197                 public void AddFullTrustAssembly (StrongNameMembershipCondition snMC)
198                 {
199                         if (snMC == null)
200                                 throw new ArgumentNullException ("snMC");
201                         
202                         foreach (StrongNameMembershipCondition sn in full_trust_assemblies) {
203                                 if (sn.Equals (snMC)) {
204                                         throw new ArgumentException (Locale.GetText ("sn already has full trust."));
205                                 }
206                         }
207                         full_trust_assemblies.Add (snMC);
208                 }
209
210                 public void AddNamedPermissionSet (NamedPermissionSet permSet)
211                 {
212                         if (permSet == null)
213                                 throw new ArgumentNullException ("permSet");
214
215                         foreach (NamedPermissionSet n in named_permission_sets) {
216                                 if (permSet.Name == n.Name) {
217                                         throw new ArgumentException (
218                                                 Locale.GetText ("This NamedPermissionSet is the same an existing NamedPermissionSet."));
219                                 }
220                         }
221                         named_permission_sets.Add (permSet.Copy ());
222                 }
223
224                 public NamedPermissionSet ChangeNamedPermissionSet (string name, PermissionSet pSet)
225                 {
226                         if (name == null)
227                                 throw new ArgumentNullException ("name");
228                         if (pSet == null)
229                                 throw new ArgumentNullException ("pSet");
230                         if (DefaultPolicies.ReservedNames.IsReserved (name))
231                                 throw new ArgumentException (Locale.GetText ("Reserved name"));
232
233                         foreach (NamedPermissionSet n in named_permission_sets) {
234                                 if (name == n.Name) {
235                                         named_permission_sets.Remove (n);
236                                         AddNamedPermissionSet (new NamedPermissionSet (name, pSet));
237                                         return n;
238                                 }
239                         }
240                         throw new ArgumentException (Locale.GetText ("PermissionSet not found"));
241                 }
242
243                 public static PolicyLevel CreateAppDomainLevel ()
244                 {
245                         NamedPermissionSet fullTrust = new NamedPermissionSet ("FullTrust", PermissionState.Unrestricted);
246                         UnionCodeGroup cg = new UnionCodeGroup (new AllMembershipCondition (), new PolicyStatement (fullTrust));
247                         cg.Name = "All_Code";
248                         PolicyLevel pl = new PolicyLevel ("AppDomain", PolicyLevelType.AppDomain);
249                         pl.RootCodeGroup = cg;
250                         pl.Reset ();
251                         return pl;
252                 }
253
254
255                 public void FromXml (SecurityElement e)
256                 {
257                         if (e == null)
258                                 throw new ArgumentNullException ("e");
259 // MS doesn't throw an exception for this case
260 //                      if (e.Tag != "PolicyLevel")
261 //                              throw new ArgumentException (Locale.GetText ("Invalid XML"));
262
263                         FromXml1 (e);
264                         FromXml2 (e);
265                 }
266
267                 internal void FromXml1 (SecurityElement e)
268                 {
269                         SecurityElement sc = e.SearchForChildByTag ("SecurityClasses");
270                         if ((sc != null) && (sc.Children != null) && (sc.Children.Count > 0)) {
271                                 fullNames = new Hashtable (sc.Children.Count);
272                                 foreach (SecurityElement se in sc.Children) {
273                                         fullNames.Add (se.Attributes ["Name"], se.Attributes ["Description"]);
274                                 }
275                         }
276
277                         SecurityElement fta = e.SearchForChildByTag ("FullTrustAssemblies");
278                         if ((fta != null) && (fta.Children != null) && (fta.Children.Count > 0)) {
279                                 full_trust_assemblies.Clear ();
280                                 foreach (SecurityElement se in fta.Children) {
281                                         if (se.Tag != "IMembershipCondition")
282                                                 throw new ArgumentException (Locale.GetText ("Invalid XML"));
283                                         string className = se.Attribute ("class");
284                                         if (className.IndexOf ("StrongNameMembershipCondition") < 0)
285                                                 throw new ArgumentException (Locale.GetText ("Invalid XML - must be StrongNameMembershipCondition"));
286                                         // we directly use StrongNameMembershipCondition
287                                         full_trust_assemblies.Add (new StrongNameMembershipCondition (se));
288                                 }
289                         }
290
291                         SecurityElement cg = e.SearchForChildByTag ("CodeGroup");
292                         if ((cg != null) && (cg.Children != null) && (cg.Children.Count > 0)) {
293                                 root_code_group = CodeGroup.CreateFromXml (cg, this);
294                         }
295                         else
296                                 throw new ArgumentException (Locale.GetText ("Missing Root CodeGroup"));
297                 }
298
299                 internal void FromXml2 (SecurityElement e)
300                 {
301                         SecurityElement nps = e.SearchForChildByTag ("NamedPermissionSets");
302                         if ((nps != null) && (nps.Children != null) && (nps.Children.Count > 0)) {
303                                 named_permission_sets.Clear ();
304                                 foreach (SecurityElement se in nps.Children) {
305                                         NamedPermissionSet n = new NamedPermissionSet ();
306                                         n.Resolver = this;
307                                         n.FromXml (se);
308                                         named_permission_sets.Add (n);
309                                 }
310                         }
311                 }
312
313                 public NamedPermissionSet GetNamedPermissionSet (string name)
314                 {
315                         if (name == null)
316                                 throw new ArgumentNullException ("name");
317
318                         foreach (NamedPermissionSet n in named_permission_sets) {
319                                 if (n.Name == name)
320                                         return (NamedPermissionSet) n.Copy ();
321                         }
322                         return null;
323                 }
324
325                 public void Recover ()
326                 {
327                         if (_location == null) {
328                                 string msg = Locale.GetText ("Only file based policies may be recovered.");
329                                 throw new PolicyException (msg);
330                         }
331
332                         string backup = _location + ".backup";
333                         if (!File.Exists (backup)) {
334                                 string msg = Locale.GetText ("No policy backup exists.");
335                                 throw new PolicyException (msg);
336                         }
337
338                         try {
339                                 File.Copy (backup, _location, true);
340                         }
341                         catch (Exception e) {
342                                 string msg = Locale.GetText ("Couldn't replace the policy file with it's backup.");
343                                 throw new PolicyException (msg, e);
344                         }
345                 }
346
347                 public void RemoveFullTrustAssembly (StrongName sn)
348                 {
349                         if (sn == null)
350                                 throw new ArgumentNullException ("sn");
351
352                         StrongNameMembershipCondition s = new StrongNameMembershipCondition (sn.PublicKey, sn.Name, sn.Version);
353                         RemoveFullTrustAssembly (s);
354                 }
355
356                 public void RemoveFullTrustAssembly (StrongNameMembershipCondition snMC)
357                 {
358                         if (snMC == null)
359                                 throw new ArgumentNullException ("snMC");
360
361                         if (((IList) full_trust_assemblies).Contains (snMC))
362                                 ((IList) full_trust_assemblies).Remove (snMC);
363
364                         else
365                                 throw new ArgumentException (
366                                         Locale.GetText ("sn does not have full trust."));
367                 }
368
369                 public NamedPermissionSet RemoveNamedPermissionSet (NamedPermissionSet permSet)
370                 {
371                         if (permSet == null)
372                                 throw new ArgumentNullException ("permSet");
373
374                         return RemoveNamedPermissionSet (permSet.Name);
375                 }
376
377                 public NamedPermissionSet RemoveNamedPermissionSet (string name)
378                 {
379                         if (name == null)
380                                 throw new ArgumentNullException ("name");
381                         if (DefaultPolicies.ReservedNames.IsReserved (name))
382                                 throw new ArgumentException (Locale.GetText ("Reserved name"));
383
384                         foreach (NamedPermissionSet nps in named_permission_sets) {
385                                 if (name == nps.Name) {
386                                         named_permission_sets.Remove (nps);
387                                         return nps;
388                                 }
389                         }
390                         string msg = String.Format (Locale.GetText ("Name '{0}' cannot be found."), name);
391                         throw new ArgumentException (msg, "name");
392                 }
393
394                 public void Reset ()
395                 {
396                         if (fullNames != null)
397                                 fullNames.Clear ();
398                         full_trust_assemblies.Clear ();
399                         named_permission_sets.Clear ();
400
401                         if (_type != PolicyLevelType.AppDomain) {
402                                 // because the policy doesn't exist LoadFromFile will try to
403                                 // 1. use the .default file if existing (like Fx 2.0 does); or
404                                 // 2. use the hard-coded default values
405                                 // and recreate a policy file
406                                 if ((_location != null) && (File.Exists (_location))) {
407                                         try {
408                                                 File.Delete (_location);
409                                         }
410                                         catch {}
411                                 }
412                                 LoadFromFile (_location);
413                         }
414                         else {
415                                 named_permission_sets.Add (DefaultPolicies.LocalIntranet);
416                                 named_permission_sets.Add (DefaultPolicies.Internet);
417                                 named_permission_sets.Add (DefaultPolicies.SkipVerification);
418                                 named_permission_sets.Add (DefaultPolicies.Execution);
419                                 named_permission_sets.Add (DefaultPolicies.Nothing);
420                                 named_permission_sets.Add (DefaultPolicies.Everything);
421                                 named_permission_sets.Add (DefaultPolicies.FullTrust);
422                         }
423                 }
424
425                 public PolicyStatement Resolve (Evidence evidence)
426                 {
427                         if (evidence == null)
428                                 throw new ArgumentNullException ("evidence");
429
430                         PolicyStatement ps = root_code_group.Resolve (evidence);
431                         return ((ps != null) ? ps : PolicyStatement.Empty ());
432                 }
433
434                 public CodeGroup ResolveMatchingCodeGroups (Evidence evidence)
435                 {
436                         if (evidence == null)
437                                 throw new ArgumentNullException ("evidence");
438
439                         CodeGroup cg = root_code_group.ResolveMatchingCodeGroups (evidence);
440                         // TODO
441                         return ((cg != null) ? cg : null);
442
443                 }
444
445                 public SecurityElement ToXml ()
446                 {
447                         Hashtable fullNames = new Hashtable ();
448                         // only StrongNameMembershipCondition so no need to loop
449                         if (full_trust_assemblies.Count > 0) {
450                                 if (!fullNames.Contains ("StrongNameMembershipCondition")) {
451                                         fullNames.Add ("StrongNameMembershipCondition", typeof (StrongNameMembershipCondition).FullName);
452                                 }
453                         }
454                         
455                         SecurityElement namedPSs = new SecurityElement ("NamedPermissionSets");
456                         foreach (NamedPermissionSet nps in named_permission_sets) {
457                                 SecurityElement se = nps.ToXml ();
458                                 object objectClass = se.Attributes ["class"];
459                                 if (!fullNames.Contains (objectClass)) {
460                                         fullNames.Add (objectClass, nps.GetType ().FullName);
461                                 }
462                                 namedPSs.AddChild (se);
463                         }
464
465                         SecurityElement fta = new SecurityElement ("FullTrustAssemblies");
466                         foreach (StrongNameMembershipCondition snmc in full_trust_assemblies) {
467                                 fta.AddChild (snmc.ToXml (this));
468                         }
469
470                         SecurityElement security_classes = new SecurityElement ("SecurityClasses");
471                         if (fullNames.Count > 0) {
472                                 foreach (DictionaryEntry de in fullNames) {
473                                         SecurityElement sc = new SecurityElement ("SecurityClass");
474                                         sc.AddAttribute ("Name", (string)de.Key);
475                                         sc.AddAttribute ("Description", (string)de.Value);
476                                         security_classes.AddChild (sc);
477                                 }
478                         }
479
480                         SecurityElement element = new SecurityElement (typeof (System.Security.Policy.PolicyLevel).Name);
481                         element.AddAttribute ("version", "1");
482                         element.AddChild (security_classes);
483                         element.AddChild (namedPSs);
484                         if (root_code_group != null) {
485                                 element.AddChild (root_code_group.ToXml (this));
486                         }
487                         element.AddChild (fta);
488
489                         return element;
490                 }
491
492                 // internal stuff
493
494                 // NOTE: Callers are expected to check for ControlPolicy
495                 internal void Save ()
496                 {
497                         if (_type == PolicyLevelType.AppDomain) {
498                                 throw new PolicyException (Locale.GetText (
499                                         "Can't save AppDomain PolicyLevel"));
500                         }
501
502                         if (_location != null) {
503                                 try {
504                                         if (File.Exists (_location)) {
505                                                 File.Copy (_location, _location + ".backup", true);
506                                         }
507                                 }
508                                 catch (Exception) {
509                                 }
510                                 finally {
511                                         using (StreamWriter sw = new StreamWriter (_location)) {
512                                                 sw.Write (ToXml ().ToString ());
513                                                 sw.Close ();
514                                         }
515                                 }
516                         }
517                 }
518
519                 // Hardcode defaults in case 
520                 // (a) the specified policy file doesn't exists; and
521                 // (b) no corresponding default policy file exists
522                 internal void CreateDefaultLevel (PolicyLevelType type) 
523                 {
524                         PolicyStatement psu = new PolicyStatement (new PermissionSet (PermissionState.Unrestricted));
525
526                         switch (type) {
527                         case PolicyLevelType.Machine:
528                                 // by default all stuff is in the machine policy...
529                                 root_code_group = new UnionCodeGroup (new ZoneMembershipCondition (SecurityZone.MyComputer), psu);
530                                 root_code_group.Name = "All_Code";
531                                 break;
532                         case PolicyLevelType.User:
533                         case PolicyLevelType.Enterprise:
534                         case PolicyLevelType.AppDomain:
535                                 // while the other policies don't restrict anything
536                                 root_code_group = new UnionCodeGroup (new AllMembershipCondition (), psu); 
537                                 root_code_group.Name = "All_Code";
538                                 break;
539                         }
540
541                         // (default) assemblies that are fully trusted during policy resolution
542                         full_trust_assemblies.Clear ();
543                         full_trust_assemblies.Add (DefaultPolicies.FullTrustMembership ("mscorlib", DefaultPolicies.Key.Ecma));
544                         full_trust_assemblies.Add (DefaultPolicies.FullTrustMembership ("System", DefaultPolicies.Key.Ecma));
545                         full_trust_assemblies.Add (DefaultPolicies.FullTrustMembership ("System.Data", DefaultPolicies.Key.Ecma));
546                         full_trust_assemblies.Add (DefaultPolicies.FullTrustMembership ("System.DirectoryServices", DefaultPolicies.Key.MsFinal));
547                         full_trust_assemblies.Add (DefaultPolicies.FullTrustMembership ("System.Drawing", DefaultPolicies.Key.Ecma));
548                         full_trust_assemblies.Add (DefaultPolicies.FullTrustMembership ("System.Messaging", DefaultPolicies.Key.MsFinal));
549                         full_trust_assemblies.Add (DefaultPolicies.FullTrustMembership ("System.ServiceProcess", DefaultPolicies.Key.MsFinal));
550                 }
551
552                 internal void CreateDefaultNamedPermissionSets () 
553                 {
554                         named_permission_sets.Clear ();
555                         named_permission_sets.Add (DefaultPolicies.LocalIntranet);
556                         named_permission_sets.Add (DefaultPolicies.Internet);
557                         named_permission_sets.Add (DefaultPolicies.SkipVerification);
558                         named_permission_sets.Add (DefaultPolicies.Execution);
559                         named_permission_sets.Add (DefaultPolicies.Nothing);
560                         named_permission_sets.Add (DefaultPolicies.Everything);
561                         named_permission_sets.Add (DefaultPolicies.FullTrust);
562                 }
563
564                 internal string ResolveClassName (string className)
565                 {
566                         if (fullNames != null) {
567                                 object name = fullNames [className];
568                                 if (name != null)
569                                         return (string) name;
570                         }
571                         return className;
572                 }
573
574                 internal bool IsFullTrustAssembly (Assembly a)
575                 {
576                         AssemblyName an = a.GetName ();
577                         StrongNamePublicKeyBlob snpkb = new StrongNamePublicKeyBlob (an.GetPublicKey ());
578                         StrongNameMembershipCondition snMC = new StrongNameMembershipCondition (snpkb, an.Name, an.Version);
579                         foreach (StrongNameMembershipCondition sn in full_trust_assemblies) {
580                                 if (sn.Equals (snMC)) {
581                                         return true;
582                                 }
583                         }
584                         return false;
585                 }
586         }
587 }