File: Core\CSharp\MS\Internal\AppModel\CustomCredentialPolicy.cs
Project: wpf\src\PresentationCore.csproj (PresentationCore)
//-----------------------------------------------------------------------------
//
// <copyright file="CustomCredentialPolicy.cs" company="Microsoft">
//    Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// Description:
//
// This creates and registers a custom ICredentialPolicy that will be used 
// whenever a server sends a 401 UNAUTHORIZED in response to a WebRequest. The 
// .NET framework will call ShouldSendCredential to determine if credentials 
// should be sent. 
//
// Our policy is to go ahead and send whatever credentials there might be, 
// EXCEPT if we are using default credentials and the request is not going to 
// an Intranet, Local Machine or Trusted domain.
//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// IMPORTANT: We are creating an instance of IInternetSecurityManager here. This
// is currently also done in the AppSecurityManager at the Framework level. Any
// modification to either of these classes--especially concerning MapUrlToZone--
// should be considered for both classes.
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// An IInternetSecurityManagerSite is not currently needed here, because the
// only method of IInternetSecurityManager that we are calling is MapUrlToZone,
// and that does not prompt the user.
// 
// History:
//  03/15/2006: Microsoft - Initial creation. 
//-----------------------------------------------------------------------------
 
using System;
using System.Collections.Generic;
using System.Net;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
 
using MS.Internal.PresentationCore;
using MS.Win32;
 
namespace MS.Internal.AppModel
{
    [FriendAccessAllowed]
    internal class CustomCredentialPolicy : ICredentialPolicy
    {
 
        /// <SecurityNote>
        /// Critical - Accesses critical members
        /// PublicOK - Nothing is exposed, and nothing depends on user input. We're just creating objects for use later.
        /// </SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        static CustomCredentialPolicy()
        {
            _lockObj = new object();
            _initialized = false;
        }
 
        /// <SecurityNote>
        /// Critical - Access critical member _environmentPermissionSet
        /// PublicOK - Nothing is exposed, and nothing depends on user input. We're just creating objects for use later.
        /// </SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        public CustomCredentialPolicy()
        {
            _environmentPermissionSet = new PermissionSet(null);
            _environmentPermissionSet.AddPermission(new EnvironmentPermission(EnvironmentPermissionAccess.Read, "USERDOMAIN"));
            _environmentPermissionSet.AddPermission(new EnvironmentPermission(EnvironmentPermissionAccess.Read, "USERNAME"));
        }
 
        /// <SecurityNote>
        /// Critical    - Asserts for permission to set the credential policy.
        /// TreatAsSafe - Nothing is exposed. This method is safe to call at any time.
        /// </SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        static internal void EnsureCustomCredentialPolicy()
        {
            if (!_initialized)
            {
                lock (_lockObj)
                {
                    if (!_initialized)
                    {
                        new SecurityPermission(SecurityPermissionFlag.ControlPolicy).Assert();  // BlessedAssert: 
                        try
                        {
                            // We should allow an application to set its own credential policy, if it has permssion to.
                            // We do not want to overwrite the application's setting. 
                            // Check whether it is already set before setting it. 
                            // The default of this property is null. It demands ControlPolicy permission to be set.
                            if (AuthenticationManager.CredentialPolicy == null)
                            {
                                AuthenticationManager.CredentialPolicy = new CustomCredentialPolicy();
                            }
                            _initialized = true;
                        }
                        finally
                        {
                            CodeAccessPermission.RevertAssert();
                        }
                    }
                }
            }
        }
 
        #region ICredentialPolicy Members
 
        /// <SecurityNote>
        /// Critical    - Calls SecurityCritical method IsDefaultCredentials.
        /// TreatAsSafe - This is called by the framework to determine if credentials should be sent
        ///               in response to the server sending a 401. The challengUri and request are
        ///               coming from the app; the credential and authenticationModule are coming
        ///               from the framework. The only ouput is whether or not credentials should
        ///               be sent, which is not critical.
        /// </SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        public bool ShouldSendCredential(Uri challengeUri, WebRequest request, NetworkCredential credential, IAuthenticationModule authenticationModule)
        {
            switch (MapUrlToZone(challengeUri))
            {
                // Always send credentials (including default credentials) to these zones
                case SecurityZone.Intranet:
                case SecurityZone.Trusted:
                case SecurityZone.MyComputer:
                    return true;
                // Don't send default credentials to any of these zones
                case SecurityZone.Internet:
                case SecurityZone.Untrusted:
                default:
                    return !IsDefaultCredentials(credential);
            }
        }
 
        #endregion
 
        /// <SecurityNote>
        /// Critical - Asserts for permission to examine the user name and password. They are 
        ///            only checked to see if they are non-null, and not exposed, but the 
        ///            fact that default credential are being used and are available might
        ///            something we would not want revealed, which is why it is not TAS.
        /// </SecurityNote>
        [SecurityCritical]
        private bool IsDefaultCredentials(NetworkCredential credential)
        {
            _environmentPermissionSet.Assert();  // BlessedAssert: 
            try
            {
                return credential == CredentialCache.DefaultCredentials;
            }
            finally
            {
                CodeAccessPermission.RevertAssert();
            }
        }
 
        /// <SecurityNote>
        /// Critical    - Call critical method MapUrlToZone.
        /// TreatAsSafe - Returns the zone of the uri, which is not critical.
        /// </SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        internal static SecurityZone MapUrlToZone(Uri uri)
        {
            EnsureSecurityManager();
 
            int targetZone;
            _securityManager.MapUrlToZone(BindUriHelper.UriToString(uri), out targetZone, 0);
 
            // The enum is directly mappable, but taking no chances...
            switch (targetZone)
            {
                case NativeMethods.URLZONE_LOCAL_MACHINE:
                    return SecurityZone.MyComputer;
                case NativeMethods.URLZONE_INTERNET:
                    return SecurityZone.Internet;
                case NativeMethods.URLZONE_INTRANET:
                    return SecurityZone.Intranet;
                case NativeMethods.URLZONE_TRUSTED:
                    return SecurityZone.Trusted;
                case NativeMethods.URLZONE_UNTRUSTED:
                    return SecurityZone.Untrusted;
            }
 
            return SecurityZone.NoZone;
        }
 
        /// <SecurityNote>
        /// Critical    - Accesses critical member _securityManager and calls the SUC'd MapUrlToZone.
        /// TreatAsSafe - Doesn't expose anything, doesn't take user input, safe to call at any time.
        /// </SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        private static void EnsureSecurityManager()
        {
            // IMPORTANT: See comments in header r.e. IInternetSecurityManager
 
            if (_securityManager == null)
            {
                lock (_lockObj)
                {
                    if (_securityManager == null)
                    {
                        _securityManager = (UnsafeNativeMethods.IInternetSecurityManager)new InternetSecurityManager();                        
                    }
                }
            }
        }
 
        [ComImport, ComVisible(false), Guid("7b8a2d94-0ac9-11d1-896c-00c04Fb6bfc4")]
        private class InternetSecurityManager
        {
        }
 
        ///<SecurityNote> 
        /// Critical - requires an elevation to create. 
        ///</SecurityNote> 
        [SecurityCritical]
        private static UnsafeNativeMethods.IInternetSecurityManager _securityManager;
 
        [SecurityCritical]
        private static object _lockObj;
 
        [SecurityCritical]
        private static bool _initialized;
 
        [SecurityCritical]
        PermissionSet _environmentPermissionSet;
    }
}