File: winforms\Managed\System\WinForms\TrustManager.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="TrustManager.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
/*
 */
using System;
using System.Security;
using System.Security.Policy;
using System.Windows.Forms;
using System.Globalization;
using System.Text;
using System.IO;
using System.Collections;
using System.Reflection;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Security.Permissions;
using System.Xml;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Deployment.Internal;
using System.Deployment.Internal.Isolation;
using System.Deployment.Internal.Isolation.Manifest;
using System.Deployment.Internal.CodeSigning;
using Microsoft.Win32;
using System.Runtime.InteropServices;
 
namespace System.Security.Policy
{
    /// <include file='doc\TrustManager.uex' path='docs/doc[@for="TrustManager"]/*' />
    internal class TrustManager : IApplicationTrustManager
    {
        public const string PromptingLevelKeyName = @"Software\Microsoft\.NETFramework\Security\TrustManager\PromptingLevel";
 
        /// <include file='doc\TrustManager.uex' path='docs/doc[@for="TrustManager.TrustManager"]/*' />
        public TrustManager()
        {
        }
 
        // IApplicationTrustManager Implementation
 
        /// <include file='doc\TrustManager.uex' path='docs/doc[@for="TrustManager.IApplicationTrustManager.DetermineApplicationTrust"]/*' />
        [
            HostProtection(UI=true)
        ]
        public ApplicationTrust DetermineApplicationTrust(ActivationContext activationContext, TrustManagerContext trustManagerContext)
        {            
            if (activationContext == null)
            {
                throw new ArgumentNullException("activationContext");
            }
 
            ApplicationSecurityInfo info = new ApplicationSecurityInfo(activationContext);
            ApplicationTrustExtraInfo appTrustExtraInfo = new ApplicationTrustExtraInfo();
            // ISSUE - fix this....
            HostContextInternal hostContextInternal = new HostContextInternal(trustManagerContext);
 
            ICMS cms = (ICMS)InternalActivationContextHelper.GetDeploymentComponentManifest(activationContext);            
 
            ParsedData parsedData = new ParsedData();
            if (ParseManifest(cms, parsedData))
            {
                appTrustExtraInfo.RequestsShellIntegration = parsedData.RequestsShellIntegration; 
            }
 
            string deploymentUrl = GetDeploymentUrl(info);
            string zoneName = GetZoneNameFromDeploymentUrl(deploymentUrl);
            MemoryStream ms;
            PromptsAllowed promptsAllowed;
 
            if (!ExtractManifestContent(cms, out ms))
            {
                // Block prompt
                return BlockingPrompt(activationContext, parsedData, deploymentUrl, info, appTrustExtraInfo, zoneName, AppRequestsBeyondDefaultTrust(info) /*permissionElevationRequired*/);
            }
            
            bool distrustedPublisher, trustedPublisher, noCertificate;
            AnalyzeCertificate(parsedData, ms, out distrustedPublisher, out trustedPublisher, out noCertificate);
 
            /// Check whether application manifest allows to use deployment manifest certificate.
            /// If not then we have to use application manifest certificate instead.
            ICMS applicationCms = (ICMS)InternalActivationContextHelper.GetApplicationComponentManifest(activationContext);
            ParsedData applicationParsedData = new ParsedData();
            if (ParseManifest(applicationCms, applicationParsedData))
            {
                if (applicationParsedData.UseManifestForTrust)
                {
                    MemoryStream applicationMs;
                    if (ExtractManifestContent(applicationCms, out applicationMs))
                    {
                        /// Use the old parsedData.
                        bool applicationDistrustedPublisher, applicationTrustedPublisher, applicationNoCertificate;
                        AnalyzeCertificate(parsedData, applicationMs, out applicationDistrustedPublisher, out applicationTrustedPublisher, out applicationNoCertificate);
                        distrustedPublisher = applicationDistrustedPublisher;
                        trustedPublisher = applicationTrustedPublisher;
                        noCertificate = applicationNoCertificate;
                        parsedData.AppName = applicationParsedData.AppName;
                        parsedData.AppPublisher = applicationParsedData.AppPublisher;
                        parsedData.SupportUrl = applicationParsedData.SupportUrl;
                    }
                }
            }
 
            if (distrustedPublisher)
            {             
                promptsAllowed = GetPromptsAllowed(hostContextInternal, zoneName, parsedData);
                if (promptsAllowed == PromptsAllowed.None)
                {
                    // No prompt allowed, return Do Not Trust.
                    return CreateApplicationTrust(activationContext, info, appTrustExtraInfo, false /*trust*/, false /*persist*/);
                }
                return BlockingPrompt(activationContext, parsedData, deploymentUrl, info, appTrustExtraInfo, zoneName, AppRequestsBeyondDefaultTrust(info) /*permissionElevationRequired*/);
            }            
 
            if (noCertificate)
            {            
                parsedData.AuthenticodedPublisher = null;
                parsedData.Certificate = null;
            }
 
            if (!hostContextInternal.IgnorePersistedDecision)
            {
                // Check if there are previously trusted versions installed.
                ArrayList matchingTrusts;
                if (SearchPreviousTrustedVersion(activationContext, out matchingTrusts))
                {
                    Debug.Assert(matchingTrusts != null && matchingTrusts.Count > 0);
 
                    // Found a matching app, with normally a different version.
                    if (ExistingTrustApplicable(info, matchingTrusts))
                    {
                        // There is at least one old version that requires at the same or more permissions.
                        // ExistingTrustApplicable removed the non-applicable version from the matchingTrusts arrays.
                        Debug.Assert(matchingTrusts != null && matchingTrusts.Count > 0);
 
                        // Check if the new app requires shell integration while none of the old ones did
                        if (appTrustExtraInfo.RequestsShellIntegration &&
                            !SomePreviousTrustedVersionRequiresShellIntegration(matchingTrusts) &&
                            !trustedPublisher)
                        {
                            promptsAllowed = GetPromptsAllowed(hostContextInternal, zoneName, parsedData);
                            switch (promptsAllowed)
                            {
                                case PromptsAllowed.None:
                                    // No prompt allowed, return Do Not Trust.
                                    return CreateApplicationTrust(activationContext, info, appTrustExtraInfo, false /*trust*/, false /*persist*/);
                                case PromptsAllowed.BlockingOnly:
                                    return BlockingPrompt(activationContext, parsedData, deploymentUrl, info, appTrustExtraInfo, zoneName, AppRequestsBeyondDefaultTrust(info) /*permissionElevationRequired*/);
                                case PromptsAllowed.All:
                                    // New app requires shell integration - bring up the Basic Install Prompt
                                    return BasicInstallPrompt(activationContext,
                                                              parsedData, 
                                                              deploymentUrl, 
                                                              hostContextInternal, 
                                                              info, 
                                                              appTrustExtraInfo,
                                                              zoneName, 
                                                              AppRequestsBeyondDefaultTrust(info) /*permissionElevationRequired*/);
                            }
                        }
 
                        // No prompt, return Trust & Persist.
                        return CreateApplicationTrust(activationContext, info, appTrustExtraInfo, true /*trust*/, hostContextInternal.Persist /*persist*/);
                    }
                }
            }            
 
            bool permissionElevationRequired = AppRequestsBeyondDefaultTrust(info);
            if (!permissionElevationRequired || trustedPublisher)
            {             
                if (!trustedPublisher)
                {
                    Debug.Assert(!permissionElevationRequired);
                    promptsAllowed = GetPromptsAllowed(hostContextInternal, zoneName, parsedData);
                    switch (promptsAllowed)
                    {
                        case PromptsAllowed.BlockingOnly:
                            return BlockingPrompt(activationContext, parsedData, deploymentUrl, info, appTrustExtraInfo, zoneName, permissionElevationRequired);
                        case PromptsAllowed.None:
                            // XBaps should also prompt in InternetZone
                            // Originally xbaps were silently trusted, along with other ClickOnce apps
                        case PromptsAllowed.All:
                            // App shell integrates and is not from a trusted deployer, bring up the Basic Install Prompt.
                            return BasicInstallPrompt(activationContext,
                                                      parsedData, 
                                                      deploymentUrl, 
                                                      hostContextInternal, 
                                                      info,
                                                      appTrustExtraInfo,
                                                      zoneName,
                                                      false /*permissionElevationRequired*/);
                    }
                }
                else
                {
                    // App does not shell integrate and does not run in "Internet" zone, or is from a trusted deployer, return Trust
                    return CreateApplicationTrust(activationContext, info, appTrustExtraInfo, true /*trust*/, hostContextInternal.Persist /*persist*/);
                }
            }            
 
            promptsAllowed = GetPromptsAllowed(hostContextInternal, zoneName, parsedData);
            switch (promptsAllowed)
            {
                case PromptsAllowed.None:
                    // No prompt allowed, return Do Not Trust.
                    return CreateApplicationTrust(activationContext, info, appTrustExtraInfo, false /*trust*/, false /*persist*/);
                case PromptsAllowed.BlockingOnly:
                    return BlockingPrompt(activationContext, parsedData, deploymentUrl, info, appTrustExtraInfo, zoneName, true /*permissionElevationRequired*/);
                default: // PromptsAllowed.All:
                    // Bring up the HighRisk Install Prompt if the app shell integrates, or the HighRisk Run Prompt otherwise.
                    return HighRiskPrompt(activationContext, parsedData, deploymentUrl, hostContextInternal, info, appTrustExtraInfo, zoneName);
            }
        }
 
        // ISecurityEncodable implementation
        
        /// <include file='doc\TrustManager.uex' path='docs/doc[@for="TrustManager.ISecurityEncodable.ToXml"]/*' />
        public SecurityElement ToXml()
        {
            SecurityElement elRoot = new SecurityElement("IApplicationTrustManager");
            elRoot.AddAttribute("class", SecurityElement.Escape(this.GetType().AssemblyQualifiedName));
            elRoot.AddAttribute("version", "1");
            return elRoot;
        }
    
        /// <include file='doc\TrustManager.uex' path='docs/doc[@for="TrustManager.ISecurityEncodable.FromXml"]/*' />
        public void FromXml(SecurityElement element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
 
            if (!String.Equals(element.Tag, "IApplicationTrustManager", StringComparison.Ordinal))
            {
                throw new ArgumentException(SR.GetString(SR.TrustManagerBadXml ,"IApplicationTrustManager"));
            }
        }
 
        [
            SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")
        ]
        private static string DefaultBrowserExePath
        {
            get
            {
                try
                {
                    string strBrowserPath = null;
                    new RegistryPermission(PermissionState.Unrestricted).Assert();
                    try 
                    {
                        RegistryKey key = Registry.ClassesRoot.OpenSubKey("http\\shell\\open\\command");
                        if (key != null)
                        {
                            string strBrowserCommand = (string) key.GetValue(string.Empty);
                            key.Close();
 
                            if (strBrowserCommand != null)
                            {
                                strBrowserCommand = strBrowserCommand.Trim();
                                if (strBrowserCommand.Length != 0)
                                {
                                    if (strBrowserCommand[0] == '\"')
                                    {
                                        int closingQuoteIndex = strBrowserCommand.IndexOf('"', 1);
                                        if (closingQuoteIndex != -1)
                                        {
                                            strBrowserPath = strBrowserCommand.Substring(1, closingQuoteIndex - 1);
                                        }
                                    }
                                    else
                                    {
                                        int firstSpaceIndex = strBrowserCommand.IndexOf(' ');
                                        if (firstSpaceIndex != -1)
                                        {
                                            strBrowserPath = strBrowserCommand.Substring(0, firstSpaceIndex);
                                        }
                                        else
                                        {
                                            strBrowserPath = strBrowserCommand;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    finally
                    {
                        System.Security.CodeAccessPermission.RevertAssert();
                    }
                    return strBrowserPath;
                }
                catch (Exception ex)
                {
                    Debug.Fail("Exception occurred while locating default browser executable: " + ex.Message);
                    return null;
                }
            }
        }
 
        private static void ProcessSignerInfo(SignedCmiManifest2 signedManifest, out bool distrustedPublisher, out bool noCertificate)
        {
            Debug.Assert(signedManifest != null);
            Debug.Assert(signedManifest.AuthenticodeSignerInfo != null);
 
            const int TRUST_E_REVOKED = unchecked((int)0x80092010);
            distrustedPublisher = false;
            noCertificate = false;
 
            //
            // AuthenticodeSignerInfo can be null if the manifest is only strong name signed
            // but not authenticode signed. This can happen when the strong name
            // signature is invalid.
            //
            int error = signedManifest.AuthenticodeSignerInfo.ErrorCode;
            if (error == Win32.TRUST_E_EXPLICIT_DISTRUST || error == TRUST_E_REVOKED)
            {
                distrustedPublisher = true;
                return;
            }
            
            if (error == Win32.TRUST_E_SUBJECT_NOT_TRUSTED)
            {
                // Certificate is valid but publisher is not in trusted list
                return;
            }
 
            // No certificate is equivalent to strong name signing only - run app only with user consent
            noCertificate = true;
            return;
        }
 
        [
            SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")
        ]
        private static bool AnalyzeCertificate(ParsedData parsedData, MemoryStream ms, out bool distrustedPublisher, out bool trustedPublisher, out bool noCertificate)
        {
            Debug.Assert(parsedData != null);
            Debug.Assert(ms != null);
 
            distrustedPublisher = false;
            trustedPublisher = false;
            noCertificate = false;
            SignedCmiManifest2 signedManifest = null;
            XmlDocument deploymentManifestXmlDom = null;
            try
            {
                deploymentManifestXmlDom = new XmlDocument();
                deploymentManifestXmlDom.PreserveWhitespace = true;
                deploymentManifestXmlDom.Load(ms);
 
                signedManifest = new SignedCmiManifest2(deploymentManifestXmlDom, true);
 
                signedManifest.Verify(CmiManifestVerifyFlags.None);                
            }
            catch (Exception e)
            {
                if (!(e is CryptographicException))
                {
                    return false;
                }
                if (signedManifest.StrongNameSignerInfo != null && signedManifest.StrongNameSignerInfo.ErrorCode != Win32.TRUST_E_BAD_DIGEST) {
                    if (signedManifest.AuthenticodeSignerInfo == null) 
                    {
                        return false;
                    }
                    ProcessSignerInfo(signedManifest, out distrustedPublisher, out noCertificate);
                    return true;
                }
                // try SHA1 
                try
                {
                    signedManifest = new SignedCmiManifest2(deploymentManifestXmlDom, false);
                    signedManifest.Verify(CmiManifestVerifyFlags.None);
                }
                catch (Exception ex)
                {
                    if (!(ex is CryptographicException)) 
                    {
                        return false;
                    }
 
                    if (signedManifest.AuthenticodeSignerInfo != null) 
                    {
                        ProcessSignerInfo(signedManifest, out distrustedPublisher, out noCertificate);
                        return true;
                    }
                    return false;
                }
                
            }
            finally
            {                
                if (signedManifest != null && 
                    signedManifest.AuthenticodeSignerInfo != null && 
                    signedManifest.AuthenticodeSignerInfo.SignerChain != null)
                {             
                    parsedData.Certificate = signedManifest.AuthenticodeSignerInfo.SignerChain.ChainElements[0].Certificate;
                    parsedData.AuthenticodedPublisher = parsedData.Certificate.GetNameInfo(X509NameType.SimpleName, false);
                }
            }
 
            if (signedManifest == null || signedManifest.AuthenticodeSignerInfo == null)
            {
                noCertificate = true;
            }
            else
            {
                trustedPublisher = true;
            }
 
            return true;
        }
 
        [
            SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")
        ]
        private static bool AppRequestsBeyondDefaultTrust(ApplicationSecurityInfo info)
        {
            Debug.Assert(info != null);
 
            try
            {
                PermissionSet permSetDefaultZone = SecurityManager.GetStandardSandbox(info.ApplicationEvidence);
                PermissionSet permSetRequested = GetRequestedPermissionSet(info);
 
                if (permSetDefaultZone == null && permSetRequested != null)
                {
                    // No permissions are granted, and this launch requests some permissions
                    return true;
                }
                else if (permSetDefaultZone != null && permSetRequested == null)
                {
                    // Some permissions are granted, and this launch does not require any permissions
                    return false;
                }
 
                Debug.Assert(permSetDefaultZone != null);
                Debug.Assert(permSetRequested != null);
 
                return !permSetRequested.IsSubsetOf(permSetDefaultZone);
            }
            catch (Exception)
            {
                return true;
            }
        }
 
        [
            SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")
        ]
        private static ApplicationTrust BasicInstallPrompt(ActivationContext activationContext,
                                                           ParsedData parsedData,
                                                           String deploymentUrl,
                                                           HostContextInternal hostContextInternal, 
                                                           ApplicationSecurityInfo info,
                                                           ApplicationTrustExtraInfo appTrustExtraInfo,
                                                           string zoneName,
                                                           bool permissionElevationRequired)
        {
            DialogResult ret;
            TrustManagerPromptOptions options = CompletePromptOptions(permissionElevationRequired ? TrustManagerPromptOptions.RequiresPermissions : TrustManagerPromptOptions.None, appTrustExtraInfo, zoneName, info);
            try
            {
                TrustManagerPromptUIThread basicInstallDialog = new TrustManagerPromptUIThread(string.IsNullOrEmpty(parsedData.AppName) ? info.ApplicationId.Name : parsedData.AppName,
                                                                                   DefaultBrowserExePath,
                                                                                   parsedData.SupportUrl,
                                                                                   GetHostFromDeploymentUrl(deploymentUrl),
                                                                                   parsedData.AuthenticodedPublisher /*publisherName*/,
                                                                                   parsedData.Certificate,
                                                                                   options);
                ret = basicInstallDialog.ShowDialog();
            }
            catch (Exception ex)
            {
                Debug.Fail("Error occurred while showing basic install dialog: " + ex.Message);
                ret = DialogResult.No;
            }
 
            return CreateApplicationTrust(activationContext, info, appTrustExtraInfo, ret == DialogResult.OK /*trust*/, hostContextInternal.Persist && ret == DialogResult.OK /*persist*/);
        }
 
        private static TrustManagerPromptOptions CompletePromptOptions(TrustManagerPromptOptions options,
                                                                       ApplicationTrustExtraInfo appTrustExtraInfo,
                                                                       string zoneName, 
                                                                       ApplicationSecurityInfo info)
        {
            if (appTrustExtraInfo.RequestsShellIntegration)
            {
                options |= TrustManagerPromptOptions.AddsShortcut;
            }
            if (zoneName != null)
            {
                if (string.Compare(zoneName, "Internet", true, CultureInfo.InvariantCulture) == 0)
                {
                    options |= TrustManagerPromptOptions.InternetSource;
                }
                else if (string.Compare(zoneName, "TrustedSites", true, CultureInfo.InvariantCulture) == 0)
                {
                    options |= TrustManagerPromptOptions.TrustedSitesSource;
                }
                else if (string.Compare(zoneName, "UntrustedSites", true, CultureInfo.InvariantCulture) == 0)
                {
                    options |= TrustManagerPromptOptions.UntrustedSitesSource;
                }
                else if (string.Compare(zoneName, "LocalIntranet", true, CultureInfo.InvariantCulture) == 0)
                {
                    options |= TrustManagerPromptOptions.LocalNetworkSource;
                }
                else if (string.Compare(zoneName, "MyComputer", true, CultureInfo.InvariantCulture) == 0)
                {
                    options |= TrustManagerPromptOptions.LocalComputerSource;
                }
            }
            if (info != null)
            {
                PermissionSet pset = info.DefaultRequestSet;
                if (pset != null && pset.IsUnrestricted())
                {
                    options |= TrustManagerPromptOptions.WillHaveFullTrust;
                }
            }
            return options;
        }
 
        private static ApplicationTrust CreateApplicationTrust(ActivationContext activationContext,
                                                               ApplicationSecurityInfo info,
                                                               ApplicationTrustExtraInfo appTrustExtraInfo,
                                                               bool trust,
                                                               bool persist)
        {
            ApplicationTrust appTrust = new ApplicationTrust(activationContext.Identity);
 
            appTrust.ExtraInfo = appTrustExtraInfo;
            appTrust.IsApplicationTrustedToRun = trust;
            appTrust.DefaultGrantSet = new PolicyStatement(info.DefaultRequestSet, (PolicyStatementAttribute) 0);
            appTrust.Persist = persist;
 
            return appTrust;
        }
 
        private static bool ExistingTrustApplicable(ApplicationSecurityInfo info, 
                                                    ArrayList matchingTrusts)
        {
            Debug.Assert(info != null);
            Debug.Assert(matchingTrusts != null);
 
            int entry = 0;
            while (entry < matchingTrusts.Count)
            {
                ApplicationTrust matchingTrust = (ApplicationTrust) matchingTrusts[entry];
                if (!matchingTrust.IsApplicationTrustedToRun)
                {
                    // Microsoft: Can this ever happen?  I have serious doubts...
                    matchingTrusts.RemoveAt(entry);
                }
 
                PermissionSet permSetRequested = GetRequestedPermissionSet(info);
                PermissionSet permSetGranted = matchingTrust.DefaultGrantSet.PermissionSet; //PolicyStatement makes of copy of its permission set here.
 
                if (permSetGranted == null && permSetRequested != null)
                {
                    // No permissions were granted, and this launch requests some permissions
                    matchingTrusts.RemoveAt(entry);
                }
                else if (permSetGranted != null && permSetRequested == null)
                {
                    // Some permissions were granted, and this launch does not require any permissions
                    entry++;
                    continue;
                }
 
                Debug.Assert(permSetGranted != null);
                Debug.Assert(permSetRequested != null);
 
                if (permSetRequested.IsSubsetOf(permSetGranted))
                {
                    entry++; // Found a version that requires at least as much
                }
                else
                {
                    // This version does not require as much
                    matchingTrusts.RemoveAt(entry);
                }
            }
            return matchingTrusts.Count > 0;
        }
 
        [
            SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")
        ]
        private unsafe static bool ExtractManifestContent(ICMS cms, out MemoryStream ms)
        {
            ms = new MemoryStream();
            try
            {
                System.Runtime.InteropServices.ComTypes.IStream pStream = cms as System.Runtime.InteropServices.ComTypes.IStream;
                if (pStream == null)
                {
                    return false;
                }
 
                byte[] pv = new byte[4096];
                int size = 4096;
                do {
                    pStream.Read(pv, size, new IntPtr(&size));
                    ms.Write(pv, 0, size);
                }
                while (size == 4096);
                ms.Position = 0;
                return true;
            }
            catch (Exception)
            {
                Debug.Fail("Exception occurred in ExtractDeploymentManifestContent.");
                return false;
            }
        }
 
        private static bool IsInternetZone(string zoneName)
        {
            if (string.Compare(zoneName, "Internet", true, CultureInfo.InvariantCulture) == 0)
            {
                return true;
            }
 
            return false;
        }
        
        private static PromptingLevel GetDefaultPromptingLevel(string zoneName)
        {
            PromptingLevel promptingLevel;
            switch (zoneName)
            {
                case "Internet":
                case "LocalIntranet":
                case "MyComputer":
                case "TrustedSites":
                    promptingLevel = PromptingLevel.Prompt;
                    break;
                case "UntrustedSites":
                    promptingLevel = PromptingLevel.Disabled;
                    break;
                default:
                    promptingLevel = PromptingLevel.Disabled;
                    break;
            }
            return promptingLevel;
        }
 
        private static string GetDeploymentUrl(ApplicationSecurityInfo info)
        {
            Debug.Assert(info != null);
            Evidence appEvidence = info.ApplicationEvidence;
 
            Url deploymentUrl = appEvidence.GetHostEvidence<Url>();
            if (deploymentUrl != null)
            {
                return deploymentUrl.Value;
            }
 
            // Couldn't find deployment Url
            return null;
        }
 
        private static PermissionSet GetRequestedPermissionSet(ApplicationSecurityInfo info)
        {
            Debug.Assert(info != null);
 
            PermissionSet pset = info.DefaultRequestSet;
            PermissionSet permSetRequested = null;
            if (pset != null)
            {
                permSetRequested = pset.Copy();
            }
            return permSetRequested;
        }
 
        [
            SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")
        ]
        private static string GetHostFromDeploymentUrl(string deploymentUrl)
        {
            if (deploymentUrl == null)
            { 
                return string.Empty;
            }
            string host = null;
            try
            {
                System.Uri uri = new System.Uri(deploymentUrl);
                if (uri.Scheme == System.Uri.UriSchemeHttp || uri.Scheme == System.Uri.UriSchemeHttps)
                {
                    host = uri.Host;                    
                }
                if (string.IsNullOrEmpty(host))
                {
                    host = uri.AbsolutePath;
                    int separatorIndex = -1;
                    if (string.IsNullOrEmpty(uri.Host) && host.StartsWith("/"))
                    {
                        host = host.TrimStart('/');
                        separatorIndex = host.IndexOf('/');
                    }
                    else if (uri.LocalPath.Length > 2 && (uri.LocalPath[1] == ':' || uri.LocalPath.StartsWith("\\\\")))
                    {
                        host = uri.LocalPath;
                        separatorIndex = host.LastIndexOf('\\');
                    }
                    if (separatorIndex != -1)
                    {
                        host = host.Remove(separatorIndex);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.Fail("Exception occurred in GetHostFromDeploymentUrl: " + ex.Message);
                return string.Empty;
            }
            return host;
        }
 
        private static PromptsAllowed GetPromptsAllowed(HostContextInternal hostContextInternal, 
                                                        string zoneName, 
                                                        ParsedData parsedData)
        {
            Debug.Assert(hostContextInternal != null);
            Debug.Assert(zoneName != null);
            Debug.Assert(parsedData != null);
 
            if (hostContextInternal.NoPrompt)
            {
                return PromptsAllowed.None;
            }
 
            PromptingLevel promptingLevel = GetZonePromptingLevel(zoneName);
            if (promptingLevel == PromptingLevel.Disabled || 
                (promptingLevel == PromptingLevel.PromptOnlyForAuthenticode && parsedData.AuthenticodedPublisher == null))
            {
                return PromptsAllowed.BlockingOnly;
            }
 
            return PromptsAllowed.All;
        }
 
        private static string GetZoneNameFromDeploymentUrl(String deploymentUrl)
        {
            Zone zone = Zone.CreateFromUrl(deploymentUrl);
            if (zone == null || zone.SecurityZone == SecurityZone.NoZone)
            {
                return "UntrustedSites";
            }
            switch (zone.SecurityZone)
            {
                case SecurityZone.Internet:
                    return "Internet";
                case SecurityZone.Intranet:
                    return "LocalIntranet";
                case SecurityZone.MyComputer:
                    return "MyComputer";
                case SecurityZone.Trusted:
                    return "TrustedSites";
                case SecurityZone.Untrusted:
                    return "UntrustedSites";
                default:
                    Debug.Fail("Unexpected SecurityZone in GetZoneNameFromDeploymentUrl");
                    return "UntrustedSites";
            }
        }
 
        [
            SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")
        ]
        private static PromptingLevel GetZonePromptingLevel(string zoneName)
        {
            try
            {
                string promptingLevelStr = null;
                new RegistryPermission(PermissionState.Unrestricted).Assert();
                try 
                {
                    using (RegistryKey key = Registry.LocalMachine.OpenSubKey(PromptingLevelKeyName))
                    {
                        if (key != null) 
                        {
                            promptingLevelStr = (string)key.GetValue(zoneName);
                        }
                    }
                }
                finally
                {
                    System.Security.CodeAccessPermission.RevertAssert();
                }
 
                if (string.IsNullOrEmpty(promptingLevelStr))
                {
                    return GetDefaultPromptingLevel(zoneName);
                }
                else if (string.Compare(promptingLevelStr, "Enabled", true, CultureInfo.InvariantCulture) == 0)
                {
                    return PromptingLevel.Prompt;
                }
                else if (string.Compare(promptingLevelStr, "Disabled", true, CultureInfo.InvariantCulture) == 0)
                {
                    return PromptingLevel.Disabled;
                }
                else if (string.Compare(promptingLevelStr, "AuthenticodeRequired", true, CultureInfo.InvariantCulture) == 0)
                {
                    return PromptingLevel.PromptOnlyForAuthenticode;
                }
                else
                {
                    return GetDefaultPromptingLevel(zoneName);
                }
            }
            catch (Exception ex)
            {
                Debug.Fail("Exception occurred in GetZonePromptingLevel: " + ex.Message);
                return GetDefaultPromptingLevel(zoneName);
            }
        }
 
        [
            SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")
        ]
        private static ApplicationTrust HighRiskPrompt(ActivationContext activationContext,
                                                       ParsedData parsedData,
                                                       String deploymentUrl,
                                                       HostContextInternal hostContextInternal, 
                                                       ApplicationSecurityInfo info, 
                                                       ApplicationTrustExtraInfo appTrustExtraInfo,
                                                       string zoneName)
        {
            DialogResult ret;
            TrustManagerPromptOptions options = CompletePromptOptions(TrustManagerPromptOptions.RequiresPermissions, appTrustExtraInfo, zoneName, info);
            try
            {
                TrustManagerPromptUIThread highRiskDialog = new TrustManagerPromptUIThread(string.IsNullOrEmpty(parsedData.AppName) ? info.ApplicationId.Name : parsedData.AppName,
                                                                               DefaultBrowserExePath,
                                                                               parsedData.SupportUrl, 
                                                                               GetHostFromDeploymentUrl(deploymentUrl),
                                                                               parsedData.AuthenticodedPublisher /*publisherName*/, 
                                                                               parsedData.Certificate,
                                                                               options);
                ret = highRiskDialog.ShowDialog();
            }
            catch (Exception ex)
            {
                Debug.Fail("Error occurred while showing high risk dialog: " + ex.Message);
                ret = DialogResult.No;
            }
 
            return CreateApplicationTrust(activationContext, info, appTrustExtraInfo, ret == DialogResult.OK /*trust*/, hostContextInternal.Persist && ret == DialogResult.OK /*persist*/);
        }
 
        [
            SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")
        ]
        private static ApplicationTrust BlockingPrompt(ActivationContext activationContext,
                                                       ParsedData parsedData,
                                                       String deploymentUrl,
                                                       ApplicationSecurityInfo info,
                                                       ApplicationTrustExtraInfo appTrustExtraInfo,
                                                       string zoneName,
                                                       bool permissionElevationRequired)
        {
            TrustManagerPromptOptions options = CompletePromptOptions(permissionElevationRequired ? (TrustManagerPromptOptions.StopApp | TrustManagerPromptOptions.RequiresPermissions) : TrustManagerPromptOptions.StopApp,
                                                                      appTrustExtraInfo, zoneName, info);
            try
            {
                TrustManagerPromptUIThread errorDialog = new TrustManagerPromptUIThread(string.IsNullOrEmpty(parsedData.AppName) ? info.ApplicationId.Name : parsedData.AppName,
                                                                            DefaultBrowserExePath,
                                                                            parsedData.SupportUrl,
                                                                            GetHostFromDeploymentUrl(deploymentUrl),
                                                                            parsedData.AuthenticodedPublisher /*publisherName*/,
                                                                            parsedData.Certificate,
                                                                            options);
                errorDialog.ShowDialog();
            }
            catch (Exception ex)
            {
                Debug.Fail("Error occurred while showing error dialog: " + ex.Message);
            }
 
            // Trust Manager should prompt, but is not allowed to. Return Don't Trust and Don't Persist.
            return CreateApplicationTrust(activationContext, info, appTrustExtraInfo, false /*trust*/, false /*persist*/);
        }
                
 
        [
            SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")
        ]
        private static bool ParseManifest(ICMS cms, ParsedData parsedData)
        {
            // parsedData is safely initialized to requestsShellIntegration:false/supportUrl:null/authenticodedPublisher:null  //deploymentProviderCodebase:null
            try
            {
                if (cms != null)
                {
                    // Extract SupportUrl and Shell Visibility
                    if (cms.MetadataSectionEntry != null)
                    {
                        IMetadataSectionEntry metaDataSectionEntry = cms.MetadataSectionEntry as IMetadataSectionEntry;
                        if (metaDataSectionEntry != null)
                        {
                            IDescriptionMetadataEntry description = metaDataSectionEntry.DescriptionData;
                            if (description != null)
                            {
                                parsedData.SupportUrl = description.SupportUrl;
                                parsedData.AppName = description.Product;
                                parsedData.AppPublisher = description.Publisher;
                            }
 
                            IDeploymentMetadataEntry deployment = metaDataSectionEntry.DeploymentData;
                            if (deployment != null)
                            {
                                parsedData.RequestsShellIntegration = ((deployment.DeploymentFlags & 
                                    (uint)(CMS_ASSEMBLY_DEPLOYMENT_FLAG.CMS_ASSEMBLY_DEPLOYMENT_FLAG_INSTALL)) != 0);
                                // parsedData.DeploymentProviderCodebase = deployment.DeploymentProviderCodebase;
                            }
 
                            if ((metaDataSectionEntry.ManifestFlags & (uint)CMS_MANIFEST_FLAG.CMS_MANIFEST_FLAG_USEMANIFESTFORTRUST) != 0)
                            {                                
                                parsedData.UseManifestForTrust = true;
                            }
                            else
                            {                             
                                parsedData.UseManifestForTrust = false;
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Debug.Fail("Error parsing manifest: " + e.Message);
                return false;
            }
            return true;
        }
 
        private static bool SomePreviousTrustedVersionRequiresShellIntegration(ArrayList /*ApplicationTrust[]*/ matchingTrusts)
        {
            Debug.Assert(matchingTrusts != null);
            foreach (ApplicationTrust matchingTrust in matchingTrusts)
            {
                ApplicationTrustExtraInfo matchingAppTrustExtraInfo = matchingTrust.ExtraInfo as ApplicationTrustExtraInfo;
                if (matchingAppTrustExtraInfo != null && matchingAppTrustExtraInfo.RequestsShellIntegration)
                {
                    return true;
                }
                if (null == matchingAppTrustExtraInfo && matchingTrust.DefaultGrantSet.PermissionSet.IsUnrestricted())
                {   // Full trust trumps RequestsShellIntegration, so return true in this case.
                    return true;
                }
            }
            // No previous trusted version requires shell integration
            return false;
        }
 
        private static bool SearchPreviousTrustedVersion(ActivationContext activationContext,
                                                         out ArrayList matchingTrusts)
        {
            // No match found by default
            matchingTrusts = null;
 
            ApplicationTrustCollection appTrusts = ApplicationSecurityManager.UserApplicationTrusts;
            foreach (ApplicationTrust appTrust in appTrusts) {
                IDefinitionAppId appTrustAppId = IsolationInterop.AppIdAuthority.TextToDefinition(0, appTrust.ApplicationIdentity.FullName);
                IDefinitionAppId actCtxAppId = IsolationInterop.AppIdAuthority.TextToDefinition(0, activationContext.Identity.FullName);
                if (IsolationInterop.AppIdAuthority.AreDefinitionsEqual((uint) IAPPIDAUTHORITY_ARE_DEFINITIONS_EQUAL_FLAGS.IAPPIDAUTHORITY_ARE_DEFINITIONS_EQUAL_FLAG_IGNORE_VERSION, appTrustAppId, actCtxAppId))
                {
                    // Found an older matching app (or same version app which should never occur in theory) 
                    if (matchingTrusts == null)
                    {
                        matchingTrusts = new ArrayList();
                    }
                    matchingTrusts.Add(appTrust);
                }
            }
 
            // (matchingTrusts != null) <==> Found a prior version that was allowed to run.
            return (matchingTrusts != null);
        }
 
        private enum PromptingLevel
        {
            /// <devdoc>
            ///     <para>
            ///         In the cases the Trust Manager needs to prompt, it needs to show the blocking prompt
            ///     </para>
            /// </devdoc>
            Disabled = 0,
            /// <devdoc>
            ///     <para>
            ///         Prompting is allowed for strong named and authenticode signed application deployments
            ///     </para>
            /// </devdoc>
            Prompt = 1,
            /// <devdoc>
            ///     <para>
            ///         Prompting is allowed for authenticode signed application deployments only
            ///     </para>
            /// </devdoc>
            PromptOnlyForAuthenticode = 2
        }
 
        private enum PromptsAllowed
        {
            /// <devdoc>
            ///     <para>
            ///         The Trust Manager is allowed to show any prompt
            ///     </para>
            /// </devdoc>
            All = 0,
            /// <devdoc>
            ///     <para>
            ///         The Trust Manager is only allowed to show the blocking prompt
            ///     </para>
            /// </devdoc>
            BlockingOnly = 1,
            /// <devdoc>
            ///     <para>
            ///         The Trust Manager is not allowed to prompt at all
            ///     </para>
            /// </devdoc>
            None = 2
        }
    }
 
    [Serializable]
    internal class ApplicationTrustExtraInfo
    {
        private bool requestsShellIntegration = true;  // If manifest parsing fails, we assume shell integration is required.
 
        public ApplicationTrustExtraInfo()
        {
        }
 
        public bool RequestsShellIntegration
        {
            get
            {
                return this.requestsShellIntegration;
            }
 
            set
            {
                this.requestsShellIntegration = value;
            }
        }
    }
 
    internal class TrustManagerPromptUIThread {
        private string m_appName, m_defaultBrowserExePath, m_supportUrl, m_deploymentUrl, m_publisherName;
        private X509Certificate2 m_certificate;
        private TrustManagerPromptOptions m_options;
        private DialogResult m_ret = DialogResult.No;
 
        public TrustManagerPromptUIThread(string appName, string defaultBrowserExePath, string supportUrl, string deploymentUrl,
                                          string publisherName, X509Certificate2 certificate, TrustManagerPromptOptions options) {
            this.m_appName = appName;
            this.m_defaultBrowserExePath = defaultBrowserExePath;
            this.m_supportUrl = supportUrl;
            this.m_deploymentUrl = deploymentUrl;
            this.m_publisherName = publisherName;
            this.m_certificate = certificate;
            this.m_options = options;
        }
 
        public DialogResult ShowDialog() {
            System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(ShowDialogWork));
            thread.SetApartmentState(System.Threading.ApartmentState.STA);
            thread.Start();
            thread.Join();
            return m_ret;
        }
 
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        private void ShowDialogWork()
        {
            try {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                using (TrustManagerPromptUI trustManagerDialog = new TrustManagerPromptUI(this.m_appName, this.m_defaultBrowserExePath,
                                                                                          this.m_supportUrl, this.m_deploymentUrl,
                                                                                          this.m_publisherName, this.m_certificate, this.m_options)) {
                    m_ret = trustManagerDialog.ShowDialog();
                }
            }
            catch {
            }
            finally {
                Application.ExitThread(); //explicitly call Dispose [DevDiv2 bug 184375, OleUnitinialize not being called]
            }
        }
    }
 
    internal class ParsedData
    {
        private bool requestsShellIntegration; /* == false by default. shellVisible attribute is optional */
        private string appName;
        private string appPublisher;
        private string supportUrl;
        private string authenticodedPublisher;
        private bool disallowTrustOverride;
        private X509Certificate2 certificate;
//        private string deploymentProviderCodebase;
 
        public ParsedData()
        {
        }
 
        public bool RequestsShellIntegration
        {
            get
            {
                return this.requestsShellIntegration;
            }
            set
            {
                this.requestsShellIntegration = value;
            }
        }
 
        public X509Certificate2 Certificate
        {
            get
            {
                return this.certificate;
            }
            set
            {
                this.certificate = value;
            }
        }
 
        public string AppName
        {
            get
            {
                return this.appName;
            }
            set
            {
                this.appName = value;
            }
        }
 
        public string AppPublisher
        {
            get
            {
                return this.appPublisher;
            }
            set
            {
                this.appPublisher = value;
            }
        }
 
        public string AuthenticodedPublisher
        {
            get
            {
                return this.authenticodedPublisher;
            }
            set
            {
                this.authenticodedPublisher = value;
            }
        }
 
        public bool UseManifestForTrust
        {
            get
            {
                return disallowTrustOverride;
            }
 
            set
            {
                disallowTrustOverride = value;
            }
        }
 
//        public string DeploymentProviderCodebase
//        {
//            get
//            {
//                return this.deploymentProviderCodebase;
//            }
//            set
//            {
//                this.deploymentProviderCodebase = value;
//            }
//        }
 
        public string SupportUrl
        {
            get
            {
                return this.supportUrl;
            }
            set
            {
                this.supportUrl = value;
            }
        }
    }
 
    internal class HostContextInternal
    {
        private bool ignorePersistedDecision;
        private bool noPrompt;
        private bool persist;
        private ApplicationIdentity previousAppId;
 
        public HostContextInternal(TrustManagerContext trustManagerContext)
        {
            if (trustManagerContext == null)
            {
                // Used in case DetermineApplicationTrust is not given a TrustManagerContext object.
                this.persist = true;
            }
            else
            {
                // ISSUE - fix this...
                // Note that exclusiveGrant is never set. It is read in CreateApplicationTrust however.
                this.ignorePersistedDecision = trustManagerContext.IgnorePersistedDecision;
                this.noPrompt = trustManagerContext.NoPrompt;
                this.persist = trustManagerContext.Persist;
                this.previousAppId = trustManagerContext.PreviousApplicationIdentity;
            }
        }
 
        public bool IgnorePersistedDecision
        { 
            get
            {
                return this.ignorePersistedDecision; 
            }
        }
 
        public bool NoPrompt
        { 
            get
            {
                return this.noPrompt; 
            }
        }
 
        public bool Persist
        { 
            get
            {
                return this.persist; 
            }
        }
 
        public ApplicationIdentity PreviousAppId
        {
            get
            {
                return this.previousAppId;
            }
        }
    }
}