//----------------------------------------------------------------------------- // // Class: NDPHost // // Fusion ClickOnce NDP Application host // // Date: 12/7/2001 // // Copyright (c) Microsoft, 2001 // //----------------------------------------------------------------------------- using System; using System.Text; using System.Net; using System.IO; using System.Text.RegularExpressions; using System.Runtime.Remoting; using System.Globalization; using System.Security; using System.Security.Policy; using System.Security.Permissions; using System.Collections; using System.Runtime.InteropServices; using System.Reflection; using System.Configuration.Assemblies; using Avalon.Security; [assembly:AssemblyCultureAttribute("")] [assembly:AssemblyVersionAttribute("1.0.1218.0")] [assembly:AssemblyKeyFileAttribute(/*"..\..\*/"NDPHostKey.snk")] [assembly:AssemblyTitleAttribute("Microsoft Application Deployment Framework .Net Assembly Execute Host")] [assembly:AssemblyDescriptionAttribute("Microsoft Application Deployment Framework - NDP Hosting for .Net assemblies")] [assembly:AssemblyProductAttribute("Microsoft Application Deployment Framework")] [assembly:AssemblyInformationalVersionAttribute("1.0.0.0")] [assembly:AssemblyTrademarkAttribute("Microsoft® is a registered trademark of Microsoft Corporation. Windows(TM) is a trademark of Microsoft Corporation")] [assembly:AssemblyCompanyAttribute("Microsoft Corporation")] [assembly:AssemblyCopyrightAttribute("Copyright © Microsoft Corp. 1999-2002. All rights reserved.")] [assembly:System.CLSCompliant(true)] namespace Microsoft.Fusion.ADF { //---------------------------------------------------------------------------- // class NDPHost //---------------------------------------------------------------------------- public class NDPHost : MarshalByRefObject { // cmd arg ordinals. enum eCmd : int { AppBase, AsmName, AsmClass, AsmMethod, AsmArgs, Url, Zone, ManifestPath, SecurityStatement, Max } // internal strings. string _sAppBase = null; string _sAsmName = null; string _sAsmClass = null; string _sAsmMethod = null; string _sAsmArgs = null; string _sUrl = null; string _sZone = null; string _sManifestPath = null; string _sSecurityStatement = null; Zone _zZone = null; static bool g_fLoadingAssembly = false; static string[] g_sZones = {"MyComputer", "Intranet", "Trusted", "Internet", "Untrusted"}; //---------------------------------------------------------------------------- // ctor //---------------------------------------------------------------------------- public NDPHost() { } //---------------------------------------------------------------------------- // ctor // Not used currently, but possibly useful for CreateInstanceFrom w/args to ctor. //---------------------------------------------------------------------------- public NDPHost(string[] args) { LoadFromStrings(args); } //---------------------------------------------------------------------------- // Main //---------------------------------------------------------------------------- public static void Main(string[] sCmdLine) { NDPHost ndpHost= new NDPHost(); try { ndpHost.ParseCmdLine(sCmdLine); ndpHost.ParseManifest(); ndpHost.LaunchApp(); } catch(ArgumentException e) { if ((e.Message == "Invalid Command Line")) Usage(); else throw(e); } } //---------------------------------------------------------------------------- // LaunchApp //---------------------------------------------------------------------------- public void LaunchApp() { Evidence securityEvidence = null; PolicyLevel appPolicy = null; try { if (GetTMEvidenceAndPolicy(ref securityEvidence, ref appPolicy) != true) { System.Windows.Forms.MessageBox.Show("Application Failed to Run Due to Insufficient Permissions"); Console.WriteLine("Application Failed to Run Due to Insufficient Permissions"); return; } } catch(System.IO.FileNotFoundException fnfe) { Console.WriteLine("==\nBinding to Avalon throws: " + fnfe + "==\n"); // Construct the Evidence object from url, zone. securityEvidence = ConstructEvidence(); // Construct PolicyLevel object from entry file path, Evidence object. appPolicy = ConstructAppPolicy(securityEvidence); securityEvidence = null; // do not set evidence on appdomain } // Run app given entry path, type and PolicyLevel object. ExecuteApplication(appPolicy, securityEvidence); } //---------------------------------------------------------------------------- // ParseCmdLine //---------------------------------------------------------------------------- public void ParseCmdLine(string[] sCmdLine) { Console.WriteLine("\nParsing Comand Line..."); if ((sCmdLine.Length != 2)) throw new ArgumentException("Invalid Command Line"); _sManifestPath = sCmdLine[0]; _sUrl = sCmdLine[1]; Uri appBaseUri = new Uri(_sManifestPath); string localPath = appBaseUri.LocalPath;//AbsolutePath; _sAppBase = System.IO.Path.GetDirectoryName(localPath) + "\\"; _zZone = System.Security.Policy.Zone.CreateFromUrl(_sUrl); _sZone = ZoneToString(_zZone.SecurityZone); Console.WriteLine("\n"); Console.WriteLine("ManifestPath=\t" + _sManifestPath); Console.WriteLine("AppBase=\t" + _sAppBase); Console.WriteLine("Url=\t\t" + _sUrl); Console.WriteLine("Zone=\t\t" + _sZone); } //---------------------------------------------------------------------------- // ParseManifest //---------------------------------------------------------------------------- public void ParseManifest() { Uri sManifestUri = new Uri(_sManifestPath); ApplicationManifestImport ami = new ApplicationManifestImport(sManifestUri); ActivationInfo ai = ami.GetActivationInfo(); SecurityInfo si = ami.GetSecurityInfo(); _sAsmName = ai["assemblyName"]; _sAsmClass = ai["assemblyClass"]; _sAsmMethod = ai["assemblyMethod"]; _sAsmArgs = ai["assemblyMethodArgs"]; if (si != null) _sSecurityStatement = si["Security"]; Console.WriteLine("AsmName=\t" + _sAsmName); Console.WriteLine("Class=\t\t" + _sAsmClass); Console.WriteLine("Method=\t\t" + _sAsmMethod); Console.WriteLine("Args=\t\t" + _sAsmArgs); Console.WriteLine("\n"); Console.WriteLine("Security=\t\t" + _sSecurityStatement); } //---------------------------------------------------------------------------- // GetTMEvidenceAndPolicy //---------------------------------------------------------------------------- public bool GetTMEvidenceAndPolicy(ref Evidence securityEvidence, ref PolicyLevel appPolicy) { SecurityManifest sm = null; if (_sSecurityStatement != null) sm = new SecurityManifest(_sSecurityStatement); else sm = new SecurityManifest(); // setup object for domain specifically to get trust. // for demo hack, securityEvidence = null at first, then set to additional evidence returned. TrustDecision trustDecision = TrustManager.EvaluateTrustRequest(sm, securityEvidence, "file://"+_sAppBase); if (trustDecision.Action == BasicTrustAction.Deny) return false; appPolicy = TrustManager.CreatePolicyLevel(trustDecision); securityEvidence = trustDecision.DomainEvidence; return true; } //---------------------------------------------------------------------------- // ConstructEvidence //---------------------------------------------------------------------------- public Evidence ConstructEvidence() { Console.WriteLine("Constructing Evidence..."); Evidence securityEvidence = new Evidence(); securityEvidence.AddHost(_zZone); if ((new Uri(_sUrl)).IsFile) Console.WriteLine(" Skipping Site evidence for file://"); else securityEvidence.AddHost( System.Security.Policy.Site.CreateFromUrl(_sUrl) ); // bugbug - add the actual url. return securityEvidence; } //---------------------------------------------------------------------------- // ConstructAppPolicy //---------------------------------------------------------------------------- public PolicyLevel ConstructAppPolicy(Evidence securityEvidence) { Console.WriteLine("Constructing App Policy Level..."); // NOTENOTE: not effective if not both url and zone in evidence // Populate the PolicyLevel with code groups that will do the following: // 1) For all assemblies that come from this app's cache directory, // give permissions from retrieved permission set from SecurityManager. // 2) For all other assemblies, give FullTrust permission set. Remember, // since the permissions will be intersected with other policy levels, // just because we grant full trust to all other assemblies does not mean // those assemblies will end up with full trust. PolicyLevel AppPolicy = null; // ResolvePolicy will return a System.Security.PermissionSet PermissionSet AppPerms = SecurityManager.ResolvePolicy(securityEvidence); // Create a new System.Security.Policy.PolicyStatement that does not contain any permissions. PolicyStatement Nada = new PolicyStatement(new PermissionSet(PermissionState.None));//PermissionSet()); // Create a PolicyStatement for the permissions that we want to grant to code from the app directory: PolicyStatement AppStatement = new PolicyStatement(AppPerms); // Create Full trust PolicyStatement so all other code gets full trust, by passing in an _unrestricted_ PermissionSet PolicyStatement FullTrustStatement = new PolicyStatement(new PermissionSet(PermissionState.Unrestricted)); // Create a System.Security.Policy.FirstMatchCodeGroup as the root that matches all // assemblies by supplying an AllMembershipCondition: FirstMatchCodeGroup RootCG = new FirstMatchCodeGroup(new AllMembershipCondition(), Nada); // Create a child UnionCodeGroup to handle the assemblies from the app cache. We do this // by using a UrlMembershipCondition set to the app cache directory: UnionCodeGroup AppCG = new UnionCodeGroup(new UrlMembershipCondition("file://"+_sAppBase+"*"), AppStatement); // Add AppCG to RootCG as first child. This is important because we need it to be evaluated first RootCG.AddChild(AppCG); // Create a second child UnionCodeGroup to handle all other code, by using the AllMembershipCondition again UnionCodeGroup AllCG = new UnionCodeGroup(new AllMembershipCondition(), FullTrustStatement); // Add AllCG to RootCG after AppCG. If AppCG doesnt apply to the assembly, AllCG will. RootCG.AddChild(AllCG); // This will be the PolicyLevel that we will associate with the new AppDomain. AppPolicy = PolicyLevel.CreateAppDomainLevel(); // Set the RootCG as the root code group on the new policy level AppPolicy.RootCodeGroup = RootCG; // NOTENOTE // Code from the site that lives on the local machine will get the reduced permissions as expected. // Dependencies of this app (not under app dir or maybe dependencies that live in the GAC) would still get full trust. // If the full trust dependencies need to do something trusted, they carry the burden of asserting to overcome the stack walk. return AppPolicy; } //---------------------------------------------------------------------------- // ExecuteApplication //---------------------------------------------------------------------------- public void ExecuteApplication(PolicyLevel AppPolicy, Evidence securityEvidence) { Console.WriteLine("Executing Application..."); // setup object for new domain AppDomainSetup appDomainSetup = new AppDomainSetup(); // app base, config file name and friendly name = host name. appDomainSetup.ApplicationBase = _sAppBase; appDomainSetup.ConfigurationFile = GetConfigFileName(); AppDomain dom = AppDomain.CreateDomain(GetHostName(), securityEvidence, appDomainSetup); // Set the policy level on the domain. dom.SetAppDomainPolicy(AppPolicy); // Normal exe case. if (_sAsmMethod == "") { Console.WriteLine("\nRunning ExecuteAssembly in: " + GetExeFilePath()); // bugbug - question security guys on whether or not useful to have evidence passed in. dom.ExecuteAssembly(GetExeFilePath()); } // Library entry case. else { Console.WriteLine("\nRunning "+_sAsmMethod+" in Assembly: "+_sAsmName); // Hosted code metadata must be present in both default app domain // and remote app domain. Load NDPHost assembly into remote domain. AssemblyName asmName = Assembly.GetExecutingAssembly().GetName(); // Instance the NDPHost class with default constructor. ObjectHandle objhNDP = dom.CreateInstanceFrom(asmName.CodeBase, "Microsoft.Fusion.ADF.NDPHost"); // Unwrap the handle. NDPHost objNDP = (NDPHost) objhNDP.Unwrap(); // Get a string array representation of this object in the current app domain. string[] s = this.LoadToStrings(); // Do the real construction in the remote domain. objNDP.LoadFromStrings(s); // Load the assembly resolve handler in the remote domain. objNDP.LoadResolveEventHandler(); // Run the method. objNDP.ExecMethod(); // NOTE: why doesn't the following construction work? // ObjectHandle objhNDP = dom.CreateInstanceFrom(asmName.CodeBase, "NDPHost", true, // BindingFlags.Instance|BindingFlags.Public|BindingFlags.DeclaredOnly, // null, (object[]) s, null, null, null); } } //---------------------------------------------------------------------------- // ExecMethod //---------------------------------------------------------------------------- public void ExecMethod() { new PermissionSet(PermissionState.Unrestricted).Assert(); // Load the assembly. Assembly assembly = Assembly.Load(_sAsmName); // Instance the class. Object objInstance = assembly.CreateInstance(_sAsmClass, false); // Pass args as 0th element of object array. Slight hack // because we canonicalize against app base. Likely should // make first arg equal to appbase, + subsequent args. string sAppEntryPoint = _sAppBase + _sAsmArgs; Object [] objArgs = new Object [] {new String[] {sAppEntryPoint}}; // Retrieve method from class instance. MethodInfo method = objInstance.GetType().GetMethod(_sAsmMethod, BindingFlags.Instance|BindingFlags.Public|BindingFlags.DeclaredOnly); // Invoke the method. try { Object pRet=method.Invoke(objInstance, objArgs); } catch(Exception e) { // bugbug - show base exception text instead. Console.WriteLine(e.ToString()); throw e.GetBaseException(); } } //---------------------------------------------------------------------------- // LoadResolveEventHandler //---------------------------------------------------------------------------- public void LoadResolveEventHandler() { // ISSUE - onDemand download disabled for M2 //AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(OnAssemblyResolve); } //---------------------------------------------------------------------------- // OnAssemblyResolve //---------------------------------------------------------------------------- private Assembly OnAssemblyResolve(Object sender, ResolveEventArgs args) { new PermissionSet(PermissionState.Unrestricted).Assert(); Assembly assembly = null; if (g_fLoadingAssembly) goto done; g_fLoadingAssembly=true; string[] sAssemblyNameParts = args.Name.Split(new Char[] {','}, 2); string sAssemblyName = sAssemblyNameParts[0] + ".dll"; try { GetFile(sAssemblyName); } catch { g_fLoadingAssembly=false; goto done; } try { assembly = Assembly.Load(args.Name); } catch { assembly = null; } finally { g_fLoadingAssembly=false; } done: return assembly; } //---------------------------------------------------------------------------- // GetFile //---------------------------------------------------------------------------- private void GetFile(string name) { HttpWebResponse Response; //Retrieve the File HttpWebRequest Request = (HttpWebRequest)HttpWebRequest.Create(_sUrl + name); try { Response = (HttpWebResponse)Request.GetResponse(); } catch(WebException e) { Console.WriteLine(e.ToString()); throw e; // BUGBUG: apply probing rules? } Stream responseStream = Response.GetResponseStream(); // setup UI // BUGBUG: allow no UI case? // BUGBUG: test with file > 4GB Int64 totalLength = 0; int factor = (int) (Response.ContentLength / int.MaxValue); int max = int.MaxValue; if (factor <= 1) { factor = 1; max = (int) Response.ContentLength; } if (max <= -1) { // no content length returned from the server (-1), // or error (what does < -1 mean?) // no progress, set max to 0 max = 0; } DownloadStatus statusForm = new DownloadStatus(0, max); statusForm.SetStatus(0); statusForm.Show(); // write from stream to disk byte[] buffer = new byte[4096]; int length; try { FileStream AFile = File.Open(_sAppBase+name, FileMode.Create, FileAccess.ReadWrite); length = responseStream.Read(buffer, 0, 4096); while ( length != 0 ) { AFile.Write(buffer, 0, length); if (max != 0) { totalLength += length; statusForm.SetStatus((int) (totalLength/factor)); } length = responseStream.Read(buffer, 0, 4096); // dispatch messages System.Windows.Forms.Application.DoEvents(); } AFile.Close(); statusForm.SetMessage("Download complete"); } catch(Exception e) { statusForm.SetMessage(e.Message); // BUGBUG: AFile may not be closed } responseStream.Close(); //Pause for a moment to show the status dialog in //case the app downloaded extremely quickly (small file?). statusForm.Refresh(); System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1)); statusForm.Close(); } //---------------------------------------------------------------------------- // ValidateParams //---------------------------------------------------------------------------- public void ValidateParams(string[] s) { // appbase, asmname, url required. if (((s[(int) eCmd.AppBase] == "") || (s[(int) eCmd.AsmName] == "") || (s[(int) eCmd.Url] == ""))) throw new ArgumentException("Invalid Parameters"); // if class or method specified, must specify both. if (((s[(int)eCmd.AsmMethod] != "") && (s[(int)eCmd.AsmClass] == "")) || ((s[(int)eCmd.AsmMethod] == "") && (s[(int)eCmd.AsmClass] != ""))) throw new ArgumentException("Invalid Parameters"); } //---------------------------------------------------------------------------- // LoadFromStrings //---------------------------------------------------------------------------- public void LoadFromStrings(string[] s) { ValidateParams(s); _sAppBase = s[(int) eCmd.AppBase]; _sAsmName=s[(int) eCmd.AsmName]; _sAsmClass=s[(int) eCmd.AsmClass]; _sAsmMethod=s[(int) eCmd.AsmMethod]; _sAsmArgs=s[(int) eCmd.AsmArgs]; _sUrl = s[(int) eCmd.Url]; _sZone=s[(int) eCmd.Zone]; _zZone = new Zone((System.Security.SecurityZone)StringToZone(_sZone)); _sManifestPath = s[(int) eCmd.ManifestPath]; _sSecurityStatement = s[(int) eCmd.SecurityStatement]; } //---------------------------------------------------------------------------- // LoadToStrings //---------------------------------------------------------------------------- public string[] LoadToStrings() { string[] s = new string[(int) eCmd.Max]; s[(int) eCmd.AppBase] = _sAppBase; s[(int) eCmd.AsmName]=_sAsmName; s[(int) eCmd.AsmClass]=_sAsmClass; s[(int) eCmd.AsmMethod]=_sAsmMethod; s[(int) eCmd.AsmArgs]=_sAsmArgs; s[(int) eCmd.Url]=_sUrl; s[(int) eCmd.Zone]=_sZone; s[(int) eCmd.ManifestPath] = _sManifestPath; s[(int) eCmd.SecurityStatement] = _sSecurityStatement; return s; } //---------------------------------------------------------------------------- // GetConfigFileName //---------------------------------------------------------------------------- public string GetConfigFileName() { StringBuilder sb = new StringBuilder(); if (_sAsmMethod == "") sb.Append(GetExeFilePath()); else sb.Append(_sAsmArgs); sb.Append(".config"); return sb.ToString(); } //---------------------------------------------------------------------------- // GetExeFilePath //---------------------------------------------------------------------------- public string GetExeFilePath() { StringBuilder sb = new StringBuilder(); string sExe = @"exe$"; Match m = Regex.Match(_sAsmName, sExe, RegexOptions.IgnoreCase); sb.Append(_sAppBase); sb.Append(_sAsmName); if (!m.Success) sb.Append(".exe"); return sb.ToString(); } //---------------------------------------------------------------------------- // GetHostName //---------------------------------------------------------------------------- public string GetHostName() { System.Uri uri = new System.Uri(_sUrl); return uri.Host; } //---------------------------------------------------------------------------- // ZoneToString //---------------------------------------------------------------------------- string ZoneToString(SecurityZone z) { return g_sZones[(int) z]; } //---------------------------------------------------------------------------- // StringToZone //---------------------------------------------------------------------------- SecurityZone StringToZone(string s) { for (int i = 0; i < g_sZones.Length; i++) { if (g_sZones[i] == s) return (SecurityZone) i; } return (SecurityZone) (-1); } //---------------------------------------------------------------------------- // Usage //---------------------------------------------------------------------------- public static void Usage() { Console.WriteLine("--NDPHost Application Launcher--"); Console.WriteLine("NDP Build Version = v1.0.3705\n"); Console.WriteLine("Usage: NDPHost ManifestFileUri, ApplicationBaseUri\n"); Console.WriteLine("Example:\n"); Console.WriteLine("1) To launch application at c:\\foo\\bar\\bar.manifest with permissions based"); Console.WriteLine("on url http://foo/bar/:\n"); Console.WriteLine("NDPHost file://c:/foo/bar/bar.manifest http://foo/bar/"); } } }