FormsAuthentication.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / xsp / System / Web / Security / FormsAuthentication.cs / 1305376 / FormsAuthentication.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

/* 
 * FormsAuthentication class 
 *
 * Copyright (c) 1999 Microsoft Corporation 
 */

namespace System.Web.Security {
    using System; 
    using System.Web;
    using System.Text; 
    using System.Web.Configuration; 
    using System.Web.Caching;
    using System.Collections; 
    using System.Web.Util;
    using System.Security.Cryptography;
    using System.Security.Principal;
    using System.Threading; 
    using System.Globalization;
    using System.Security.Permissions; 
    using System.Web.Management; 
    using System.Collections.Specialized;
    using System.Web.Compilation; 



    ///  
    ///    This class consists of static methods that
    ///    provides helper utilities for manipulating authentication tickets. 
    ///  
    public sealed class FormsAuthentication {
        private const int MAX_TICKET_LENGTH = 4096; 
        private static object _lockObject = new object();
        internal const string RETURN_URL = "ReturnUrl";

        public FormsAuthentication() { } 

 		///////////////////////////////////////////////////////////////////////////// 
        ///////////////////////////////////////////////////////////////////////////// 
        /////////////////////////////////////////////////////////////////////////////
        // Helper functions: Hash a password 

        /// 
        ///    Initializes FormsAuthentication by reading
        ///    configuration and getting the cookie values and encryption keys for the given 
        ///    application.
        ///  
        public static String HashPasswordForStoringInConfigFile(String password, String passwordFormat) { 
            if (password == null) {
                throw new ArgumentNullException("password"); 
            }
            if (passwordFormat == null) {
                throw new ArgumentNullException("passwordFormat");
            } 
            HashAlgorithm hashAlgorithm;
            if (StringUtil.EqualsIgnoreCase(passwordFormat, "sha1")) 
                hashAlgorithm = SHA1.Create(); 
            else if (StringUtil.EqualsIgnoreCase(passwordFormat, "md5"))
                hashAlgorithm = MD5.Create(); 
            else
                throw new ArgumentException(SR.GetString(SR.InvalidArgumentValue, "passwordFormat"));

            return MachineKeySection.ByteArrayToHexString(hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(password)), 0); 
        }
 
        ///////////////////////////////////////////////////////////////////////////// 
        /////////////////////////////////////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////// 
        // Initialize this

        /// 
        ///    Initializes FormsAuthentication by reading 
        ///    configuration and getting the cookie values and encryption keys for the given
        ///    application. 
        ///  
        public static void Initialize() {
            if (_Initialized) 
                return;

            lock(_lockObject) {
                if (_Initialized) 
                    return;
 
                AuthenticationSection settings = RuntimeConfig.GetAppConfig().Authentication; 
                settings.ValidateAuthenticationMode();
                _FormsName = settings.Forms.Name; 
                _RequireSSL = settings.Forms.RequireSSL;
                _SlidingExpiration = settings.Forms.SlidingExpiration;
                if (_FormsName == null)
                    _FormsName = CONFIG_DEFAULT_COOKIE; 

                _Protection = settings.Forms.Protection; 
                _Timeout = (int) settings.Forms.Timeout.TotalMinutes; 
                _FormsCookiePath = settings.Forms.Path;
                _LoginUrl = settings.Forms.LoginUrl; 
                if (_LoginUrl == null)
                    _LoginUrl = "login.aspx";
                _DefaultUrl = settings.Forms.DefaultUrl;
                if (_DefaultUrl == null) 
                    _DefaultUrl = "default.aspx";
                _CookieMode = settings.Forms.Cookieless; 
                _CookieDomain = settings.Forms.Domain; 
                _EnableCrossAppRedirects = settings.Forms.EnableCrossAppRedirects;
                _TicketCompatibilityMode = settings.Forms.TicketCompatibilityMode; 

                _Initialized = true;
            }
        } 

        ///////////////////////////////////////////////////////////////////////////// 
        ///////////////////////////////////////////////////////////////////////////// 
        /////////////////////////////////////////////////////////////////////////////
        // Decrypt and get the auth ticket 

        /// 
        ///    Given an encrypted authenitcation ticket as
        ///       obtained from an HTTP cookie, this method returns an instance of a 
        ///       FormsAuthenticationTicket class.
        ///  
        public static FormsAuthenticationTicket Decrypt(string encryptedTicket) { 
            if (String.IsNullOrEmpty(encryptedTicket) || encryptedTicket.Length > MAX_TICKET_LENGTH)
                throw new ArgumentException(SR.GetString(SR.InvalidArgumentValue, "encryptedTicket")); 

            Initialize();
            byte[] bBlob = null;
            if ((encryptedTicket.Length % 2) == 0) { // Could be a hex string 
                try {
                    bBlob = MachineKeySection.HexStringToByteArray(encryptedTicket); 
                } catch { } 
            }
            if (bBlob == null) 
                bBlob = HttpServerUtility.UrlTokenDecode(encryptedTicket);
            if (bBlob == null || bBlob.Length < 1)
                throw new ArgumentException(SR.GetString(SR.InvalidArgumentValue, "encryptedTicket"));
 
            if (_Protection == FormsProtectionEnum.All || _Protection == FormsProtectionEnum.Encryption)
            { 
                // DevDiv Bugs 137864: Include a random IV if under the right compat mode 
                // for improved encryption semantics
                bBlob = MachineKeySection.EncryptOrDecryptData(false, bBlob, null, 0, bBlob.Length, false, false, IVType.Random); 
                if (bBlob == null)
                    return null;
            }
 
            int ticketLength = bBlob.Length;
 
            if (_Protection == FormsProtectionEnum.All || _Protection == FormsProtectionEnum.Validation) 
            {
                if (!MachineKeySection.VerifyHashedData(bBlob)) 
                    return null;
                ticketLength -= MachineKeySection.HashSize;
            }
 
            //////////////////////////////////////////////////////////////////////
            // Step 4: Change binary ticket to managed struct 
            int iSize = ((ticketLength > MAX_TICKET_LENGTH) ? MAX_TICKET_LENGTH : ticketLength); 
            StringBuilder     name = new StringBuilder(iSize);
            StringBuilder     data = new StringBuilder(iSize); 
            StringBuilder     path = new StringBuilder(iSize);
            byte []           internalData = null;
            byte []           pBin = new byte[4];
            long []           pDates = new long[2]; 
            int               iRet;
 
            // FEATURE_PAL: replace Forms Authentication ticket parsing 
            // functionality normally found in webengine.dll
#if !FEATURE_PAL 
            if (TicketCompatibilityMode == TicketCompatibilityMode.Framework20) {
                iRet = UnsafeNativeMethods.CookieAuthParseTicket(bBlob, ticketLength,
                                                                   name, iSize,
                                                                   data, iSize, 
                                                                   path, iSize,
                                                                   pBin, pDates); 
            } 
            else {
                // Initial offset after random bits 
                int offset = 8;
                int byteCount = 0;
                // Get the version
                pBin[0] = bBlob[offset]; 
                offset += 1;
                // Verify the null pad 
                if (offset + 3 > ticketLength) { 
                    return null;
                } 
                if ((bBlob[offset] != 0) ||
                    (bBlob[offset + 1] != 0) ||
                    (bBlob[offset + 2] != 0)) {
                    return null; 
                }
                offset += 3; 
                // Copy the name 
                if (offset + 4 > ticketLength) {
                    return null; 
                }
                byteCount = BitConverter.ToInt32(bBlob, offset);
                offset += 4;
                if (offset + byteCount > ticketLength) { 
                    return null;
                } 
                name.Append(Encoding.Unicode.GetChars(bBlob, offset, byteCount)); 
                offset += byteCount;
                // Copy the issue date 
                if (offset + 8 > ticketLength) {
                    return null;
                }
                pDates[0] = BitConverter.ToInt64(bBlob, offset); 
                offset += 8;
                // Copy the IsPersistent byte 
                if (offset + 1 > ticketLength) { 
                    return null;
                } 
                pBin[1] = bBlob[offset];
                offset += 1;
                // Copy the expires date
                if (offset + 8 > ticketLength) { 
                    return null;
                } 
                pDates[1] = BitConverter.ToInt64(bBlob, offset); 
                offset += 8;
                // Copy the internal version 
                if (offset + 1 > ticketLength) {
                    return null;
                }
                pBin[2] = bBlob[offset]; 
                offset += 1;
                // Copy the internal data 
                int internalDataSize = 0; 
                if (offset + 4 > ticketLength) {
                    return null; 
                }
                internalDataSize = BitConverter.ToInt32(bBlob, offset);
                offset += 4;
                if (internalDataSize > 0) { 
                    if (offset + internalDataSize > ticketLength) {
                        return null; 
                    } 
                    internalData = new byte[internalDataSize];
                    Buffer.BlockCopy(bBlob, offset, internalData, 0, internalDataSize); 
                }
                offset += internalDataSize;
                // Copy the user data
                if (offset + 4 > ticketLength) { 
                    return null;
                } 
                byteCount = BitConverter.ToInt32(bBlob, offset); 
                offset += 4;
                if (offset + byteCount > ticketLength) { 
                    return null;
                }
                data.Append(Encoding.Unicode.GetChars(bBlob, offset, byteCount));
                offset += byteCount; 
                // Copy the path
                if (offset + 4 > ticketLength) { 
                    return null; 
                }
                byteCount = BitConverter.ToInt32(bBlob, offset); 
                offset += 4;
                if (offset + byteCount > ticketLength) {
                    return null;
                } 
                path.Append(Encoding.Unicode.GetChars(bBlob, offset, byteCount));
                offset += byteCount; 
                iRet = 0; 
            }
#else // !FEATURE_PAL 
            int iRet = FormsAuthCoriolis.CookieAuthParseTicket(bBlob,
              ticketLength, name, data, path, pBin, pDates);

#endif // !FEATURE_PAL 

            if (iRet != 0) 
                return null; 

            DateTime dt1; 
            DateTime dt2;
            if (TicketCompatibilityMode == TicketCompatibilityMode.Framework20) {
                dt1 = DateTime.FromFileTime(pDates[0]);
                dt2 = DateTime.FromFileTime(pDates[1]); 
            }
            else { 
                dt1 = DateTime.FromFileTimeUtc(pDates[0]).ToLocalTime(); 
                dt2 = DateTime.FromFileTimeUtc(pDates[1]).ToLocalTime();
            } 

            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket((int) pBin[0],
                                                     name.ToString(),
                                                     dt1, 
                                                     dt2,
                                                     (bool) (pBin[1] != 0), 
                                                     data.ToString(), 
                                                     path.ToString());
            if (TicketCompatibilityMode > TicketCompatibilityMode.Framework20) { 
                ticket.InternalVersion = (int)pBin[2];
                ticket.InternalData = internalData;
            }
 
            return ticket;
        } 
 

        ///////////////////////////////////////////////////////////////////////////// 
        /////////////////////////////////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////
        // Encrypt a ticket
 
        /// 
        ///    Given a FormsAuthenticationTicket, this 
        ///    method produces a string containing an encrypted authentication ticket suitable 
        ///    for use in an HTTP cookie.
        ///  
        public static String Encrypt(FormsAuthenticationTicket ticket) {
            return Encrypt(ticket, true);
        }
        private static String Encrypt(FormsAuthenticationTicket ticket, bool hexEncodedTicket) { 
            if (ticket == null)
                throw new ArgumentNullException("ticket"); 
 
            Initialize();
            ////////////////////////////////////////////////////////////////////// 
            // Step 1: Make it into a binary blob
            byte[] bBlob = MakeTicketIntoBinaryBlob(ticket);
            if (bBlob == null)
                return null; 

            ////////////////////////////////////////////////////////////////////// 
            // Step 2: Get the MAC and add to the blob 
            if (_Protection == FormsProtectionEnum.All || _Protection == FormsProtectionEnum.Validation)
            { 
                byte [] bMac    = MachineKeySection.HashData(bBlob, null, 0, bBlob.Length);
                if (bMac == null)
                    return null;
                byte [] bAll  = new byte[bMac.Length + bBlob.Length]; 
                Buffer.BlockCopy(bBlob, 0, bAll, 0, bBlob.Length);
                Buffer.BlockCopy(bMac, 0, bAll, bBlob.Length, bMac.Length); 
                bBlob = bAll; 
            }
 
            if (_Protection == FormsProtectionEnum.All || _Protection == FormsProtectionEnum.Encryption) {
                //////////////////////////////////////////////////////////////////////
                // Step 3: Do the actual encryption
                // DevDiv Bugs 137864: Include a random IV if under the right compat mode 
                // for improved encryption semantics
                bBlob = MachineKeySection.EncryptOrDecryptData(true, bBlob, null, 0, bBlob.Length, false, false, IVType.Random); 
            } 
            if (!hexEncodedTicket)
                return HttpServerUtility.UrlTokenEncode(bBlob); 
            else
                return MachineKeySection.ByteArrayToHexString(bBlob, 0);
        }
 
        /////////////////////////////////////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////// 
        ///////////////////////////////////////////////////////////////////////////// 
        // Verify User name and Password
 
        /// 
        ///    Given the supplied credentials, this method
        ///    attempts to validate the credentials against those contained in the configured
        ///    credential store. 
        /// 
        public static bool Authenticate(String name, String password) { 
            bool retVal = InternalAuthenticate(name, password); 

            if (retVal) { 
                PerfCounters.IncrementCounter(AppPerfCounter.FORMS_AUTH_SUCCESS);
                WebBaseEvent.RaiseSystemEvent(null, WebEventCodes.AuditFormsAuthenticationSuccess, name);
            }
            else { 
                PerfCounters.IncrementCounter(AppPerfCounter.FORMS_AUTH_FAIL);
                WebBaseEvent.RaiseSystemEvent(null, WebEventCodes.AuditFormsAuthenticationFailure, name); 
            } 

            return retVal; 
        }

        private static bool InternalAuthenticate(String name, String password) {
            ////////////////////////////////////////////////////////////////////// 
            // Step 1: Make sure we are initialized
            if (name == null || password == null) 
                return false; 

            Initialize(); 
            //////////////////////////////////////////////////////////////////////
            // Step 2: Get the user database
            AuthenticationSection settings = RuntimeConfig.GetAppConfig().Authentication;
            settings.ValidateAuthenticationMode(); 
            FormsAuthenticationUserCollection Users = settings.Forms.Credentials.Users;
 
//            Hashtable hTable = settings.Credentials; 

            if (Users == null) { 
                return false;
            }

            ////////////////////////////////////////////////////////////////////// 
            // Step 3: Get the (hashed) password for this user
            FormsAuthenticationUser u = Users[name.ToLower(CultureInfo.InvariantCulture)]; 
            if (u == null) 
                return false;
 
            String pass = (String)u.Password;

            if (pass == null) {
                return false; 
            }
 
            ////////////////////////////////////////////////////////////////////// 
            // Step 4: Hash the given password
            String   encPassword; 

            switch (settings.Forms.Credentials.PasswordFormat)
            {
                case FormsAuthPasswordFormat.SHA1: 
                    encPassword = HashPasswordForStoringInConfigFile(password, "sha1");
                    break; 
 
                case FormsAuthPasswordFormat.MD5:
                    encPassword = HashPasswordForStoringInConfigFile(password, "md5"); 
                    break;

                case FormsAuthPasswordFormat.Clear:
                    encPassword = password; 
                    break;
 
                default: 
                    return false;
            } 

            //////////////////////////////////////////////////////////////////////
            // Step 5: Compare the hashes
            return(String.Compare(encPassword, 
                                  pass,
                                  ((settings.Forms.Credentials.PasswordFormat != FormsAuthPasswordFormat.Clear) 
                                        ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)) 
                   == 0);
        } 

        /////////////////////////////////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////// 

        ///  
        ///    Given an authenticated user, calling SignOut 
        ///    removes the authentication ticket by doing a SetForms with an empty value. This
        ///    removes either durable or session cookies. 
        /// 
        public static void SignOut() {
            Initialize();
 
            HttpContext    context    = HttpContext.Current;
            bool           needToRedirect  = context.CookielessHelper.DoesCookieValueExistInOriginal('F'); 
 
            context.CookielessHelper.SetCookieValue('F', null); // Always clear the uri-cookie
 
            if (!CookielessHelperClass.UseCookieless(context, false, CookieMode) || context.Request.Browser.Cookies)
            { // clear cookie if required
                string cookieValue = String.Empty;
                if (context.Request.Browser["supportsEmptyStringInCookieValue"] == "false") 
                    cookieValue = "NoCookie";
                HttpCookie cookie = new HttpCookie(FormsCookieName, cookieValue); 
                cookie.HttpOnly = true; 
                cookie.Path = _FormsCookiePath;
                cookie.Expires = new System.DateTime(1999, 10, 12); 
                cookie.Secure = _RequireSSL;
                if (_CookieDomain != null)
                    cookie.Domain = _CookieDomain;
                context.Response.Cookies.RemoveCookie(FormsCookieName); 
                context.Response.Cookies.Add(cookie);
            } 
            if (needToRedirect) 
                context.Response.Redirect(GetLoginPage(null), false);
        } 
        /////////////////////////////////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////
 
        /// 
        ///    This method creates an authentication ticket 
        ///    for the given userName and attaches it to the cookies collection of the outgoing 
        ///    response. It does not perform a redirect.
        ///  
        public static void SetAuthCookie(String userName, bool createPersistentCookie) {
            Initialize();
            SetAuthCookie(userName, createPersistentCookie, FormsAuthentication.FormsCookiePath);
        } 

        ///  
        ///    This method creates an authentication ticket 
        ///    for the given userName and attaches it to the cookies collection of the outgoing
        ///    response. It does not perform a redirect. 
        /// 
        public static void SetAuthCookie(String userName, bool createPersistentCookie, String strCookiePath) {
            Initialize();
            HttpContext context = HttpContext.Current; 

            if (!context.Request.IsSecureConnection && RequireSSL) 
                throw new HttpException(SR.GetString(SR.Connection_not_secure_creating_secure_cookie)); 
            bool        cookieless  = CookielessHelperClass.UseCookieless(context, false, CookieMode);
            HttpCookie  cookie      = GetAuthCookie(userName, createPersistentCookie, cookieless ? "/" : strCookiePath, !cookieless); 

            if (!cookieless) {
                HttpContext.Current.Response.Cookies.Add(cookie);
                context.CookielessHelper.SetCookieValue('F', null); 
            }
            else { 
                context.CookielessHelper.SetCookieValue('F', cookie.Value); 
            }
        } 

        /////////////////////////////////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////// 

        ///  
        ///    Creates an authentication cookie for a given 
        ///    user name. This does not set the cookie as part of the outgoing response, so
        ///    that an application can have more control over how the cookie is issued. 
        /// 
        public static HttpCookie GetAuthCookie(String userName, bool createPersistentCookie) {
            Initialize();
            return GetAuthCookie(userName, createPersistentCookie, FormsAuthentication.FormsCookiePath); 
        }
 
        public static HttpCookie GetAuthCookie(String userName, bool createPersistentCookie, String strCookiePath) { 
            return GetAuthCookie(userName, createPersistentCookie, strCookiePath, true);
        } 
        private static HttpCookie GetAuthCookie(String userName, bool createPersistentCookie, String strCookiePath, bool hexEncodedTicket) {
            Initialize();
            if (userName == null)
                userName = String.Empty; 

            if (strCookiePath == null || strCookiePath.Length < 1) 
                strCookiePath = FormsCookiePath; 

            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket( 
                2, // version
                userName, // User-Name
                DateTime.Now, // Issue-Date
                DateTime.Now.AddMinutes(_Timeout), // Expiration 
                createPersistentCookie, // IsPersistent
                String.Empty, // User-Data 
                strCookiePath // Cookie Path 
                );
            if (TicketCompatibilityMode > TicketCompatibilityMode.Framework20) { 
                ticket.InternalVersion = (int)TicketCompatibilityMode;
                ticket.InternalData = null;
            }
 
            String strTicket = Encrypt(ticket, hexEncodedTicket);
            if (strTicket == null || strTicket.Length < 1) 
                        throw new HttpException( 
                                SR.GetString(
                                        SR.Unable_to_encrypt_cookie_ticket)); 


            HttpCookie cookie = new HttpCookie(FormsCookieName, strTicket);
 
            cookie.HttpOnly = true;
            cookie.Path = strCookiePath; 
            cookie.Secure = _RequireSSL; 
            if (_CookieDomain != null)
                cookie.Domain = _CookieDomain; 
            if (ticket.IsPersistent)
                cookie.Expires = ticket.Expiration;
            return cookie;
        } 

        ///////////////////////////////////////////////////////////////////////////// 
        ///////////////////////////////////////////////////////////////////////////// 
        /////////////////////////////////////////////////////////////////////////////
 
        internal static String GetReturnUrl(bool useDefaultIfAbsent)
        {
            Initialize();
 
            HttpContext     context     = HttpContext.Current;
            String          returnUrl   = context.Request.QueryString[RETURN_URL]; 
 
            // If it is not in the QueryString, look in the Posted-body
            if (returnUrl == null) { 
                returnUrl = context.Request.Form[RETURN_URL];
                if (!string.IsNullOrEmpty(returnUrl) && !returnUrl.Contains("/") && returnUrl.Contains("%"))
                    returnUrl = HttpUtility.UrlDecode(returnUrl);
            } 

            // Make sure it is on the current server if EnableCrossAppRedirects is false 
            if (!string.IsNullOrEmpty(returnUrl) && !EnableCrossAppRedirects) { 
                if (!UrlPath.IsPathOnSameServer(returnUrl, context.Request.Url))
                    returnUrl = null; 
            }

            // Make sure it is not dangerous, i.e. does not contain script, etc.
            if (!string.IsNullOrEmpty(returnUrl) && CrossSiteScriptingValidation.IsDangerousUrl(returnUrl)) 
                throw new HttpException(SR.GetString(SR.Invalid_redirect_return_url));
 
            return ((returnUrl == null && useDefaultIfAbsent) ? DefaultUrl : returnUrl); 
        }
 
        /// 
        ///    Returns the redirect URL for the original
        ///    request that caused the redirect to the login page.
        ///  
        public static String GetRedirectUrl(String userName, bool createPersistentCookie)
        { 
            if (userName == null) 
                return null;
            return GetReturnUrl(true); 
        }
        /////////////////////////////////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////// 
        // Redirect from logon page to orignal page
 
        ///  
        ///    This method redirects an authenticated user
        ///    back to the original URL that they requested. 
        /// 
        public static void RedirectFromLoginPage(String userName, bool createPersistentCookie) {
            Initialize();
            RedirectFromLoginPage(userName, createPersistentCookie, FormsAuthentication.FormsCookiePath); 
        }
 
        public static void RedirectFromLoginPage(String userName, bool createPersistentCookie, String strCookiePath) { 
            Initialize();
            if (userName == null) 
                return;

            HttpContext context = HttpContext.Current;
            string strUrl = GetReturnUrl(true); 
            if (  CookiesSupported || // Cookies-supported: Most common scenario
                  IsPathWithinAppRoot(context, strUrl)) { // Cookies not suported, so add it to the current app URL 
 
                SetAuthCookie(userName, createPersistentCookie, strCookiePath);
                strUrl = RemoveQueryStringVariableFromUrl(strUrl, FormsCookieName); // Make sure there is no other ticket in the Query String. 
                if (!CookiesSupported) {// Make sure the URL is relative, if we are using cookieless.
                    int pos = strUrl.IndexOf("://", StringComparison.Ordinal);
                    if (pos > 0) {
                        pos = strUrl.IndexOf('/', pos + 3); 
                        if (pos > 0)
                            strUrl = strUrl.Substring(pos); 
                    } 
                }
            } else if (EnableCrossAppRedirects) { // Cookieless scenario -- add it to the QueryString if allowed to 

                HttpCookie cookie = GetAuthCookie(userName, createPersistentCookie, strCookiePath);
                strUrl = RemoveQueryStringVariableFromUrl(strUrl, cookie.Name); // Make sure there is no other ticket in the Query String.
                if (strUrl.IndexOf('?') > 0) { 
                    strUrl += "&" + cookie.Name + "=" + cookie.Value;
                } 
                else { 
                    strUrl += "?" + cookie.Name + "=" + cookie.Value;
                } 

            } else {
                // Broken scenario:
                throw new HttpException(SR.GetString(SR.Can_not_issue_cookie_or_redirect)); 
            }
 
            context.Response.Redirect(strUrl, false); 
        }
 
        public static FormsAuthenticationTicket RenewTicketIfOld(FormsAuthenticationTicket tOld) {
            if (tOld == null)
                return null;
 
            DateTime dtN = DateTime.Now;
            TimeSpan t1  = dtN - tOld.IssueDate; 
            TimeSpan t2  = tOld.Expiration - dtN; 

            if (t2 > t1) 
                return tOld;

            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket (
                                                       tOld.Version, 
                                                       tOld.Name,
                                                       dtN, // Issue Date: Now 
                                                       dtN + (tOld.Expiration - tOld.IssueDate), // Expiration 
                                                       tOld.IsPersistent,
                                                       tOld.UserData, 
                                                       tOld.CookiePath);
            if (TicketCompatibilityMode > TicketCompatibilityMode.Framework20) {
                ticket.InternalVersion = tOld.InternalVersion;
                ticket.InternalData = tOld.InternalData; 
            }
 
            return ticket; 
        }
 
        public static void EnableFormsAuthentication(NameValueCollection configurationData) {
            BuildManager.ThrowIfPreAppStartNotRunning();
            configurationData = configurationData ?? new NameValueCollection();
            AuthenticationConfig.Mode = AuthenticationMode.Forms; 
            Initialize();
            // Last caller overwrites only the values that are present in the dictionary. 
            string defaultUrl = configurationData["defaultUrl"]; 
            if (!String.IsNullOrEmpty(defaultUrl)) {
                _DefaultUrl = defaultUrl; 
            }
            string loginUrl = configurationData["loginUrl"];
            if (!String.IsNullOrEmpty(loginUrl)) {
                _LoginUrl = loginUrl; 
            }
        } 
 
        public static bool IsEnabled {
            get { 
                return AuthenticationConfig.Mode == AuthenticationMode.Forms;
            }
        }
 
        public static String FormsCookieName { get { Initialize(); return _FormsName; }}
 
        public static String FormsCookiePath { get { Initialize(); return _FormsCookiePath; }} 

        public static bool   RequireSSL { get { Initialize(); return _RequireSSL; }} 

        public static TimeSpan Timeout { get { Initialize(); return new TimeSpan(0, _Timeout, 0); } }

        public static bool   SlidingExpiration { get { Initialize(); return _SlidingExpiration; }} 

        public static HttpCookieMode CookieMode { get { Initialize(); return _CookieMode; }} 
 
        public static string CookieDomain { get { Initialize ();return _CookieDomain; } }
 
        public static bool EnableCrossAppRedirects { get { Initialize(); return _EnableCrossAppRedirects; } }

        public static TicketCompatibilityMode TicketCompatibilityMode { get { Initialize(); return _TicketCompatibilityMode; } }
 
        public static bool CookiesSupported {
            get { 
                HttpContext context = HttpContext.Current; 
                if (context != null) {
                    return !(CookielessHelperClass.UseCookieless(context, false, CookieMode)); 
                }
                return true;
            }
        } 

        public static string LoginUrl { 
            get { 
                Initialize();
                HttpContext context = HttpContext.Current; 
                if (context != null)  {
                    return AuthenticationConfig.GetCompleteLoginUrl(context, _LoginUrl);
                }
                if (_LoginUrl.Length == 0 || (_LoginUrl[0] != '/' && _LoginUrl.IndexOf("//", StringComparison.Ordinal) < 0)) 
                    return ("/" + _LoginUrl);
                return _LoginUrl; 
            } 
        }
 
        public static string DefaultUrl {
            get {
                Initialize();
                HttpContext context = HttpContext.Current; 
                if (context != null)  {
                    return AuthenticationConfig.GetCompleteLoginUrl(context, _DefaultUrl); 
                } 
                if (_DefaultUrl.Length == 0 || (_DefaultUrl[0] != '/' && _DefaultUrl.IndexOf("//", StringComparison.Ordinal) < 0))
                    return ("/" + _DefaultUrl); 
                return _DefaultUrl;
            }
        }
 
        internal static string GetLoginPage(string extraQueryString) {
            return GetLoginPage(extraQueryString, false); 
        } 
        internal static string GetLoginPage(string extraQueryString, bool reuseReturnUrl) {
            HttpContext context = HttpContext.Current; 
            string loginUrl = FormsAuthentication.LoginUrl;
            if (loginUrl.IndexOf('?') >= 0)
                loginUrl = RemoveQueryStringVariableFromUrl(loginUrl, RETURN_URL);
            int pos = loginUrl.IndexOf('?'); 
            if (pos < 0)
                loginUrl += "?"; 
            else 
                if (pos < loginUrl.Length -1)
                    loginUrl += "&"; 
            string returnUrl = null;
            if (reuseReturnUrl) {
                returnUrl = HttpUtility.UrlEncode( GetReturnUrl(false),
                                                   context.Request.QueryStringEncoding ); 
            }
            if (returnUrl == null) 
                returnUrl = HttpUtility.UrlEncode(context.Request.RawUrl, context.Request.ContentEncoding); 

            loginUrl += "ReturnUrl=" + returnUrl; 
            if (!String.IsNullOrEmpty(extraQueryString)) {
                loginUrl += "&" + extraQueryString;
            }
            return loginUrl; 
        }
 
 
        public static void RedirectToLoginPage() {
            RedirectToLoginPage(null); 
        }


        public static void RedirectToLoginPage(string extraQueryString) { 
            HttpContext context = HttpContext.Current;
            string loginUrl = GetLoginPage(extraQueryString); 
            context.Response.Redirect(loginUrl, false); 
        }
 
        /////////////////////////////////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////
        // Private stuff 

        ///////////////////////////////////////////////////////////////////////////// 
        // Config Tags 
        private  const String   CONFIG_DEFAULT_COOKIE    = ".ASPXAUTH";
 
        /////////////////////////////////////////////////////////////////////////////
        // Private data
        private static bool                _Initialized;
        private static String              _FormsName; 
        //private static FormsProtectionEnum _Protection;
        private static FormsProtectionEnum _Protection; 
        private static Int32               _Timeout; 
        private static String              _FormsCookiePath;
        private static bool                _RequireSSL; 
        private static bool                _SlidingExpiration;
        private static string              _LoginUrl;
        private static string              _DefaultUrl;
        private static HttpCookieMode      _CookieMode; 
        private static string              _CookieDomain = null;
        private static bool                _EnableCrossAppRedirects; 
        private static TicketCompatibilityMode _TicketCompatibilityMode; 

        ///////////////////////////////////////////////////////////////////////////// 
        private static byte[] MakeTicketIntoBinaryBlob(FormsAuthenticationTicket ticket) {
            byte []   bData  = new byte[4096];
            byte []   pBin   = new byte[4];
            long []   pDates = new long[2]; 
            byte []   pNull  = { 0, 0, 0 };
 
            // DevDiv Bugs 137864: 8 bytes may not be enough random bits as the length should be equal to the 
            // key size. In CompatMode > Framework20SP1, use the IVType.Random feature instead of these 8 bytes,
            // but still include empty 8 bytes for compat with webengine.dll, where CookieAuthConstructTicket is. 
            // Note that even in CompatMode = Framework20SP2 we fill 8 bytes with random data if the ticket
            // is not going to be encrypted.

            bool willEncrypt = (_Protection == FormsProtectionEnum.All || _Protection == FormsProtectionEnum.Encryption); 
            bool legacyPadding = !willEncrypt || (MachineKeySection.CompatMode == MachineKeyCompatibilityMode.Framework20SP1);
            if (legacyPadding) { 
                // Fill the first 8 bytes of the blob with random bits 
                byte[] bRandom = new byte[8];
                RNGCryptoServiceProvider randgen = new RNGCryptoServiceProvider(); 
                randgen.GetBytes(bRandom);
                Buffer.BlockCopy(bRandom, 0, bData, 0, 8);
            }
            else { 
                // use blank 8 bytes for compatibility with CookieAuthConstructTicket (do nothing)
            } 
 
            pBin[0] = (byte) ticket.Version;
            pBin[1] = (byte) (ticket.IsPersistent ? 1 : 0); 

            if (TicketCompatibilityMode == TicketCompatibilityMode.Framework20) {
                pDates[0] = ticket.IssueDate.ToFileTime();
                pDates[1] = ticket.Expiration.ToFileTime(); 
            }
            else { 
                pDates[0] = ticket.IssueDate.ToUniversalTime().ToFileTimeUtc(); 
                pDates[1] = ticket.Expiration.ToUniversalTime().ToFileTimeUtc();
                pBin[2] = (byte)ticket.InternalVersion; 
            }

          // Coriolis: replace Forms Authentication ticket parsing
          // functionality normally found in webengine.dll 
            int iRet;
#if !FEATURE_PAL 
            if (TicketCompatibilityMode == TicketCompatibilityMode.Framework20) { 
                iRet = UnsafeNativeMethods.CookieAuthConstructTicket(
                        bData, bData.Length, 
                        ticket.Name, ticket.UserData, ticket.CookiePath,
                        pBin, pDates);
            }
            else { 
                // Initial offset after random bits
                int offset = 8; 
                int byteCount = 0; 
                int bDataLength = bData.Length;
                if (ticket.Name == null || ticket.UserData == null || ticket.CookiePath == null) { 
                    return null;
                }
                // Copy the version
                bData[offset] = pBin[0]; 
                offset += 1;
                // Copy the null pad 
                Buffer.BlockCopy(pNull, 0, bData, offset, 3); 
                offset += 3;
                // Copy the name 
                byteCount = Encoding.Unicode.GetByteCount(ticket.Name);
                if (offset + 4 > bDataLength) {
                    return null;
                } 
                Buffer.BlockCopy(BitConverter.GetBytes(byteCount), 0, bData, offset, 4);
                offset += 4; 
                if (offset + byteCount > bDataLength) { 
                    return null;
                } 
                Buffer.BlockCopy(Encoding.Unicode.GetBytes(ticket.Name), 0, bData, offset, byteCount);
                offset += byteCount;
                // Copy the issue date
                if (offset + 8 > bDataLength) { 
                    return null;
                } 
                Buffer.BlockCopy(BitConverter.GetBytes(pDates[0]), 0, bData, offset, 8); 
                offset += 8;
                // Copy the IsPersistent byte 
                if (offset + 1 > bDataLength) {
                    return null;
                }
                bData[offset] = pBin[1]; 
                offset += 1;
                // Copy the expires date 
                if (offset + 8 > bDataLength) { 
                    return null;
                } 
                Buffer.BlockCopy(BitConverter.GetBytes(pDates[1]), 0, bData, offset, 8);
                offset += 8;
                // Copy the internal version
                if (offset + 1 > bDataLength) { 
                    return null;
                } 
                bData[offset] = pBin[2]; 
                offset += 1;
                // Copy the internal data 
                if (offset + 4 > bDataLength) {
                    return null;
                }
                if (ticket.InternalData == null) { 
                    byteCount = 0;
                } 
                else { 
                    byteCount = ticket.InternalData.Length;
                } 
                Buffer.BlockCopy(BitConverter.GetBytes(byteCount), 0, bData, offset, 4);
                offset += 4;
                if (offset + byteCount > bDataLength) {
                    return null; 
                }
                if (byteCount != 0) { 
                    Buffer.BlockCopy(ticket.InternalData, 0, bData, offset, byteCount); 
                    offset += byteCount;
                } 
                // Copy the user data
                byteCount = Encoding.Unicode.GetByteCount(ticket.UserData);
                if (offset + 4 > bDataLength) {
                    return null; 
                }
                Buffer.BlockCopy(BitConverter.GetBytes(byteCount), 0, bData, offset, 4); 
                offset += 4; 
                if (offset + byteCount > bDataLength) {
                    return null; 
                }
                Buffer.BlockCopy(Encoding.Unicode.GetBytes(ticket.UserData), 0, bData, offset, byteCount);
                offset += byteCount;
                // Copy the path 
                byteCount = Encoding.Unicode.GetByteCount(ticket.CookiePath);
                if (offset + 4 > bDataLength) { 
                    return null; 
                }
                Buffer.BlockCopy(BitConverter.GetBytes(byteCount), 0, bData, offset, 4); 
                offset += 4;
                if (offset + byteCount > bDataLength) {
                    return null;
                } 
                Buffer.BlockCopy(Encoding.Unicode.GetBytes(ticket.CookiePath), 0, bData, offset, byteCount);
                offset += byteCount; 
                iRet = offset; 
            }
#else // !FEATURE_PAL 
            int iRet = FormsAuthCoriolis.CookieAuthConstructTicket(bData,
              bData.Length, ticket.Name, ticket.UserData, ticket.CookiePath,
              pBin, pDates);
 
#endif // !FEATURE_PAL
 
 
            if (iRet < 0)
                return null; 

            byte[] ciphertext = new byte[iRet];
            Buffer.BlockCopy(bData, 0, ciphertext, 0, iRet);
            return ciphertext; 
        }
 
        ///////////////////////////////////////////////////////////////////////////// 
        /////////////////////////////////////////////////////////////////////////////
 
        internal static string RemoveQueryStringVariableFromUrl(string strUrl, string QSVar) {
            int posQ = strUrl.IndexOf('?');
            if (posQ < 0)
                return strUrl; 

            // Remove non-encoded QSVars 
            string amp   = @"&"; 
            string question = @"?";
 
            string token = amp + QSVar + "=";
            RemoveQSVar(ref strUrl, posQ, token, amp, amp.Length);

            token = question + QSVar + "="; 
            RemoveQSVar(ref strUrl, posQ, token, amp, question.Length);
 
            // Remove Url-enocoded strings 
            amp = HttpUtility.UrlEncode(@"&");
            question = HttpUtility.UrlEncode(@"?"); 

            token = amp + HttpUtility.UrlEncode(QSVar + "=");
            RemoveQSVar(ref strUrl, posQ, token, amp, amp.Length);
 
            token = question + HttpUtility.UrlEncode(QSVar + "=");
            RemoveQSVar(ref strUrl, posQ, token, amp, question.Length); 
            return strUrl; 
        }
 
        /////////////////////////////////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////
        static private void RemoveQSVar(ref string strUrl, int posQ, string token, string sep, int lenAtStartToLeave)
        { 
            for (int pos = strUrl.LastIndexOf(token, StringComparison.Ordinal); pos >= posQ; pos = strUrl.LastIndexOf(token, StringComparison.Ordinal))
            { 
                int end = strUrl.IndexOf(sep, pos + token.Length, StringComparison.Ordinal) + sep.Length; 
                if (end < sep.Length || end >= strUrl.Length)
                { // ending separator not found or nothing is at the end 
                    strUrl = strUrl.Substring(0, pos);
                }
                else
                { 
                    strUrl = strUrl.Substring(0, pos + lenAtStartToLeave) + strUrl.Substring(end);
                } 
            } 
        }
        static private bool IsPathWithinAppRoot(HttpContext context, string path) 
        {
            Uri absUri;
            if (!Uri.TryCreate(path, UriKind.Absolute, out absUri))
                return HttpRuntime.IsPathWithinAppRoot(path); 

            if (!absUri.IsLoopback && !string.Equals(context.Request.Url.Host, absUri.Host, StringComparison.OrdinalIgnoreCase)) 
                return false; // different servers 

            return HttpRuntime.IsPathWithinAppRoot(absUri.AbsolutePath); 
        }
    }
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.


                        

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