Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / MS / Internal / AppModel / AppSecurityManager.cs / 1407647 / 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.Win32; using System.Runtime.InteropServices; using MS.Internal.Documents.Application; using SecurityHelper=MS.Internal.SecurityHelper; 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 && MS.Internal.PresentationFramework.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, string targetFrame = null) { // This'll likely go into SafeLaunchBrowser() function. if (Application.Current != null && Application.Current.CheckAccess()) { IBrowserCallbackServices ibcs = Application.Current.BrowserCallbackServices; if (ibcs != null) { // Browser app. // ibcs.DelegateNavigation(BindUriHelper.UriToString(uri), targetFrame, 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; 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 ; targetZone = MapUrlToZone(destinationUri); // 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 ) { sourceZone = MapUrlToZone(sourceUri); } 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. /// The SecurityManager is a critical resource. /// Safe: The Security Manager is used only within this class (not exposed). Just creating it has /// no observable side effects. /// [SecurityCritical, SecurityTreatAsSafe] 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(); } } } ////// Critical - Calls the COM method. A URL's security zone is not a big secret, and in most cases it /// can be inferred by just parsing the URL, but it's still information obtained under elevation, /// and thus we shouldn't leak it without a good reason. /// [SecurityCritical] internal static int MapUrlToZone(Uri url) { EnsureSecurityManager(); int zone; _secMgr.MapUrlToZone(BindUriHelper.UriToString(url), out zone, 0); return zone; } [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.Win32; using System.Runtime.InteropServices; using MS.Internal.Documents.Application; using SecurityHelper=MS.Internal.SecurityHelper; 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 && MS.Internal.PresentationFramework.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, string targetFrame = null) { // This'll likely go into SafeLaunchBrowser() function. if (Application.Current != null && Application.Current.CheckAccess()) { IBrowserCallbackServices ibcs = Application.Current.BrowserCallbackServices; if (ibcs != null) { // Browser app. // ibcs.DelegateNavigation(BindUriHelper.UriToString(uri), targetFrame, 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; 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 ; targetZone = MapUrlToZone(destinationUri); // 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 ) { sourceZone = MapUrlToZone(sourceUri); } 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. /// The SecurityManager is a critical resource. /// Safe: The Security Manager is used only within this class (not exposed). Just creating it has /// no observable side effects. /// [SecurityCritical, SecurityTreatAsSafe] 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(); } } } ////// Critical - Calls the COM method. A URL's security zone is not a big secret, and in most cases it /// can be inferred by just parsing the URL, but it's still information obtained under elevation, /// and thus we shouldn't leak it without a good reason. /// [SecurityCritical] internal static int MapUrlToZone(Uri url) { EnsureSecurityManager(); int zone; _secMgr.MapUrlToZone(BindUriHelper.UriToString(url), out zone, 0); return zone; } [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

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- PageAction.cs
- SessionIDManager.cs
- IgnoreFlushAndCloseStream.cs
- AlignmentXValidation.cs
- SafeNativeMethods.cs
- TableRowCollection.cs
- LinqDataSourceStatusEventArgs.cs
- TreeWalker.cs
- HtmlInputControl.cs
- Trace.cs
- FontUnitConverter.cs
- WebPartMovingEventArgs.cs
- TypePropertyEditor.cs
- ManagementObjectCollection.cs
- PersonalizationEntry.cs
- TextBox.cs
- TextBoxBase.cs
- StringStorage.cs
- IisTraceWebEventProvider.cs
- CurrentChangedEventManager.cs
- EntityPropertyMappingAttribute.cs
- WebServiceData.cs
- TrackingMemoryStream.cs
- CharUnicodeInfo.cs
- SystemInfo.cs
- Memoizer.cs
- CodePrimitiveExpression.cs
- LoginDesignerUtil.cs
- XmlSchemaObjectCollection.cs
- ChangePassword.cs
- DefaultTypeArgumentAttribute.cs
- ProfileSettingsCollection.cs
- PerformanceCounter.cs
- DbConnectionPoolIdentity.cs
- DataGridViewTextBoxEditingControl.cs
- AppDomain.cs
- TransformProviderWrapper.cs
- HealthMonitoringSection.cs
- SoapElementAttribute.cs
- KeyEvent.cs
- VerificationException.cs
- FullTextLine.cs
- unitconverter.cs
- XmlSchemaType.cs
- CommunicationObject.cs
- SelfSignedCertificate.cs
- ThreadNeutralSemaphore.cs
- PreservationFileWriter.cs
- SelectedCellsChangedEventArgs.cs
- XmlNode.cs
- GC.cs
- ReaderOutput.cs
- PolicyException.cs
- FunctionMappingTranslator.cs
- DtcInterfaces.cs
- Win32SafeHandles.cs
- Unit.cs
- SqlDataRecord.cs
- GridViewRowPresenter.cs
- SystemInformation.cs
- UInt16.cs
- XhtmlMobileTextWriter.cs
- FilterElement.cs
- COM2PropertyDescriptor.cs
- BaseParaClient.cs
- TCEAdapterGenerator.cs
- FixedSOMTableCell.cs
- NCryptSafeHandles.cs
- AspCompat.cs
- TagPrefixInfo.cs
- ResponseBodyWriter.cs
- UnsafeNativeMethods.cs
- Process.cs
- TextRangeSerialization.cs
- PathData.cs
- SessionStateItemCollection.cs
- DateTimeFormatInfoScanner.cs
- NativeCppClassAttribute.cs
- AttributeData.cs
- BuildResultCache.cs
- RepeaterItem.cs
- Constants.cs
- MemberNameValidator.cs
- InputLanguage.cs
- WinFormsSecurity.cs
- WebReferenceOptions.cs
- FilteredDataSetHelper.cs
- mil_commands.cs
- HierarchicalDataBoundControl.cs
- WFItemsToSpacerVisibility.cs
- Comparer.cs
- UpdateException.cs
- Stylus.cs
- MarkerProperties.cs
- SecureConversationServiceCredential.cs
- OleDbConnectionPoolGroupProviderInfo.cs
- SByteConverter.cs
- MsmqIntegrationSecurityMode.cs
- ToolboxItemAttribute.cs
- MarkupCompilePass1.cs