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");