2 // caspol.cs: Code Access Security Policy Tool
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
11 using System.Collections;
13 using System.Reflection;
14 using System.Security;
15 using System.Security.Cryptography;
16 using System.Security.Cryptography.X509Certificates;
17 using System.Security.Permissions;
18 using System.Security.Policy;
21 using Mono.Security.Cryptography;
23 [assembly: AssemblyTitle ("Mono CasPol")]
24 [assembly: AssemblyDescription ("Command line tool to modify Code Access Security policies.")]
26 namespace Mono.Tools {
28 class CustomMembershipCondition : IMembershipCondition {
32 public CustomMembershipCondition (SecurityElement se)
37 public bool Check (Evidence evidence)
42 public IMembershipCondition Copy ()
44 return new CustomMembershipCondition (_se);
47 public void FromXml (SecurityElement e)
52 public SecurityElement ToXml ()
57 public void FromXml (SecurityElement e, PolicyLevel level)
62 public SecurityElement ToXml (PolicyLevel level)
70 static ArrayList _levels;
72 static private void Help ()
74 Console.WriteLine ("Usage: caspol [options] [arguments] ...{0}", Environment.NewLine);
77 // (to be) Stored Options
78 static bool PolicyChangesConfirmation = true;
80 static bool forcePolicyChanges = false;
81 static bool policyLevelDefault = true;
83 static void PrintGlobalInfo ()
85 Console.WriteLine ("Security: {0}", SecurityManager.SecurityEnabled);
86 Console.WriteLine ("Execution check: {0}", SecurityManager.CheckExecutionRights);
87 Console.WriteLine ("Policy changes confirmation: {0}", PolicyChangesConfirmation);
90 static bool Confirm ()
92 if (PolicyChangesConfirmation) {
93 Console.WriteLine ("WARNING: This action will modify the specified security policy!");
94 Console.WriteLine ("Do you want to change the policy ?");
95 string answer = Console.ReadLine ();
96 switch (answer.ToUpper ()) {
101 Console.WriteLine ("Change aborted!");
108 static string Policies (string prefix)
110 StringBuilder sb = new StringBuilder (prefix);
111 PolicyLevel pl = null;
112 for (int i = 0; i < Levels.Count - 1; i++) {
113 pl = (PolicyLevel)Levels [i];
114 sb.AppendFormat ("{0}, ", pl.Label);
116 pl = (PolicyLevel)Levels [Levels.Count - 1];
117 sb.Append (pl.Label);
119 sb.Append (" policy level");
120 if (Levels.Count > 1)
123 return sb.ToString ();
126 // In Fx 1.0/1.1 there is not direct way to load a XML file
127 // into a SecurityElement so we use SecurityParser from
128 // Mono.Security.dll.
129 static SecurityElement LoadXml (string filename)
131 if (!File.Exists (filename)) {
132 Console.WriteLine ("Couldn't not find '{0}'.", filename);
137 using (StreamReader sr = new StreamReader (filename)) {
138 xml = sr.ReadToEnd ();
141 // actually this use the SecurityParser (on the Mono
142 // runtime) in corlib do to the job - but it remove
143 // the dependency on Mono.Security.dll
144 SecurityElement se = SecurityElement.FromString (xml);
148 static PermissionSet LoadPermissions (string filename)
150 SecurityElement se = LoadXml (filename);
154 PermissionSet ps = new PermissionSet (PermissionState.None);
156 if (se.Attribute ("class").IndexOf ("System.Security.NamedPermissionSet") == -1)
158 // now we know it's a NamedPermissionSet
159 return (PermissionSet) new NamedPermissionSet (se.Attribute ("Name"), ps);
162 static StrongName GetStrongName (string filename)
165 AssemblyName an = AssemblyName.GetAssemblyName (filename);
166 byte [] pk = an.GetPublicKey ();
167 return new StrongName (new StrongNamePublicKeyBlob (pk), an.Name, an.Version);
169 catch (FileNotFoundException) {
170 Console.WriteLine ("Couldn't find assembly '{0}'.", filename);
175 static Assembly GetAssembly (string filename)
178 AssemblyName an = AssemblyName.GetAssemblyName (filename);
179 return Assembly.Load (an);
181 catch (FileNotFoundException) {
182 Console.WriteLine ("Couldn't find assembly '{0}'.", filename);
187 static Evidence GetAssemblyEvidences (string filename)
189 return GetAssembly (filename).Evidence;
192 static bool OnOff (string value, ref bool on)
194 switch (value.ToUpper ()) {
207 static bool SaveSettings ()
209 Console.WriteLine ("TODO - where to save those settings ?");
216 static void ShowCodeGroup (CodeGroup cg, string prefix)
218 Console.WriteLine ("{0}. {1}: {2}", prefix, cg.MembershipCondition, cg.PermissionSetName);
219 for (int i=0; i < cg.Children.Count; i++) {
220 ShowCodeGroup ((CodeGroup)cg.Children [i], " " + prefix + "." + (i + 1));
226 static void ListCodeGroups ()
230 foreach (PolicyLevel pl in Levels) {
231 Console.WriteLine ("{0}Level: {1}{0}", Environment.NewLine, pl.Label);
233 Console.WriteLine ("Code Groups:{0}", Environment.NewLine);
234 ShowCodeGroup (pl.RootCodeGroup, "1");
238 static void ShowDescription (CodeGroup cg, string prefix)
240 Console.WriteLine ("{0}. {1}: {2}", prefix, cg.Name, cg.Description);
241 for (int i = 0; i < cg.Children.Count; i++) {
242 ShowDescription ((CodeGroup)cg.Children [i], " " + prefix + "." + (i + 1));
248 static void ListDescriptions ()
252 foreach (PolicyLevel pl in Levels) {
253 Console.WriteLine ("{0}Level: {1}{0}", Environment.NewLine, pl.Label);
255 Console.WriteLine ("Code Groups:{0}", Environment.NewLine);
256 ShowDescription (pl.RootCodeGroup, "1");
262 static void ListPermissionSets ()
266 foreach (PolicyLevel pl in Levels) {
267 Console.WriteLine ("{0}Level: {1}{0}", Environment.NewLine, pl.Label);
269 Console.WriteLine ("Named Permission Sets:{0}", Environment.NewLine);
271 foreach (NamedPermissionSet nps in pl.NamedPermissionSets) {
272 Console.WriteLine ("{0}. {1} ({2}) = {3}{4}",
273 n++, nps.Name, nps.Description, Environment.NewLine, nps);
280 static void ListFullTrust ()
284 foreach (PolicyLevel pl in Levels) {
285 Console.WriteLine ("{0}Level: {1}{0}", Environment.NewLine, pl.Label);
287 Console.WriteLine ("Full Trust Assemblies:{0}", Environment.NewLine);
289 foreach (StrongNameMembershipCondition snmc in pl.FullTrustAssemblies) {
290 Console.WriteLine ("{0}. {1} = {2}{3}",
291 n++, snmc.Name, Environment.NewLine, snmc);
296 static void ShowResolveGroup (PolicyLevel pl, Evidence e)
298 Console.WriteLine ("{0}Level: {1}{0}", Environment.NewLine, pl.Label);
299 CodeGroup cg = pl.ResolveMatchingCodeGroups (e);
300 Console.WriteLine ("Code Groups:{0}", Environment.NewLine);
301 ShowCodeGroup (cg, "1");
302 Console.WriteLine ();
306 // -resolvegroup assemblyname
307 static bool ResolveGroup (string assemblyname)
309 Evidence ev = GetAssemblyEvidences (assemblyname);
313 if (policyLevelDefault) {
314 // different "default" here
315 IEnumerator e = SecurityManager.PolicyHierarchy ();
316 while (e.MoveNext ()) {
317 PolicyLevel pl = (PolicyLevel)e.Current;
318 ShowResolveGroup (pl, ev);
321 // use the user specified levels
322 foreach (PolicyLevel pl in Levels) {
323 ShowResolveGroup (pl, ev);
330 // -resolveperm assemblyname
331 static bool ResolvePermissions (string assemblyname)
333 Evidence ev = GetAssemblyEvidences (assemblyname);
337 PermissionSet ps = null;
338 Console.WriteLine ();
339 if (policyLevelDefault) {
340 // different "default" here
341 IEnumerator e = SecurityManager.PolicyHierarchy ();
342 while (e.MoveNext ()) {
343 PolicyLevel pl = (PolicyLevel)e.Current;
344 Console.WriteLine ("Resolving {0} level", pl.Label);
346 ps = pl.Resolve (ev).PermissionSet;
348 ps = ps.Intersect (pl.Resolve (ev).PermissionSet);
351 // use the user specified levels
352 foreach (PolicyLevel pl in Levels) {
353 Console.WriteLine ("Resolving {0} level", pl.Label);
355 ps = pl.Resolve (ev).PermissionSet;
357 ps = ps.Intersect (pl.Resolve (ev).PermissionSet);
363 IEnumerator ee = ev.GetHostEnumerator ();
364 while (ee.MoveNext ()) {
365 IIdentityPermissionFactory ipf = (ee.Current as IIdentityPermissionFactory);
367 IPermission p = ipf.CreateIdentityPermission (ev);
368 ps.AddPermission (p);
372 Console.WriteLine ("{0}Grant:{0}{1}", Environment.NewLine, ps.ToXml ().ToString ());
377 // -addpset namedxmlfile
379 // -addpset xmlfile name
380 static bool AddPermissionSet (string [] args, ref int i)
382 // two syntax - so we first load the XML file and
383 // if it's not a named XML file, then we use the next
384 // parameter as it's name
385 string xmlfile = args [++i];
386 PermissionSet ps = LoadPermissions (xmlfile);
387 if ((ps == null) || !Confirm ())
390 NamedPermissionSet nps = null;
391 if (ps is NamedPermissionSet) {
392 nps = (NamedPermissionSet)ps;
394 nps = new NamedPermissionSet (args [++i], ps);
397 foreach (PolicyLevel pl in Levels) {
398 pl.AddNamedPermissionSet (nps);
399 SecurityManager.SavePolicyLevel (pl);
404 // -cp xmlfile psetname
405 // -chgpset xmlfile psetname
406 static bool ChangePermissionSet (string[] args, ref int i)
408 string xmlfile = args [++i];
409 PermissionSet ps = LoadPermissions (xmlfile);
413 bool confirmed = false;
414 string psname = args [++i];
416 foreach (PolicyLevel pl in Levels) {
417 if (pl.GetNamedPermissionSet (psname) == null) {
418 Console.WriteLine ("Couldn't find '{0}' permission set in policy.", psname);
420 } else if (confirmed || Confirm ()) {
421 confirmed = true; // only ask once
422 pl.ChangeNamedPermissionSet (psname, ps);
423 SecurityManager.SavePolicyLevel (pl);
432 static bool RemovePermissionSet (string psname)
434 bool confirmed = false;
436 foreach (PolicyLevel pl in Levels) {
437 PermissionSet ps = pl.GetNamedPermissionSet (psname);
439 Console.WriteLine ("Couldn't find '{0}' permission set in policy.", psname);
441 } else if (confirmed || Confirm ()) {
442 confirmed = true; // only ask once
443 pl.RemoveNamedPermissionSet (psname);
444 SecurityManager.SavePolicyLevel (pl);
445 Console.WriteLine ("Permission set '{0}' removed from policy.", psname);
453 // -addfulltrust assemblyname
454 static bool AddFullTrust (string aname)
456 StrongName sn = GetStrongName (aname);
457 if ((sn == null) || !Confirm ())
460 foreach (PolicyLevel pl in Levels) {
461 pl.AddFullTrustAssembly (sn);
467 // -remfulltrust assemblyname
468 static bool RemoveFullTrust (string aname)
470 StrongName sn = GetStrongName (aname);
471 if ((sn == null) || !Confirm ())
474 foreach (PolicyLevel pl in Levels) {
475 pl.RemoveFullTrustAssembly (sn);
481 static CodeGroup FindCodeGroupByName (string name, ref CodeGroup parent)
483 for (int i = 0; i < parent.Children.Count; i++) {
484 CodeGroup child = (CodeGroup)parent.Children [i];
485 if (child.Name == name) {
488 CodeGroup cg = FindCodeGroupByName (name, ref child);
496 static CodeGroup FindCodeGroupByLabel (string label, string current, ref CodeGroup parent)
498 for (int i=0; i < parent.Children.Count; i++) {
499 CodeGroup child = (CodeGroup)parent.Children [i];
500 string temp = String.Concat (current, ".", (i + 1).ToString ());
501 if ((label == temp) || (label == temp + ".")) {
503 } else if (label.StartsWith (temp)) {
504 CodeGroup cg = FindCodeGroupByLabel (label, temp, ref child);
512 static CodeGroup FindCodeGroup (string name, ref CodeGroup parent, ref PolicyLevel pl)
518 // - labels starts with numbers (e.g. 1.2.1)
519 // - names cannot start with numbers (A-Z, 0-9 and _)
520 bool label = Char.IsDigit (name, 0);
523 // - we can't remove the root code group
524 // - we remove only one group (e.g. name)
525 for (int i=0; i < Levels.Count; i++) {
526 pl = (PolicyLevel) Levels [i];
527 parent = pl.RootCodeGroup;
530 cg = FindCodeGroupByLabel (name, "1", ref parent);
532 cg = FindCodeGroupByName (name, ref parent);
537 Console.WriteLine ("CodeGroup with {0} '{1}' was not found!",
538 label ? "label" : "name", name);
543 static IMembershipCondition ProcessCustomMembership (string filename)
545 SecurityElement se = LoadXml (filename);
548 return new CustomMembershipCondition (se);
551 // -hash algo -hex hash
552 // -hash algo -file assemblyname
553 static IMembershipCondition ProcessHashMembership (string[] args, ref int i)
555 HashAlgorithm ha = HashAlgorithm.Create (args [++i]);
556 byte [] value = null;
557 switch (args [++i]) {
559 value = CryptoConvert.FromHex (args [++i]);
562 Hash hash = new Hash (GetAssembly (args [++i]));
563 value = hash.GenerateHash (ha);
568 return new HashMembershipCondition (ha, value);
571 // -pub -cert certificate
572 // -pub -file signedfile
574 static IMembershipCondition ProcessPublisherMembership (string[] args, ref int i)
576 X509Certificate cert = null;
577 switch (args [++i]) {
579 cert = X509Certificate.CreateFromCertFile (args [++i]);
582 cert = X509Certificate.CreateFromSignedFile (args [++i]);
585 byte[] raw = CryptoConvert.FromHex (args [++i]);
586 cert = new X509Certificate (raw);
591 return new PublisherMembershipCondition (cert);
594 // -strong -file filename [name | -noname] [version | -noversion]
595 static IMembershipCondition ProcessStrongNameMembership (string[] args, ref int i)
597 if (args [++i] != "-file") {
598 Console.WriteLine ("Missing -file parameter.");
602 StrongName sn = GetStrongName (args [++i]);
604 string name = args [++i];
605 if (name == "-noname")
609 string version = args [++i];
610 if (version != "-noversion")
611 v = new Version (version);
613 return new StrongNameMembershipCondition (sn.PublicKey, name, v);
616 static bool ProcessCodeGroup (CodeGroup cg, string[] args, ref int i)
618 IMembershipCondition mship = null;
619 for (; i < args.Length; i++) {
620 switch (args [++i]) {
622 cg.MembershipCondition = new AllMembershipCondition ();
625 cg.MembershipCondition = new ApplicationDirectoryMembershipCondition ();
628 mship = ProcessCustomMembership (args [++i]);
631 cg.MembershipCondition = mship;
634 mship = ProcessHashMembership (args, ref i);
637 cg.MembershipCondition = mship;
640 mship = ProcessPublisherMembership (args, ref i);
643 cg.MembershipCondition = mship;
646 cg.MembershipCondition = new SiteMembershipCondition (args [++i]);
649 mship = ProcessStrongNameMembership (args, ref i);
652 cg.MembershipCondition = mship;
655 cg.MembershipCondition = new UrlMembershipCondition (args [++i]);
658 SecurityZone zone = (SecurityZone) Enum.Parse (typeof (SecurityZone), args [++i]);
659 cg.MembershipCondition = new ZoneMembershipCondition (zone);
664 cg.Description = args [++i];
667 bool exclusive = false;
668 if (OnOff (args [++i], ref exclusive)) {
670 cg.PolicyStatement.Attributes |= PolicyStatementAttribute.Exclusive;
677 if (OnOff (args [++i], ref final)) {
679 cg.PolicyStatement.Attributes |= PolicyStatementAttribute.LevelFinal;
686 cg.Name = args [++i];
696 // -ag label|name membership psetname flag
697 // -addgroup label|name membership psetname flag
698 static bool AddCodeGroup (string[] args, ref int i)
700 string name = args [++i];
702 PolicyLevel pl = null;
703 CodeGroup parent = null;
704 CodeGroup cg = FindCodeGroup (name, ref parent, ref pl);
705 if ((pl == null) || (parent == null) || (cg == null))
708 UnionCodeGroup child = new UnionCodeGroup (
709 new AllMembershipCondition (),
710 new PolicyStatement (new PermissionSet (PermissionState.Unrestricted)));
711 if (!ProcessCodeGroup (child, args, ref i))
715 SecurityManager.SavePolicyLevel (pl);
716 Console.WriteLine ("CodeGroup '{0}' added in {1} policy level.",
721 // -cg label|name membership|psetname|flag
722 // -chggroup label|name membership|psetname|flag
723 static bool ChangeCodeGroup (string[] args, ref int i)
725 string name = args [++i];
727 PolicyLevel pl = null;
728 CodeGroup parent = null;
729 CodeGroup cg = FindCodeGroup (name, ref parent, ref pl);
730 if ((pl == null) || (parent == null) || (cg == null))
733 if (!ProcessCodeGroup (cg, args, ref i))
736 SecurityManager.SavePolicyLevel (pl);
737 Console.WriteLine ("CodeGroup '{0}' modified in {1} policy level.",
743 // -remgroup label|name
744 static bool RemoveCodeGroup (string name)
746 PolicyLevel pl = null;
747 CodeGroup parent = null;
748 CodeGroup cg = FindCodeGroup (name, ref parent, ref pl);
749 if ((pl == null) || (parent == null) || (cg == null))
755 parent.RemoveChild (cg);
756 SecurityManager.SavePolicyLevel (pl);
757 Console.WriteLine ("CodeGroup '{0}' removed from {1} policy level.",
764 static void Recover ()
766 // no confirmation required to recover
767 foreach (PolicyLevel pl in Levels) {
769 SecurityManager.SavePolicyLevel (pl);
777 Console.WriteLine (Policies ("Resetting "));
779 foreach (PolicyLevel pl in Levels) {
781 SecurityManager.SavePolicyLevel (pl);
790 static bool Security (string value)
793 if (!OnOff (value, ref on))
795 SecurityManager.SecurityEnabled = on;
796 return SaveSettings ();
801 static bool Execution (string value)
804 if (!OnOff (value, ref on))
806 SecurityManager.CheckExecutionRights = on;
807 return SaveSettings ();
812 static bool BuildCache ()
819 // -polchgprompt on|off
820 static bool PolicyChangePrompt (string value)
823 if (!OnOff (value, ref on))
825 PolicyChangesConfirmation = on;
826 return SaveSettings ();
830 // Policy Levels Internal Management
832 static PolicyLevel levelEnterprise;
833 static PolicyLevel levelMachine;
834 static PolicyLevel levelUser;
836 static void BuildLevels ()
838 IEnumerator e = SecurityManager.PolicyHierarchy ();
840 levelEnterprise = (PolicyLevel) e.Current;
842 levelMachine = (PolicyLevel) e.Current;
844 levelUser = (PolicyLevel) e.Current;
847 static PolicyLevel Enterprise {
849 if (levelEnterprise == null)
851 return levelEnterprise;
855 static PolicyLevel Machine {
857 if (levelMachine == null)
863 static PolicyLevel User {
865 if (levelUser == null)
871 static ArrayList Levels {
874 _levels = new ArrayList (3);
879 static bool ProcessInstruction (string[] args, ref int i)
881 for (; i < args.Length; i++) {
885 PolicyChangesConfirmation = false;
889 forcePolicyChanges = true;
900 policyLevelDefault = false;
902 Levels.Add (Enterprise);
903 Levels.Add (Machine);
908 policyLevelDefault = false;
910 Levels.Add (Enterprise);
911 Levels.Add (Machine);
912 Levels.Add (SecurityManager.LoadPolicyLevelFromFile (args [++i], PolicyLevelType.User));
916 policyLevelDefault = false;
918 Levels.Add (SecurityManager.LoadPolicyLevelFromFile (args [++i], PolicyLevelType.User));
922 policyLevelDefault = false;
924 Levels.Add (Enterprise);
928 policyLevelDefault = false;
930 Levels.Add (Machine);
934 policyLevelDefault = false;
944 case "-listdescription":
949 ListPermissionSets ();
952 case "-listfulltrust":
958 Console.WriteLine ();
959 ListPermissionSets ();
960 Console.WriteLine ();
965 case "-resolvegroup":
966 if (!ResolveGroup (args [++i]))
971 if (!ResolvePermissions (args [++i]))
977 if (!AddPermissionSet (args, ref i))
982 if (!ChangePermissionSet (args, ref i))
987 if (!RemovePermissionSet (args [++i]))
992 case "-addfulltrust":
993 if (!AddFullTrust (args [++i]))
997 case "-remfulltrust":
998 if (!RemoveFullTrust (args [++i]))
1004 if (!AddCodeGroup (args, ref i))
1009 if (!ChangeCodeGroup (args, ref i))
1014 if (!RemoveCodeGroup (args [++i]))
1030 if (!Security (args [++i]))
1035 if (!Execution (args [++i]))
1044 case "-polchgprompt":
1045 if (!PolicyChangePrompt (args [++i]))
1050 Console.WriteLine ("*** unknown argument {0} ***", args [i]);
1053 Console.WriteLine ();
1058 static void SetDefaultPolicyLevel ()
1060 // default is User for normal users and Machine for
1061 // administrators. Here we define an administrator as
1062 // someone who can write to the Machine policy files
1064 using (FileStream fs = File.OpenWrite (Machine.StoreLocation)) {
1067 Levels.Add (Machine);
1072 // some actions, like resolves, use a different default (all)
1073 policyLevelDefault = true;
1077 static int Main (string[] args)
1079 Console.WriteLine (new AssemblyInfo ().ToString ());
1080 if (args.Length == 0) {
1086 // set default level (when none is specified
1087 // by command line options)
1088 SetDefaultPolicyLevel ();
1090 // process instructions (i.e. multiple
1091 // instructions can be chained)
1092 for (int i=0; i < args.Length; i++) {
1093 if (!ProcessInstruction (args, ref i))
1097 catch (Exception e) {
1098 Console.WriteLine ("Error: " + e.ToString ());
1102 Console.WriteLine ("Success");