AppSecurityManager.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / MS / Internal / AppModel / AppSecurityManager.cs / 1 / AppSecurityManager.cs

                            //------------------------------------------------------------------------------ 
//  Microsoft Avalon
//  Copyright (c) Microsoft Corporation, 2001
//
//  File:           appsecuritymanager.cs 
//
//  Description:    AppSecurityManager class. 
// 
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// IMPORTANT: We are creating an instance of IInternetSecurityManager here. This 
// is currently also done in the CustomCredentialPolicy at the Core level. Any
// modification to either of these classes--especially concerning MapUrlToZone--
// should be considered for both classes.
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
//
//  History:        04-24-02 - [....] - created 
//----------------------------------------------------------------------------- 

using System; 
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.Security; 
using System.Security.Permissions;
using Microsoft.Win32; 
using System.IO.Packaging; 
using System.Windows;
using System.Windows.Interop; 
using MS.Internal.Utility;
using MS.Internal.PresentationFramework;                   // SecurityHelper
using MS.Win32;
using System.Runtime.InteropServices; 

using MS.Internal.Documents.Application; 
 
namespace MS.Internal.AppModel
{ 

    enum LaunchResult
    {
        Launched, 
        NotLaunched,
        NotLaunchedDueToPrompt 
    }; 

    internal static class AppSecurityManager 
    {
        #region Internal Methods

 
        ///
        ///     Safely launch the browser if you can. 
        ///     If you can't demand unmanaged code permisison. 
        ///
        ///     originatingUri = the current uri 
        ///     destinationUri = the uri you are going to.
        ///
        ///
        /// Critical - gets access to critical resource (uri), calls critical code (launch browser) 
        /// TreatAsSafe because
        ///                     we consider navigates to http or http to the top-level browser as safe. 
        ///                     we consider navigates to mailto as safe. 
        ///                     for all other cases - we demand Unmanaged Code Permission
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal static void SafeLaunchBrowserDemandWhenUnsafe( Uri originatingUri, Uri destinationUri, bool fIsTopLevel )
        {
            LaunchResult launched = LaunchResult.NotLaunched; 

            launched = SafeLaunchBrowserOnlyIfPossible( originatingUri, destinationUri, fIsTopLevel ) ; 
            if ( launched == LaunchResult.NotLaunched ) 
            {
                SecurityHelper.DemandUnmanagedCode(); 
                UnsafeLaunchBrowser( destinationUri );
            }
        }
 
        ///
        /// Safely launch the browser if it's possible to do so in partial trust 
        /// Returns enum indicating whether we safely launched ( or at least think we did). 
        ///
        ///     This function is appropriate for use when we launch the browser from partial trust 
        ///     ( as it doesn't perform demands for the "unsafe" cases )
        ///
        internal static LaunchResult SafeLaunchBrowserOnlyIfPossible(Uri originatingUri, Uri destinationUri, bool fIsTopLevel)
        { 
            return SafeLaunchBrowserOnlyIfPossible(originatingUri, destinationUri, null, fIsTopLevel);
        } 
 
        ///
        /// Safely launch the browser if it's possible to do so in partial trust 
        /// Returns enum indicating whether we safely launched ( or at least think we did).
        /// Html target names can be passed in with this.
        ///     This function is appropriate for use when we launch the browser from partial trust
        ///     ( as it doesn't perform demands for the "unsafe" cases ) 
        ///
        /// 
        /// Critical - gets access to critical resource (uri), calls critical code (launch browser) 
        ///
        /// TreatAsSafe because 
        ///                     we consider navigates to http or http to the top-level browser as safe.
        ///                     we consider navigates to mailto as safe.
        ///
        ///                     for all other cases - we don't launch the browser - and return a result 
        ///                     indicating that we didn't launch.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal static LaunchResult SafeLaunchBrowserOnlyIfPossible(Uri originatingUri, Uri destinationUri, string targetName, bool fIsTopLevel )
        { 
            LaunchResult launched = LaunchResult.NotLaunched ;

            bool isKnownScheme =  (Object.ReferenceEquals(destinationUri.Scheme, Uri.UriSchemeHttp))  ||
                                   (Object.ReferenceEquals(destinationUri.Scheme, Uri.UriSchemeHttps)) || 
                                   destinationUri.IsFile;
 
            bool fIsMailTo = String.Compare(destinationUri.Scheme, Uri.UriSchemeMailto, StringComparison.OrdinalIgnoreCase) == 0 ; 

            // We elevate to navigate the browser iff: 
            //  We are user initiated AND
            //      Scheme == http/https & topLevel OR scheme == mailto.
            //
            // For all other cases ( evil protocols etc). 
            // We will demand.
            // 
            // The check of IsInitialViewerNavigation is necessary because viewer applications will probably 
            // need to call Navigate on the URI they receive, but we want them to be able to do it in partial trust.
            if ( (!BrowserInteropHelper.IsInitialViewerNavigation && SecurityHelper.CallerHasUserInitiatedNavigationPermission()) && 
                  ((fIsTopLevel && isKnownScheme) ||
                     fIsMailTo ) )
            {
                if (isKnownScheme) 
                {
                    IBrowserCallbackServices ibcs = ( Application.Current != null ) ? Application.Current.BrowserCallbackServices : null ; 
                    if (ibcs != null) 
                    {
                        launched = CanNavigateToUrlWithZoneCheck(originatingUri , destinationUri); 
                        if ( launched == LaunchResult.Launched )
                        {
                            // resetting launched to NotLaunched here; if the assert succeeds
                            // and ibcs.DelegateNavigation does not throw then we will set it to Launched. 
                            launched = LaunchResult.NotLaunched;
                            // Browser app. 
                            // 

                            ibcs.DelegateNavigation( BindUriHelper.UriToString( destinationUri ), targetName, GetHeaders(destinationUri)); 

                            launched = LaunchResult.Launched ;
                        }
                    } 
                }
                else if ( fIsMailTo ) // unnecessary if - but being paranoid. 
                { 
                    // Shell-Exec the browser to the mailto url.
                    // assumed safe - because we're only allowing this for mailto urls. 
                    //

                    UnsafeNativeMethods.ShellExecute( new HandleRef( null, IntPtr.Zero) , /*hwnd*/
                                                      null, /*operation*/ 
                                                      BindUriHelper.UriToString( destinationUri ), /*file*/
                                                      null, /*parameters*/ 
                                                      null, /*directory*/ 
                                                      0 ); /*nShowCmd*/
                    launched = LaunchResult.Launched ; 
                }
            }

            return launched ; 
        }
 
        // This invokes the browser unsafely. 
        // Whoever is calling this function should do the right demands.
        ///  
        /// Critical - gets access to critical resource (uri and browsercallback services), calls critical code (launch browser)
        /// 
        [SecurityCritical]
        internal static void UnsafeLaunchBrowser(Uri uri) 
        {
            // This'll likely go into SafeLaunchBrowser() function. 
            if (Application.Current != null) 
            {
                IBrowserCallbackServices ibcs = Application.Current.BrowserCallbackServices; 
                if (ibcs != null)
                {
                    // Browser app.
                    // 

                    ibcs.DelegateNavigation(BindUriHelper.UriToString(uri), null, GetHeaders(uri)); 
                    return; 
                }
            } 

            ShellExecuteDefaultBrowser(uri);
        }
 
        /// 
        /// Opens the default browser for the passed in Uri. 
        ///  
        /// 
        /// Critical - calls critical code (ShellExecuteEx) 
        /// 
        [SecurityCritical]
        internal static void ShellExecuteDefaultBrowser(Uri uri)
        { 
            UnsafeNativeMethods.ShellExecuteInfo sei = new UnsafeNativeMethods.ShellExecuteInfo();
            sei.cbSize = Marshal.SizeOf(sei); 
            sei.fMask = UnsafeNativeMethods.ShellExecuteFlags.SEE_MASK_FLAG_DDEWAIT; 
            /*
            There is a bug on Windows Vista (with IE 7): ShellExecute via SEE_MASK_CLASSNAME fails for an 
            http[s]:// URL. It works fine for file://. The cause appears to be that the DDE command template
            defined in HKCR\IE.AssocFile.HTM\shell\opennew\ddeexec is used: [file://%1",-1,,,,,]. On XP, the
            the key used is (supposedly) HKCR\htmlfile\shell\opennew\ddeexec, and its value is ["%1",,-1,0,,,,].
            The workaround here is to add the SEE_MASK_CLASSNAME flag only for non-HTTP URLs. For HTTP, 
            "plain" ShellExecute just works, incl. with Firefox/Netscape as the default browser.
            */ 
            if (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps) 
            {
                sei.fMask |= UnsafeNativeMethods.ShellExecuteFlags.SEE_MASK_CLASSNAME; 
                sei.lpClass = ".htm"; // The default browser is looked up by this.
            }
            sei.lpFile = uri.ToString(); // It's safe to use Uri.ToString since there's an inheritance demand on it that prevents spoofing by subclasses.
            if (!UnsafeNativeMethods.ShellExecuteEx(sei)) 
                throw new InvalidOperationException(SR.Get(SRID.FailToLaunchDefaultBrowser),
                    new System.ComponentModel.Win32Exception(/*uses the last Win32 error*/)); 
        } 

        #endregion Internal Methods 

        #region Private Methods

        ///  
        /// Returns the HTTP "Referer" header.
        ///  
        /// returns a string containing one or more HTTP headers separated by \r\n; the string must also be terminated with a \r\n 
        private static string GetHeaders(Uri destinationUri)
        { 
            string referer = BindUriHelper.GetReferer(destinationUri);

            if (!String.IsNullOrEmpty(referer))
            { 
                // The headers we pass in to IWebBrowser2.Navigate must
                // be terminated with a \r\n because the browser then 
                // concatenates its own headers on to the end of that string. 
                referer = RefererHeader + referer + "\r\n";
            } 

            return referer;
        }
 
        //
        // Functionally equivalent copy of Trident's CanNavigateToUrlWithZoneCheck function 
        //  Checks to see whether a navigation is considered a zone elevation. 
        //  Once a zone elevation is identified - calls into urlmon to check settings.
        // 
        ///
        ///     Critical - performs elevations to call IsFeatureEnabled, calls EnsureSecurityManager,
        ///                call critical method MUTZ
        /// 
        ///     TreatAsSafe - information disclosed is whether a navigation is "safe" or not.
        ///                   considered ok to give out. you will be able to get this anyway by trapping exceptions 
        ///                   or seeing whether a navigation succeeded/failed. 
        ///
        [SecurityCritical, SecurityTreatAsSafe ] 
        private static LaunchResult CanNavigateToUrlWithZoneCheck( Uri originatingUri, Uri destinationUri )
        {
            LaunchResult launchResult = LaunchResult.NotLaunched ; // fail securely - assume this is the default.
            int targetZone = NativeMethods.URLZONE_LOCAL_MACHINE ; // fail securely this is the most priveleged zone 
            int sourceZone = NativeMethods.URLZONE_INTERNET ; // fail securely this is the least priveleged zone.
            bool fEnabled = true; 
 
            EnsureSecurityManager();
 
            // is this feature enabled ?
            fEnabled  =  UnsafeNativeMethods.CoInternetIsFeatureEnabled(
                                        NativeMethods.FEATURE_ZONE_ELEVATION,
                                        NativeMethods.GET_FEATURE_FROM_PROCESS) != NativeMethods.S_FALSE ; 
            // Get target zone.
            string destinationUriString = BindUriHelper.UriToString( destinationUri ); 
 
            _secMgr.MapUrlToZone( destinationUriString , out targetZone, 0 );
 
            // Get source zone.

            // Initialize sourceUri to null so that source zone defaults to the least privileged zone.
            Uri sourceUri = null; 

            // If the MimeType is not a container, attempt to find sourceUri. 
            // sourceUri should be null for Container cases, since it always assumes 
            // the least privileged zone (InternetZone).
            if (Application.Current.MimeType != MimeType.Document) 
            {
                sourceUri = BrowserInteropHelper.Source;
            }
            else if (destinationUri.IsFile && 
                System.IO.Path.GetExtension(destinationUri.LocalPath)
                    .Equals(DocumentStream.XpsFileExtension, StringComparison.OrdinalIgnoreCase)) 
            { 
                // In this case we know the following:
                //  1) We are currently a Container 
                //  2) The destination is a File and another Container

                // In this case we want to treat the destination as internet too so Container
                // can navigate to other Containers by passing zone checks 
                targetZone = NativeMethods.URLZONE_INTERNET;
            } 
 
            if ( sourceUri != null )
            { 
                string sourceUriString = BindUriHelper.UriToString( sourceUri );

                _secMgr.MapUrlToZone( sourceUriString , out sourceZone, 0 );
            } 
            else
            { 
                // 2 potential ways to get here. 
                //      a) We aren't a fusion hosted app. Assume full-trust.
                //      b) Some bug in hosting caused source Uri to be null. 
                //
                // For a - we will say there is no cross-domain check.
                //     b - we'll assume InternetZone, and use Source.
 
                bool fTrusted = SecurityHelper.CheckUnmanagedCodePermission();
 
                if ( fTrusted ) 
                {
                    return LaunchResult.Launched; 
                }
                else
                {
                    // 
                    //  If we didn't get a SourceUri, we'll assume internet zone.
                    //  And use Source for the uri of origin. 
                    // 
                    //  This isn't quite right - but the sourceUri is only used to show a message to the user.
                    //  Worse case is confusing user experience. ( this uri is not used in the elevation determination). 
                    //

                    sourceZone = NativeMethods.URLZONE_INTERNET ;
                    sourceUri = originatingUri ; 
                }
            } 
 
            // 
            // ------------------------------ 
            // Check if there is a zone elevation.
            // Custom zones would have a higher value that URLZONE_UNTRUSTED, so this solution isn't quite complete.
            // However, we don't know of any product actively using custom zones, so rolling this out.
            // INTRANET and TRUSTED are treated as equals. 
            // ------------------------------
            //  
 
            //
            // Note the negative logic - it first sees to see something is *not* a zone elevation. 
            //
            if (
                    // Even if feature is disabled.
                    // We still block navigation to local machine. 
                    // IF source zone is internet or restricted.
 
                    (!fEnabled && 
                    ((sourceZone != NativeMethods.URLZONE_INTERNET &&
                       sourceZone != NativeMethods.URLZONE_UNTRUSTED) || 
                       targetZone != NativeMethods.URLZONE_LOCAL_MACHINE)) ||

                    // If feature is enabled
                    // It's not a zone elevation if 
                    //     the zones are equal OR
                    //        the zones are both less than restricted and 
                    //            the sourceZone is more trusted than Target OR 
                    //            sourceZone and TargetZone are both Intranet or Trusted
                    // 
                    // per aganjam - Intranet and Trusted are treated as equivalent
                    // as it was a common scenario for IE. ( website on intranet points to trusted site).
                    //
 
                    (fEnabled &&
                    ( 
                        sourceZone == targetZone || 
                        (
                            sourceZone <= NativeMethods.URLZONE_UNTRUSTED && 
                            targetZone <= NativeMethods.URLZONE_UNTRUSTED &&
                            (
                                sourceZone < targetZone ||
                                ( 
                                    (sourceZone == NativeMethods.URLZONE_TRUSTED || sourceZone == NativeMethods.URLZONE_INTRANET) &&
                                    (targetZone == NativeMethods.URLZONE_TRUSTED || targetZone == NativeMethods.URLZONE_INTRANET) 
                                ) 
                            )
                        ) 
                        )))
            {
                // There is no zone elevation. You can launch away !
                return LaunchResult.Launched ; 
            }
 
            launchResult = CheckBlockNavigation( sourceUri , 
                                                  destinationUri,
                                                  fEnabled ) ; 

            return launchResult ;
        }
 
        ///
        ///     Called when we suspect there is a zone elevation. 
        ///     Calls the Urlmon IsFeatureZoneElevationEnabled which may pop UI based on settings. 
        /// functionally equivalent to the BlockNavigation: label in Trident's CanNavigateToUrlWithZoneCheck
        /// 
        ///
        ///     Critical - calls a function that has a SUC on it. ( CoIntenrnetIsFeatureZoneElevationEnabled)
        ///
        [SecurityCritical] 
        private static LaunchResult CheckBlockNavigation(Uri originatingUri, Uri destinationUri, bool fEnabled )
        { 
 
            if (fEnabled)
            { 
                if( UnsafeNativeMethods.CoInternetIsFeatureZoneElevationEnabled(
                            BindUriHelper.UriToString( originatingUri ) ,
                            BindUriHelper.UriToString( destinationUri ) ,
                            _secMgr, 
                            NativeMethods.GET_FEATURE_FROM_PROCESS) == NativeMethods.S_FALSE)
                { 
                    return LaunchResult.Launched; 
                }
                else 
                {
                    if ( IsZoneElevationSettingPrompt( destinationUri ) )
                    {
                        // url action is query, and we got a "no" answer back. 
                        // we can assume user responded No.
                        return LaunchResult.NotLaunchedDueToPrompt ; 
                    } 
                    else
                        return LaunchResult.NotLaunched ; 
                }
            }
            else
            { 
                return LaunchResult.Launched ;
            } 
        } 

        // Is ZoneElevation setting set to prompt ? 
        ///
        ///     Critical - elevates to call ProcessUrlAction.
        ///     TreatAsSafe - information return indicates whether we will prompt for the current zone.
        ///                   considered ok to expose. 
        ///
        [SecurityCritical, SecurityTreatAsSafe] 
        private static bool IsZoneElevationSettingPrompt( Uri target ) 
        {
            Invariant.Assert(_secMgr != null ); 

            // Was this due to a prompt ?

            int policy = NativeMethods.URLPOLICY_DISALLOW ; 

            unsafe 
            { 
                String targetString = BindUriHelper.UriToString( target ) ;
                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); // BlessedAssert: 
                try
                {
                    _secMgr.ProcessUrlAction( targetString,
                                              NativeMethods.URLACTION_FEATURE_ZONE_ELEVATION , 
                                              (byte*) & policy ,
                                              Marshal.SizeOf( typeof (int) ), 
                                              null, 
                                              0,
                                              NativeMethods.PUAF_NOUI, 
                                              0 );
                }
                finally
                { 
                    CodeAccessPermission.RevertAssert();
                } 
            } 

            return ( policy == NativeMethods.URLPOLICY_QUERY ) ; 
        }

        ///
        /// Critical - elevates to call unmanaged code to set the security site. 
        ///
        [SecurityCritical ] 
        private static void EnsureSecurityManager() 
        {
            // IMPORTANT: See comments in header r.e. IInternetSecurityManager 

            if( _secMgr == null )
            {
                lock( _lockObj ) 
                {
                    if ( _secMgr == null ) // null check again - now that we're in the lock. 
                    { 
                          _secMgr = (UnsafeNativeMethods.IInternetSecurityManager) new InternetSecurityManager();
 
                          //
                         // Set the Security Manager Site.
                         // This enables any dialogs popped to be modal to our window.
                         // 
                        _secMgrSite = new SecurityMgrSite();
                        new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); // BlessedAssert: 
                        try 
                        {
                             _secMgr.SetSecuritySite( (NativeMethods.IInternetSecurityMgrSite) _secMgrSite ) ; 
                        }
                        finally
                        {
                            CodeAccessPermission.RevertAssert(); 
                        }
                    } 
                } 
            }
        } 


        ///
        /// Critical - elevates to call SetSecuritySite. 
        /// TreatAsSafe - clearing the security site is considered safe.
        ///               worse that can happen is any urlmon prompts will be non-modal. 
        /// 

        [SecurityCritical, SecurityTreatAsSafe ] 
        internal static void ClearSecurityManager()
        {
            if (_secMgr != null )
            { 
                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();  // BlessedAssert:
                try 
                { 
                    lock ( _lockObj )
                    { 
                        if ( _secMgr != null )
                        {
                            _secMgr.SetSecuritySite( null ) ;
                            _secMgrSite = null ; 
                            _secMgr = null ;
                        } 
                    } 
                }
                finally 
                {
                    CodeAccessPermission.RevertAssert();
                }
 

            } 
        } 

        [ComImport, ComVisible(false), Guid("7b8a2d94-0ac9-11d1-896c-00c04Fb6bfc4")] 
        internal class InternetSecurityManager {

        }
 

 
        #endregion Private Methods 

        #region Private Fields 

        private const string RefererHeader = "Referer: ";

        private const string BrowserOpenCommandLookupKey = "htmlfile\\shell\\open\\command"; 

        // Object to be used for locking.  Using typeof(Util) causes an FxCop 
        // violation DoNotLockOnObjectsWithWeakIdentity 
        private static object _lockObj = new object();
 
        ///
        ///     Critical - requires an elevation to create.
        ///
        [SecurityCritical] 
        private static UnsafeNativeMethods.IInternetSecurityManager _secMgr ;
 
        private static SecurityMgrSite _secMgrSite ; 

        #endregion Private Fields 
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------ 
//  Microsoft Avalon
//  Copyright (c) Microsoft Corporation, 2001
//
//  File:           appsecuritymanager.cs 
//
//  Description:    AppSecurityManager class. 
// 
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// IMPORTANT: We are creating an instance of IInternetSecurityManager here. This 
// is currently also done in the CustomCredentialPolicy at the Core level. Any
// modification to either of these classes--especially concerning MapUrlToZone--
// should be considered for both classes.
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
//
//  History:        04-24-02 - [....] - created 
//----------------------------------------------------------------------------- 

using System; 
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.Security; 
using System.Security.Permissions;
using Microsoft.Win32; 
using System.IO.Packaging; 
using System.Windows;
using System.Windows.Interop; 
using MS.Internal.Utility;
using MS.Internal.PresentationFramework;                   // SecurityHelper
using MS.Win32;
using System.Runtime.InteropServices; 

using MS.Internal.Documents.Application; 
 
namespace MS.Internal.AppModel
{ 

    enum LaunchResult
    {
        Launched, 
        NotLaunched,
        NotLaunchedDueToPrompt 
    }; 

    internal static class AppSecurityManager 
    {
        #region Internal Methods

 
        ///
        ///     Safely launch the browser if you can. 
        ///     If you can't demand unmanaged code permisison. 
        ///
        ///     originatingUri = the current uri 
        ///     destinationUri = the uri you are going to.
        ///
        ///
        /// Critical - gets access to critical resource (uri), calls critical code (launch browser) 
        /// TreatAsSafe because
        ///                     we consider navigates to http or http to the top-level browser as safe. 
        ///                     we consider navigates to mailto as safe. 
        ///                     for all other cases - we demand Unmanaged Code Permission
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal static void SafeLaunchBrowserDemandWhenUnsafe( Uri originatingUri, Uri destinationUri, bool fIsTopLevel )
        {
            LaunchResult launched = LaunchResult.NotLaunched; 

            launched = SafeLaunchBrowserOnlyIfPossible( originatingUri, destinationUri, fIsTopLevel ) ; 
            if ( launched == LaunchResult.NotLaunched ) 
            {
                SecurityHelper.DemandUnmanagedCode(); 
                UnsafeLaunchBrowser( destinationUri );
            }
        }
 
        ///
        /// Safely launch the browser if it's possible to do so in partial trust 
        /// Returns enum indicating whether we safely launched ( or at least think we did). 
        ///
        ///     This function is appropriate for use when we launch the browser from partial trust 
        ///     ( as it doesn't perform demands for the "unsafe" cases )
        ///
        internal static LaunchResult SafeLaunchBrowserOnlyIfPossible(Uri originatingUri, Uri destinationUri, bool fIsTopLevel)
        { 
            return SafeLaunchBrowserOnlyIfPossible(originatingUri, destinationUri, null, fIsTopLevel);
        } 
 
        ///
        /// Safely launch the browser if it's possible to do so in partial trust 
        /// Returns enum indicating whether we safely launched ( or at least think we did).
        /// Html target names can be passed in with this.
        ///     This function is appropriate for use when we launch the browser from partial trust
        ///     ( as it doesn't perform demands for the "unsafe" cases ) 
        ///
        /// 
        /// Critical - gets access to critical resource (uri), calls critical code (launch browser) 
        ///
        /// TreatAsSafe because 
        ///                     we consider navigates to http or http to the top-level browser as safe.
        ///                     we consider navigates to mailto as safe.
        ///
        ///                     for all other cases - we don't launch the browser - and return a result 
        ///                     indicating that we didn't launch.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal static LaunchResult SafeLaunchBrowserOnlyIfPossible(Uri originatingUri, Uri destinationUri, string targetName, bool fIsTopLevel )
        { 
            LaunchResult launched = LaunchResult.NotLaunched ;

            bool isKnownScheme =  (Object.ReferenceEquals(destinationUri.Scheme, Uri.UriSchemeHttp))  ||
                                   (Object.ReferenceEquals(destinationUri.Scheme, Uri.UriSchemeHttps)) || 
                                   destinationUri.IsFile;
 
            bool fIsMailTo = String.Compare(destinationUri.Scheme, Uri.UriSchemeMailto, StringComparison.OrdinalIgnoreCase) == 0 ; 

            // We elevate to navigate the browser iff: 
            //  We are user initiated AND
            //      Scheme == http/https & topLevel OR scheme == mailto.
            //
            // For all other cases ( evil protocols etc). 
            // We will demand.
            // 
            // The check of IsInitialViewerNavigation is necessary because viewer applications will probably 
            // need to call Navigate on the URI they receive, but we want them to be able to do it in partial trust.
            if ( (!BrowserInteropHelper.IsInitialViewerNavigation && SecurityHelper.CallerHasUserInitiatedNavigationPermission()) && 
                  ((fIsTopLevel && isKnownScheme) ||
                     fIsMailTo ) )
            {
                if (isKnownScheme) 
                {
                    IBrowserCallbackServices ibcs = ( Application.Current != null ) ? Application.Current.BrowserCallbackServices : null ; 
                    if (ibcs != null) 
                    {
                        launched = CanNavigateToUrlWithZoneCheck(originatingUri , destinationUri); 
                        if ( launched == LaunchResult.Launched )
                        {
                            // resetting launched to NotLaunched here; if the assert succeeds
                            // and ibcs.DelegateNavigation does not throw then we will set it to Launched. 
                            launched = LaunchResult.NotLaunched;
                            // Browser app. 
                            // 

                            ibcs.DelegateNavigation( BindUriHelper.UriToString( destinationUri ), targetName, GetHeaders(destinationUri)); 

                            launched = LaunchResult.Launched ;
                        }
                    } 
                }
                else if ( fIsMailTo ) // unnecessary if - but being paranoid. 
                { 
                    // Shell-Exec the browser to the mailto url.
                    // assumed safe - because we're only allowing this for mailto urls. 
                    //

                    UnsafeNativeMethods.ShellExecute( new HandleRef( null, IntPtr.Zero) , /*hwnd*/
                                                      null, /*operation*/ 
                                                      BindUriHelper.UriToString( destinationUri ), /*file*/
                                                      null, /*parameters*/ 
                                                      null, /*directory*/ 
                                                      0 ); /*nShowCmd*/
                    launched = LaunchResult.Launched ; 
                }
            }

            return launched ; 
        }
 
        // This invokes the browser unsafely. 
        // Whoever is calling this function should do the right demands.
        ///  
        /// Critical - gets access to critical resource (uri and browsercallback services), calls critical code (launch browser)
        /// 
        [SecurityCritical]
        internal static void UnsafeLaunchBrowser(Uri uri) 
        {
            // This'll likely go into SafeLaunchBrowser() function. 
            if (Application.Current != null) 
            {
                IBrowserCallbackServices ibcs = Application.Current.BrowserCallbackServices; 
                if (ibcs != null)
                {
                    // Browser app.
                    // 

                    ibcs.DelegateNavigation(BindUriHelper.UriToString(uri), null, GetHeaders(uri)); 
                    return; 
                }
            } 

            ShellExecuteDefaultBrowser(uri);
        }
 
        /// 
        /// Opens the default browser for the passed in Uri. 
        ///  
        /// 
        /// Critical - calls critical code (ShellExecuteEx) 
        /// 
        [SecurityCritical]
        internal static void ShellExecuteDefaultBrowser(Uri uri)
        { 
            UnsafeNativeMethods.ShellExecuteInfo sei = new UnsafeNativeMethods.ShellExecuteInfo();
            sei.cbSize = Marshal.SizeOf(sei); 
            sei.fMask = UnsafeNativeMethods.ShellExecuteFlags.SEE_MASK_FLAG_DDEWAIT; 
            /*
            There is a bug on Windows Vista (with IE 7): ShellExecute via SEE_MASK_CLASSNAME fails for an 
            http[s]:// URL. It works fine for file://. The cause appears to be that the DDE command template
            defined in HKCR\IE.AssocFile.HTM\shell\opennew\ddeexec is used: [file://%1",-1,,,,,]. On XP, the
            the key used is (supposedly) HKCR\htmlfile\shell\opennew\ddeexec, and its value is ["%1",,-1,0,,,,].
            The workaround here is to add the SEE_MASK_CLASSNAME flag only for non-HTTP URLs. For HTTP, 
            "plain" ShellExecute just works, incl. with Firefox/Netscape as the default browser.
            */ 
            if (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps) 
            {
                sei.fMask |= UnsafeNativeMethods.ShellExecuteFlags.SEE_MASK_CLASSNAME; 
                sei.lpClass = ".htm"; // The default browser is looked up by this.
            }
            sei.lpFile = uri.ToString(); // It's safe to use Uri.ToString since there's an inheritance demand on it that prevents spoofing by subclasses.
            if (!UnsafeNativeMethods.ShellExecuteEx(sei)) 
                throw new InvalidOperationException(SR.Get(SRID.FailToLaunchDefaultBrowser),
                    new System.ComponentModel.Win32Exception(/*uses the last Win32 error*/)); 
        } 

        #endregion Internal Methods 

        #region Private Methods

        ///  
        /// Returns the HTTP "Referer" header.
        ///  
        /// returns a string containing one or more HTTP headers separated by \r\n; the string must also be terminated with a \r\n 
        private static string GetHeaders(Uri destinationUri)
        { 
            string referer = BindUriHelper.GetReferer(destinationUri);

            if (!String.IsNullOrEmpty(referer))
            { 
                // The headers we pass in to IWebBrowser2.Navigate must
                // be terminated with a \r\n because the browser then 
                // concatenates its own headers on to the end of that string. 
                referer = RefererHeader + referer + "\r\n";
            } 

            return referer;
        }
 
        //
        // Functionally equivalent copy of Trident's CanNavigateToUrlWithZoneCheck function 
        //  Checks to see whether a navigation is considered a zone elevation. 
        //  Once a zone elevation is identified - calls into urlmon to check settings.
        // 
        ///
        ///     Critical - performs elevations to call IsFeatureEnabled, calls EnsureSecurityManager,
        ///                call critical method MUTZ
        /// 
        ///     TreatAsSafe - information disclosed is whether a navigation is "safe" or not.
        ///                   considered ok to give out. you will be able to get this anyway by trapping exceptions 
        ///                   or seeing whether a navigation succeeded/failed. 
        ///
        [SecurityCritical, SecurityTreatAsSafe ] 
        private static LaunchResult CanNavigateToUrlWithZoneCheck( Uri originatingUri, Uri destinationUri )
        {
            LaunchResult launchResult = LaunchResult.NotLaunched ; // fail securely - assume this is the default.
            int targetZone = NativeMethods.URLZONE_LOCAL_MACHINE ; // fail securely this is the most priveleged zone 
            int sourceZone = NativeMethods.URLZONE_INTERNET ; // fail securely this is the least priveleged zone.
            bool fEnabled = true; 
 
            EnsureSecurityManager();
 
            // is this feature enabled ?
            fEnabled  =  UnsafeNativeMethods.CoInternetIsFeatureEnabled(
                                        NativeMethods.FEATURE_ZONE_ELEVATION,
                                        NativeMethods.GET_FEATURE_FROM_PROCESS) != NativeMethods.S_FALSE ; 
            // Get target zone.
            string destinationUriString = BindUriHelper.UriToString( destinationUri ); 
 
            _secMgr.MapUrlToZone( destinationUriString , out targetZone, 0 );
 
            // Get source zone.

            // Initialize sourceUri to null so that source zone defaults to the least privileged zone.
            Uri sourceUri = null; 

            // If the MimeType is not a container, attempt to find sourceUri. 
            // sourceUri should be null for Container cases, since it always assumes 
            // the least privileged zone (InternetZone).
            if (Application.Current.MimeType != MimeType.Document) 
            {
                sourceUri = BrowserInteropHelper.Source;
            }
            else if (destinationUri.IsFile && 
                System.IO.Path.GetExtension(destinationUri.LocalPath)
                    .Equals(DocumentStream.XpsFileExtension, StringComparison.OrdinalIgnoreCase)) 
            { 
                // In this case we know the following:
                //  1) We are currently a Container 
                //  2) The destination is a File and another Container

                // In this case we want to treat the destination as internet too so Container
                // can navigate to other Containers by passing zone checks 
                targetZone = NativeMethods.URLZONE_INTERNET;
            } 
 
            if ( sourceUri != null )
            { 
                string sourceUriString = BindUriHelper.UriToString( sourceUri );

                _secMgr.MapUrlToZone( sourceUriString , out sourceZone, 0 );
            } 
            else
            { 
                // 2 potential ways to get here. 
                //      a) We aren't a fusion hosted app. Assume full-trust.
                //      b) Some bug in hosting caused source Uri to be null. 
                //
                // For a - we will say there is no cross-domain check.
                //     b - we'll assume InternetZone, and use Source.
 
                bool fTrusted = SecurityHelper.CheckUnmanagedCodePermission();
 
                if ( fTrusted ) 
                {
                    return LaunchResult.Launched; 
                }
                else
                {
                    // 
                    //  If we didn't get a SourceUri, we'll assume internet zone.
                    //  And use Source for the uri of origin. 
                    // 
                    //  This isn't quite right - but the sourceUri is only used to show a message to the user.
                    //  Worse case is confusing user experience. ( this uri is not used in the elevation determination). 
                    //

                    sourceZone = NativeMethods.URLZONE_INTERNET ;
                    sourceUri = originatingUri ; 
                }
            } 
 
            // 
            // ------------------------------ 
            // Check if there is a zone elevation.
            // Custom zones would have a higher value that URLZONE_UNTRUSTED, so this solution isn't quite complete.
            // However, we don't know of any product actively using custom zones, so rolling this out.
            // INTRANET and TRUSTED are treated as equals. 
            // ------------------------------
            //  
 
            //
            // Note the negative logic - it first sees to see something is *not* a zone elevation. 
            //
            if (
                    // Even if feature is disabled.
                    // We still block navigation to local machine. 
                    // IF source zone is internet or restricted.
 
                    (!fEnabled && 
                    ((sourceZone != NativeMethods.URLZONE_INTERNET &&
                       sourceZone != NativeMethods.URLZONE_UNTRUSTED) || 
                       targetZone != NativeMethods.URLZONE_LOCAL_MACHINE)) ||

                    // If feature is enabled
                    // It's not a zone elevation if 
                    //     the zones are equal OR
                    //        the zones are both less than restricted and 
                    //            the sourceZone is more trusted than Target OR 
                    //            sourceZone and TargetZone are both Intranet or Trusted
                    // 
                    // per aganjam - Intranet and Trusted are treated as equivalent
                    // as it was a common scenario for IE. ( website on intranet points to trusted site).
                    //
 
                    (fEnabled &&
                    ( 
                        sourceZone == targetZone || 
                        (
                            sourceZone <= NativeMethods.URLZONE_UNTRUSTED && 
                            targetZone <= NativeMethods.URLZONE_UNTRUSTED &&
                            (
                                sourceZone < targetZone ||
                                ( 
                                    (sourceZone == NativeMethods.URLZONE_TRUSTED || sourceZone == NativeMethods.URLZONE_INTRANET) &&
                                    (targetZone == NativeMethods.URLZONE_TRUSTED || targetZone == NativeMethods.URLZONE_INTRANET) 
                                ) 
                            )
                        ) 
                        )))
            {
                // There is no zone elevation. You can launch away !
                return LaunchResult.Launched ; 
            }
 
            launchResult = CheckBlockNavigation( sourceUri , 
                                                  destinationUri,
                                                  fEnabled ) ; 

            return launchResult ;
        }
 
        ///
        ///     Called when we suspect there is a zone elevation. 
        ///     Calls the Urlmon IsFeatureZoneElevationEnabled which may pop UI based on settings. 
        /// functionally equivalent to the BlockNavigation: label in Trident's CanNavigateToUrlWithZoneCheck
        /// 
        ///
        ///     Critical - calls a function that has a SUC on it. ( CoIntenrnetIsFeatureZoneElevationEnabled)
        ///
        [SecurityCritical] 
        private static LaunchResult CheckBlockNavigation(Uri originatingUri, Uri destinationUri, bool fEnabled )
        { 
 
            if (fEnabled)
            { 
                if( UnsafeNativeMethods.CoInternetIsFeatureZoneElevationEnabled(
                            BindUriHelper.UriToString( originatingUri ) ,
                            BindUriHelper.UriToString( destinationUri ) ,
                            _secMgr, 
                            NativeMethods.GET_FEATURE_FROM_PROCESS) == NativeMethods.S_FALSE)
                { 
                    return LaunchResult.Launched; 
                }
                else 
                {
                    if ( IsZoneElevationSettingPrompt( destinationUri ) )
                    {
                        // url action is query, and we got a "no" answer back. 
                        // we can assume user responded No.
                        return LaunchResult.NotLaunchedDueToPrompt ; 
                    } 
                    else
                        return LaunchResult.NotLaunched ; 
                }
            }
            else
            { 
                return LaunchResult.Launched ;
            } 
        } 

        // Is ZoneElevation setting set to prompt ? 
        ///
        ///     Critical - elevates to call ProcessUrlAction.
        ///     TreatAsSafe - information return indicates whether we will prompt for the current zone.
        ///                   considered ok to expose. 
        ///
        [SecurityCritical, SecurityTreatAsSafe] 
        private static bool IsZoneElevationSettingPrompt( Uri target ) 
        {
            Invariant.Assert(_secMgr != null ); 

            // Was this due to a prompt ?

            int policy = NativeMethods.URLPOLICY_DISALLOW ; 

            unsafe 
            { 
                String targetString = BindUriHelper.UriToString( target ) ;
                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); // BlessedAssert: 
                try
                {
                    _secMgr.ProcessUrlAction( targetString,
                                              NativeMethods.URLACTION_FEATURE_ZONE_ELEVATION , 
                                              (byte*) & policy ,
                                              Marshal.SizeOf( typeof (int) ), 
                                              null, 
                                              0,
                                              NativeMethods.PUAF_NOUI, 
                                              0 );
                }
                finally
                { 
                    CodeAccessPermission.RevertAssert();
                } 
            } 

            return ( policy == NativeMethods.URLPOLICY_QUERY ) ; 
        }

        ///
        /// Critical - elevates to call unmanaged code to set the security site. 
        ///
        [SecurityCritical ] 
        private static void EnsureSecurityManager() 
        {
            // IMPORTANT: See comments in header r.e. IInternetSecurityManager 

            if( _secMgr == null )
            {
                lock( _lockObj ) 
                {
                    if ( _secMgr == null ) // null check again - now that we're in the lock. 
                    { 
                          _secMgr = (UnsafeNativeMethods.IInternetSecurityManager) new InternetSecurityManager();
 
                          //
                         // Set the Security Manager Site.
                         // This enables any dialogs popped to be modal to our window.
                         // 
                        _secMgrSite = new SecurityMgrSite();
                        new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); // BlessedAssert: 
                        try 
                        {
                             _secMgr.SetSecuritySite( (NativeMethods.IInternetSecurityMgrSite) _secMgrSite ) ; 
                        }
                        finally
                        {
                            CodeAccessPermission.RevertAssert(); 
                        }
                    } 
                } 
            }
        } 


        ///
        /// Critical - elevates to call SetSecuritySite. 
        /// TreatAsSafe - clearing the security site is considered safe.
        ///               worse that can happen is any urlmon prompts will be non-modal. 
        /// 

        [SecurityCritical, SecurityTreatAsSafe ] 
        internal static void ClearSecurityManager()
        {
            if (_secMgr != null )
            { 
                new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();  // BlessedAssert:
                try 
                { 
                    lock ( _lockObj )
                    { 
                        if ( _secMgr != null )
                        {
                            _secMgr.SetSecuritySite( null ) ;
                            _secMgrSite = null ; 
                            _secMgr = null ;
                        } 
                    } 
                }
                finally 
                {
                    CodeAccessPermission.RevertAssert();
                }
 

            } 
        } 

        [ComImport, ComVisible(false), Guid("7b8a2d94-0ac9-11d1-896c-00c04Fb6bfc4")] 
        internal class InternetSecurityManager {

        }
 

 
        #endregion Private Methods 

        #region Private Fields 

        private const string RefererHeader = "Referer: ";

        private const string BrowserOpenCommandLookupKey = "htmlfile\\shell\\open\\command"; 

        // Object to be used for locking.  Using typeof(Util) causes an FxCop 
        // violation DoNotLockOnObjectsWithWeakIdentity 
        private static object _lockObj = new object();
 
        ///
        ///     Critical - requires an elevation to create.
        ///
        [SecurityCritical] 
        private static UnsafeNativeMethods.IInternetSecurityManager _secMgr ;
 
        private static SecurityMgrSite _secMgrSite ; 

        #endregion Private Fields 
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK