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;
26 [assembly: AssemblyTitle ("Mono CasPol")]
27 [assembly: AssemblyDescription ("Command line tool to modify Code Access Security policies.")]
29 namespace Mono.Tools {
31 class CustomMembershipCondition : IMembershipCondition {
35 public CustomMembershipCondition (SecurityElement se)
40 public bool Check (Evidence evidence)
45 public IMembershipCondition Copy ()
47 return new CustomMembershipCondition (_se);
50 public void FromXml (SecurityElement e)
55 public SecurityElement ToXml ()
60 public void FromXml (SecurityElement e, PolicyLevel level)
65 public SecurityElement ToXml (PolicyLevel level)
73 static ArrayList _levels;
75 static private void Help ()
77 Console.WriteLine ("Usage: caspol [options] [arguments] ...{0}", Environment.NewLine);
80 // (to be) Stored Options
81 static bool PolicyChangesConfirmation = true;
83 static bool forcePolicyChanges = false;
84 static bool policyLevelDefault = true;
86 static void PrintGlobalInfo ()
88 Console.WriteLine ("Security: {0}", SecurityManager.SecurityEnabled);
89 Console.WriteLine ("Execution check: {0}", SecurityManager.CheckExecutionRights);
90 Console.WriteLine ("Policy changes confirmation: {0}", PolicyChangesConfirmation);
93 static bool Confirm ()
95 if (PolicyChangesConfirmation) {
96 Console.WriteLine ("WARNING: This action will modify the specified security policy!");
97 Console.WriteLine ("Do you want to change the policy ?");
98 string answer = Console.ReadLine ();
99 switch (answer.ToUpper ()) {
104 Console.WriteLine ("Change aborted!");
111 static string Policies (string prefix)
113 StringBuilder sb = new StringBuilder (prefix);
114 PolicyLevel pl = null;
115 for (int i = 0; i < Levels.Count - 1; i++) {
116 pl = (PolicyLevel)Levels [i];
117 sb.AppendFormat ("{0}, ", pl.Label);
119 pl = (PolicyLevel)Levels [Levels.Count - 1];
120 sb.Append (pl.Label);
122 sb.Append (" policy level");
123 if (Levels.Count > 1)
126 return sb.ToString ();
129 // In Fx 1.0/1.1 there is not direct way to load a XML file
130 // into a SecurityElement so we use SecurityParser from
131 // Mono.Security.dll.
132 static SecurityElement LoadXml (string filename)
134 if (!File.Exists (filename)) {
135 Console.WriteLine ("Couldn't not find '{0}'.", filename);
140 using (StreamReader sr = new StreamReader (filename)) {
141 xml = sr.ReadToEnd ();
145 // actually this use the SecurityParser (on the Mono
146 // runtime) in corlib do to the job - but it remove
147 // the dependency on Mono.Security.dll
148 SecurityElement se = SecurityElement.FromString (xml);
150 SecurityParser sp = new SecurityParser ();
152 SecurityElement se = sp.ToXml ();
157 static PermissionSet LoadPermissions (string filename)
159 SecurityElement se = LoadXml (filename);
163 PermissionSet ps = new PermissionSet (PermissionState.None);
165 if (se.Attribute ("class").IndexOf ("System.Security.NamedPermissionSet") == -1)
167 // now we know it's a NamedPermissionSet
168 return (PermissionSet) new NamedPermissionSet (se.Attribute ("Name"), ps);
171 static StrongName GetStrongName (string filename)
174 AssemblyName an = AssemblyName.GetAssemblyName (filename);
175 byte [] pk = an.GetPublicKey ();
176 return new StrongName (new StrongNamePublicKeyBlob (pk), an.Name, an.Version);
178 catch (FileNotFoundException) {
179 Console.WriteLine ("Couldn't find assembly '{0}'.", filename);
184 static Assembly GetAssembly (string filename)
187 AssemblyName an = AssemblyName.GetAssemblyName (filename);
188 return Assembly.Load (an);
190 catch (FileNotFoundException) {
191 Console.WriteLine ("Couldn't find assembly '{0}'.", filename);
196 static Evidence GetAssemblyEvidences (string filename)
198 return GetAssembly (filename).Evidence;
201 static bool OnOff (string value, ref bool on)
203 switch (value.ToUpper ()) {
216 static bool SaveSettings ()
218 Console.WriteLine ("TODO - where to save those settings ?");
225 static void ShowCodeGroup (CodeGroup cg, string prefix)
227 Console.WriteLine ("{0}. {1}: {2}", prefix, cg.MembershipCondition, cg.PermissionSetName);
228 for (int i=0; i < cg.Children.Count; i++) {
229 ShowCodeGroup ((CodeGroup)cg.Children [i], " " + prefix + "." + (i + 1));
235 static void ListCodeGroups ()
239 foreach (PolicyLevel pl in Levels) {
240 Console.WriteLine ("{0}Level: {1}{0}", Environment.NewLine, pl.Label);
242 Console.WriteLine ("Code Groups:{0}", Environment.NewLine);
243 ShowCodeGroup (pl.RootCodeGroup, "1");
247 static void ShowDescription (CodeGroup cg, string prefix)
249 Console.WriteLine ("{0}. {1}: {2}", prefix, cg.Name, cg.Description);
250 for (int i = 0; i < cg.Children.Count; i++) {
251 ShowDescription ((CodeGroup)cg.Children [i], " " + prefix + "." + (i + 1));
257 static void ListDescriptions ()
261 foreach (PolicyLevel pl in Levels) {
262 Console.WriteLine ("{0}Level: {1}{0}", Environment.NewLine, pl.Label);
264 Console.WriteLine ("Code Groups:{0}", Environment.NewLine);
265 ShowDescription (pl.RootCodeGroup, "1");
271 static void ListPermissionSets ()
275 foreach (PolicyLevel pl in Levels) {
276 Console.WriteLine ("{0}Level: {1}{0}", Environment.NewLine, pl.Label);
278 Console.WriteLine ("Named Permission Sets:{0}", Environment.NewLine);
280 foreach (NamedPermissionSet nps in pl.NamedPermissionSets) {
281 Console.WriteLine ("{0}. {1} ({2}) = {3}{4}",
282 n++, nps.Name, nps.Description, Environment.NewLine, nps);
289 static void ListFullTrust ()
293 foreach (PolicyLevel pl in Levels) {
294 Console.WriteLine ("{0}Level: {1}{0}", Environment.NewLine, pl.Label);
296 Console.WriteLine ("Full Trust Assemblies:{0}", Environment.NewLine);
298 foreach (StrongNameMembershipCondition snmc in pl.FullTrustAssemblies) {
299 Console.WriteLine ("{0}. {1} = {2}{3}",
300 n++, snmc.Name, Environment.NewLine, snmc);
305 static void ShowResolveGroup (PolicyLevel pl, Evidence e)
307 Console.WriteLine ("{0}Level: {1}{0}", Environment.NewLine, pl.Label);
308 CodeGroup cg = pl.ResolveMatchingCodeGroups (e);
309 Console.WriteLine ("Code Groups:{0}", Environment.NewLine);
310 ShowCodeGroup (cg, "1");
311 Console.WriteLine ();
315 // -resolvegroup assemblyname
316 static bool ResolveGroup (string assemblyname)
318 Evidence ev = GetAssemblyEvidences (assemblyname);
322 if (policyLevelDefault) {
323 // different "default" here
324 IEnumerator e = SecurityManager.PolicyHierarchy ();
325 while (e.MoveNext ()) {
326 PolicyLevel pl = (PolicyLevel)e.Current;
327 ShowResolveGroup (pl, ev);
330 // use the user specified levels
331 foreach (PolicyLevel pl in Levels) {
332 ShowResolveGroup (pl, ev);
339 // -resolveperm assemblyname
340 static bool ResolvePermissions (string assemblyname)
342 Evidence ev = GetAssemblyEvidences (assemblyname);
346 PermissionSet ps = null;
347 Console.WriteLine ();
348 if (policyLevelDefault) {
349 // different "default" here
350 IEnumerator e = SecurityManager.PolicyHierarchy ();
351 while (e.MoveNext ()) {
352 PolicyLevel pl = (PolicyLevel)e.Current;
353 Console.WriteLine ("Resolving {0} level", pl.Label);
355 ps = pl.Resolve (ev).PermissionSet;
357 ps = ps.Intersect (pl.Resolve (ev).PermissionSet);
360 // use the user specified levels
361 foreach (PolicyLevel pl in Levels) {
362 Console.WriteLine ("Resolving {0} level", pl.Label);
364 ps = pl.Resolve (ev).PermissionSet;
366 ps = ps.Intersect (pl.Resolve (ev).PermissionSet);
372 IEnumerator ee = ev.GetHostEnumerator ();
373 while (ee.MoveNext ()) {
374 IIdentityPermissionFactory ipf = (ee.Current as IIdentityPermissionFactory);
376 IPermission p = ipf.CreateIdentityPermission (ev);
377 ps.AddPermission (p);
381 Console.WriteLine ("{0}Grant:{0}{1}", Environment.NewLine, ps.ToXml ().ToString ());
386 // -addpset namedxmlfile
388 // -addpset xmlfile name
389 static bool AddPermissionSet (string [] args, ref int i)
391 // two syntax - so we first load the XML file and
392 // if it's not a named XML file, then we use the next
393 // parameter as it's name
394 string xmlfile = args [++i];
395 PermissionSet ps = LoadPermissions (xmlfile);
396 if ((ps == null) || !Confirm ())
399 NamedPermissionSet nps = null;
400 if (ps is NamedPermissionSet) {
401 nps = (NamedPermissionSet)ps;
403 nps = new NamedPermissionSet (args [++i], ps);
406 foreach (PolicyLevel pl in Levels) {
407 pl.AddNamedPermissionSet (nps);
408 SecurityManager.SavePolicyLevel (pl);
413 // -cp xmlfile psetname
414 // -chgpset xmlfile psetname
415 static bool ChangePermissionSet (string[] args, ref int i)
417 string xmlfile = args [++i];
418 PermissionSet ps = LoadPermissions (xmlfile);
422 bool confirmed = false;
423 string psname = args [++i];
425 foreach (PolicyLevel pl in Levels) {
426 if (pl.GetNamedPermissionSet (psname) == null) {
427 Console.WriteLine ("Couldn't find '{0}' permission set in policy.", psname);
429 } else if (confirmed || Confirm ()) {
430 confirmed = true; // only ask once
431 pl.ChangeNamedPermissionSet (psname, ps);
432 SecurityManager.SavePolicyLevel (pl);
441 static bool RemovePermissionSet (string psname)
443 bool confirmed = false;
445 foreach (PolicyLevel pl in Levels) {
446 PermissionSet ps = pl.GetNamedPermissionSet (psname);
448 Console.WriteLine ("Couldn't find '{0}' permission set in policy.", psname);
450 } else if (confirmed || Confirm ()) {
451 confirmed = true; // only ask once
452 pl.RemoveNamedPermissionSet (psname);
453 SecurityManager.SavePolicyLevel (pl);
454 Console.WriteLine ("Permission set '{0}' removed from policy.", psname);
462 // -addfulltrust assemblyname
463 static bool AddFullTrust (string aname)
465 StrongName sn = GetStrongName (aname);
466 if ((sn == null) || !Confirm ())
469 foreach (PolicyLevel pl in Levels) {
470 pl.AddFullTrustAssembly (sn);
476 // -remfulltrust assemblyname
477 static bool RemoveFullTrust (string aname)
479 StrongName sn = GetStrongName (aname);
480 if ((sn == null) || !Confirm ())
483 foreach (PolicyLevel pl in Levels) {
484 pl.RemoveFullTrustAssembly (sn);
490 static CodeGroup FindCodeGroupByName (string name, ref CodeGroup parent)
492 for (int i = 0; i < parent.Children.Count; i++) {
493 CodeGroup child = (CodeGroup)parent.Children [i];
494 if (child.Name == name) {
497 CodeGroup cg = FindCodeGroupByName (name, ref child);
505 static CodeGroup FindCodeGroupByLabel (string label, string current, ref CodeGroup parent)
507 for (int i=0; i < parent.Children.Count; i++) {
508 CodeGroup child = (CodeGroup)parent.Children [i];
509 string temp = String.Concat (current, ".", (i + 1).ToString ());
510 if ((label == temp) || (label == temp + ".")) {
512 } else if (label.StartsWith (temp)) {
513 CodeGroup cg = FindCodeGroupByLabel (label, temp, ref child);
521 static CodeGroup FindCodeGroup (string name, ref CodeGroup parent, ref PolicyLevel pl)
527 // - labels starts with numbers (e.g. 1.2.1)
528 // - names cannot start with numbers (A-Z, 0-9 and _)
529 bool label = Char.IsDigit (name, 0);
532 // - we can't remove the root code group
533 // - we remove only one group (e.g. name)
534 for (int i=0; i < Levels.Count; i++) {
535 pl = (PolicyLevel) Levels [i];
536 parent = pl.RootCodeGroup;
539 cg = FindCodeGroupByLabel (name, "1", ref parent);
541 cg = FindCodeGroupByName (name, ref parent);
546 Console.WriteLine ("CodeGroup with {0} '{1}' was not found!",
547 label ? "label" : "name", name);
552 static IMembershipCondition ProcessCustomMembership (string filename)
554 SecurityElement se = LoadXml (filename);
557 return new CustomMembershipCondition (se);
560 // -hash algo -hex hash
561 // -hash algo -file assemblyname
562 static IMembershipCondition ProcessHashMembership (string[] args, ref int i)
564 HashAlgorithm ha = HashAlgorithm.Create (args [++i]);
565 byte [] value = null;
566 switch (args [++i]) {
568 value = CryptoConvert.FromHex (args [++i]);
571 Hash hash = new Hash (GetAssembly (args [++i]));
572 value = hash.GenerateHash (ha);
577 return new HashMembershipCondition (ha, value);
580 // -pub -cert certificate
581 // -pub -file signedfile
583 static IMembershipCondition ProcessPublisherMembership (string[] args, ref int i)
585 X509Certificate cert = null;
586 switch (args [++i]) {
588 cert = X509Certificate.CreateFromCertFile (args [++i]);
591 cert = X509Certificate.CreateFromSignedFile (args [++i]);
594 byte[] raw = CryptoConvert.FromHex (args [++i]);
595 cert = new X509Certificate (raw);
600 return new PublisherMembershipCondition (cert);
603 // -strong -file filename [name | -noname] [version | -noversion]
604 static IMembershipCondition ProcessStrongNameMembership (string[] args, ref int i)
606 if (args [++i] != "-file") {
607 Console.WriteLine ("Missing -file parameter.");
611 StrongName sn = GetStrongName (args [++i]);
613 string name = args [++i];
614 if (name == "-noname")
618 string version = args [++i];
619 if (version != "-noversion")
620 v = new Version (version);
622 return new StrongNameMembershipCondition (sn.PublicKey, name, v);
625 static bool ProcessCodeGroup (CodeGroup cg, string[] args, ref int i)
627 IMembershipCondition mship = null;
628 for (; i < args.Length; i++) {
629 switch (args [++i]) {
631 cg.MembershipCondition = new AllMembershipCondition ();
634 cg.MembershipCondition = new ApplicationDirectoryMembershipCondition ();
637 mship = ProcessCustomMembership (args [++i]);
640 cg.MembershipCondition = mship;
643 mship = ProcessHashMembership (args, ref i);
646 cg.MembershipCondition = mship;
649 mship = ProcessPublisherMembership (args, ref i);
652 cg.MembershipCondition = mship;
655 cg.MembershipCondition = new SiteMembershipCondition (args [++i]);
658 mship = ProcessStrongNameMembership (args, ref i);
661 cg.MembershipCondition = mship;
664 cg.MembershipCondition = new UrlMembershipCondition (args [++i]);
667 SecurityZone zone = (SecurityZone) Enum.Parse (typeof (SecurityZone), args [++i]);
668 cg.MembershipCondition = new ZoneMembershipCondition (zone);
673 cg.Description = args [++i];
676 bool exclusive = false;
677 if (OnOff (args [++i], ref exclusive)) {
679 cg.PolicyStatement.Attributes |= PolicyStatementAttribute.Exclusive;
686 if (OnOff (args [++i], ref final)) {
688 cg.PolicyStatement.Attributes |= PolicyStatementAttribute.LevelFinal;
695 cg.Name = args [++i];
705 // -ag label|name membership psetname flag
706 // -addgroup label|name membership psetname flag
707 static bool AddCodeGroup (string[] args, ref int i)
709 string name = args [++i];
711 PolicyLevel pl = null;
712 CodeGroup parent = null;
713 CodeGroup cg = FindCodeGroup (name, ref parent, ref pl);
714 if ((pl == null) || (parent == null) || (cg == null))
717 UnionCodeGroup child = new UnionCodeGroup (
718 new AllMembershipCondition (),
719 new PolicyStatement (new PermissionSet (PermissionState.Unrestricted)));
720 if (!ProcessCodeGroup (child, args, ref i))
724 SecurityManager.SavePolicyLevel (pl);
725 Console.WriteLine ("CodeGroup '{0}' added in {1} policy level.",
730 // -cg label|name membership|psetname|flag
731 // -chggroup label|name membership|psetname|flag
732 static bool ChangeCodeGroup (string[] args, ref int i)
734 string name = args [++i];
736 PolicyLevel pl = null;
737 CodeGroup parent = null;
738 CodeGroup cg = FindCodeGroup (name, ref parent, ref pl);
739 if ((pl == null) || (parent == null) || (cg == null))
742 if (!ProcessCodeGroup (cg, args, ref i))
745 SecurityManager.SavePolicyLevel (pl);
746 Console.WriteLine ("CodeGroup '{0}' modified in {1} policy level.",
752 // -remgroup label|name
753 static bool RemoveCodeGroup (string name)
755 PolicyLevel pl = null;
756 CodeGroup parent = null;
757 CodeGroup cg = FindCodeGroup (name, ref parent, ref pl);
758 if ((pl == null) || (parent == null) || (cg == null))
764 parent.RemoveChild (cg);
765 SecurityManager.SavePolicyLevel (pl);
766 Console.WriteLine ("CodeGroup '{0}' removed from {1} policy level.",
773 static void Recover ()
775 // no confirmation required to recover
776 foreach (PolicyLevel pl in Levels) {
778 SecurityManager.SavePolicyLevel (pl);
786 Console.WriteLine (Policies ("Resetting "));
788 foreach (PolicyLevel pl in Levels) {
790 SecurityManager.SavePolicyLevel (pl);
799 static bool Security (string value)
802 if (!OnOff (value, ref on))
804 SecurityManager.SecurityEnabled = on;
805 return SaveSettings ();
810 static bool Execution (string value)
813 if (!OnOff (value, ref on))
815 SecurityManager.CheckExecutionRights = on;
816 return SaveSettings ();
821 static bool BuildCache ()
828 // -polchgprompt on|off
829 static bool PolicyChangePrompt (string value)
832 if (!OnOff (value, ref on))
834 PolicyChangesConfirmation = on;
835 return SaveSettings ();
839 // Policy Levels Internal Management
841 static PolicyLevel levelEnterprise;
842 static PolicyLevel levelMachine;
843 static PolicyLevel levelUser;
845 static void BuildLevels ()
847 IEnumerator e = SecurityManager.PolicyHierarchy ();
849 levelEnterprise = (PolicyLevel) e.Current;
851 levelMachine = (PolicyLevel) e.Current;
853 levelUser = (PolicyLevel) e.Current;
856 static PolicyLevel Enterprise {
858 if (levelEnterprise == null)
860 return levelEnterprise;
864 static PolicyLevel Machine {
866 if (levelMachine == null)
872 static PolicyLevel User {
874 if (levelUser == null)
880 static ArrayList Levels {
883 _levels = new ArrayList (3);
888 static bool ProcessInstruction (string[] args, ref int i)
890 for (; i < args.Length; i++) {
894 PolicyChangesConfirmation = false;
898 forcePolicyChanges = true;
909 policyLevelDefault = false;
911 Levels.Add (Enterprise);
912 Levels.Add (Machine);
917 policyLevelDefault = false;
919 Levels.Add (Enterprise);
920 Levels.Add (Machine);
921 Levels.Add (SecurityManager.LoadPolicyLevelFromFile (args [++i], PolicyLevelType.User));
925 policyLevelDefault = false;
927 Levels.Add (SecurityManager.LoadPolicyLevelFromFile (args [++i], PolicyLevelType.User));
931 policyLevelDefault = false;
933 Levels.Add (Enterprise);
937 policyLevelDefault = false;
939 Levels.Add (Machine);
943 policyLevelDefault = false;
953 case "-listdescription":
958 ListPermissionSets ();
961 case "-listfulltrust":
967 Console.WriteLine ();
968 ListPermissionSets ();
969 Console.WriteLine ();
974 case "-resolvegroup":
975 if (!ResolveGroup (args [++i]))
980 if (!ResolvePermissions (args [++i]))
986 if (!AddPermissionSet (args, ref i))
991 if (!ChangePermissionSet (args, ref i))
996 if (!RemovePermissionSet (args [++i]))
1001 case "-addfulltrust":
1002 if (!AddFullTrust (args [++i]))
1006 case "-remfulltrust":
1007 if (!RemoveFullTrust (args [++i]))
1013 if (!AddCodeGroup (args, ref i))
1018 if (!ChangeCodeGroup (args, ref i))
1023 if (!RemoveCodeGroup (args [++i]))
1039 if (!Security (args [++i]))
1044 if (!Execution (args [++i]))
1053 case "-polchgprompt":
1054 if (!PolicyChangePrompt (args [++i]))
1059 Console.WriteLine ("*** unknown argument {0} ***", args [i]);
1062 Console.WriteLine ();
1067 static void SetDefaultPolicyLevel ()
1069 // default is User for normal users and Machine for
1070 // administrators. Here we define an administrator as
1071 // someone who can write to the Machine policy files
1073 using (FileStream fs = File.OpenWrite (Machine.StoreLocation)) {
1076 Levels.Add (Machine);
1081 // some actions, like resolves, use a different default (all)
1082 policyLevelDefault = true;
1086 static int Main (string[] args)
1088 Console.WriteLine (new AssemblyInfo ().ToString ());
1089 if (args.Length == 0) {
1095 // set default level (when none is specified
1096 // by command line options)
1097 SetDefaultPolicyLevel ();
1099 // process instructions (i.e. multiple
1100 // instructions can be chained)
1101 for (int i=0; i < args.Length; i++) {
1102 if (!ProcessInstruction (args, ref i))
1106 catch (Exception e) {
1107 Console.WriteLine ("Error: " + e.ToString ());
1111 Console.WriteLine ("Success");