Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / MS / Internal / FontCache / FontCacheUtil.cs / 1305600 / FontCacheUtil.cs
//---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // Description: Miscellaneous utility functions for font handling code. // //--------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Packaging; using System.Reflection; using System.Resources; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using System.Windows; using System.Windows.Markup; // for XmlLanguage using System.Windows.Media; using System.Windows.Navigation; using System.Windows.Threading; using MS.Win32; using MS.Internal; using MS.Internal.FontFace; using MS.Internal.PresentationCore; using MS.Internal.Resources; using MS.Utility; using Microsoft.Win32.SafeHandles; // Since we disable PreSharp warnings in this file, we first need to disable warnings about unknown message numbers and unknown pragmas. #pragma warning disable 1634, 1691 namespace MS.Internal.FontCache { ////// CheckedPointer is used to reference a fixed size memory block. /// The purpose of the class is to protect the memory block from overruns. /// ArgumentOutOfRangeException is thrown when an overrun is detected. /// ////// This class could potentially contain critical information for the case /// where the data pointed to by "pointer" parameter is obtained under an /// elevation. We consder CheckedPointer class itself to be security Agnostic. /// When someone creates this instance from critical data, they'll need to /// mark the instance as SecurityCritical and track usage. /// [FriendAccessAllowed] internal struct CheckedPointer { ////// Critical: This code yields unverifiable demand /// [SecurityCritical] internal unsafe CheckedPointer(void * pointer, int size) { _pointer = pointer; _size = size; } ////// Critical: This calls into PositionPointer which is link demand protected /// also it constructs data for a checked pointer. /// [SecurityCritical] internal CheckedPointer(UnmanagedMemoryStream stream) { Debug.Assert(stream.Position == 0); unsafe { _pointer = stream.PositionPointer; } long length = stream.Length; if (length < 0 || length > int.MaxValue) throw new ArgumentOutOfRangeException(); _size = (int)length; } ////// Critical: Call into unsafe code block /// TreatAsSafe: This is ok to call /// internal bool IsNull { [SecurityCritical,SecurityTreatAsSafe] get { unsafe { return (_pointer == null); } } } ////// Critical: _size is marked as critical. /// TreatAsSafe: _size is critical only for set. /// internal int Size { [SecurityCritical, SecurityTreatAsSafe] get { return _size; } } ////// Critical: This code yields unverifiable demand. This code can be used to expose contents of arbitrary pointers /// [SecurityCritical] internal byte[] ToArray() { byte[] b = new byte[_size]; unsafe { if (_pointer == null) throw new ArgumentOutOfRangeException(); Marshal.Copy((IntPtr)_pointer, b, 0, Size); } return b; } ////// Critical: This code exposes contents of arbitrary pointers. /// [SecurityCritical] internal void CopyTo(CheckedPointer dest) { unsafe { if (_pointer == null) throw new ArgumentOutOfRangeException(); byte* s = (byte*)_pointer; byte * d = (byte *)dest.Probe(0, _size); for (int i = 0; i < _size; ++i) { d[i] = s[i]; } } } // Returns the offset of the given pointer within this mapping, // with a bounds check. The returned offset may be equal to the size, // but not greater. Throws ArgumentOutOfRangeException if pointer // is not within the bounds of the mapping. ////// Critical: The method takes a pointer and performs pointer arithmetic. /// [SecurityCritical] internal unsafe int OffsetOf(void * pointer) { long offset = (byte*)pointer - (byte*)_pointer; if (offset < 0 || offset > _size || _pointer == null || pointer == null) throw new ArgumentOutOfRangeException(); return (int)offset;//no truncation possible since _size is an int } // Returns the offset of the given pointer within this mapping, // with a bounds check. The returned offset may be equal to the size, // but not greater. Throws ArgumentOutOfRangeException if pointer // is not within the bounds of the mapping. ////// Critical: The method contains unsafe code and calls the critical OffsetOf(void*) method. /// [SecurityCritical] internal int OffsetOf(CheckedPointer pointer) { unsafe { return OffsetOf(pointer._pointer); } } ////// Critical: This code yields unverifiable demand /// TreatAsSafe: This code simply overloads the + operator to increment pointer. /// It is safe because it is bounds checked to ensure it does not corrupt memory /// [SecurityCritical, SecurityTreatAsSafe] public static CheckedPointer operator+(CheckedPointer rhs, int offset) { // In future I'll just use checked context. That'll require modifying callers to expect integer overflow exceptions. unsafe { if (offset < 0 || offset > rhs._size || rhs._pointer == null) throw new ArgumentOutOfRangeException(); rhs._pointer = (byte*)rhs._pointer + offset; } rhs._size -= offset; return rhs; } ////// Critical: This code yields unverifiable demand and returns a pointer /// Although it is bounds checked the fact that it returns a pointer makes it risky. /// [SecurityCritical] internal unsafe void * Probe(int offset, int length) { if (_pointer == null || offset < 0 || offset > _size || offset + length > _size || offset + length < 0) throw new ArgumentOutOfRangeException(); return (byte *)_pointer + offset; } ////// Same as Probe, but returns a CheckedPointer instead of an unsafe pointer /// /// /// ////// /// Critical: This calls into unsafe code and also calls into checkedpointer which has a link demand /// TreatAsSafe: This code returns a checkedpointer and accessing the content is critical. Accessing is tracked in Probe /// [SecurityCritical,SecurityTreatAsSafe] internal CheckedPointer CheckedProbe(int offset, int length) { unsafe { if (_pointer == null || offset < 0 || offset > _size || offset + length > _size || offset + length < 0) throw new ArgumentOutOfRangeException(); return new CheckedPointer((byte*)_pointer + offset, length); } } ///Changes the buffer size of this CheckedPointer object. Used when memory block is resizable, ///like in a file mapping. ////// Critical: Passing an invalid size can calls future calls to Probe() to succeed even if the bounds of the buffer /// is being violated. This can cause the program to write to random memory. /// [SecurityCritical] internal void SetSize(int newSize) { _size = newSize; } ////// Critical: Contains unsafe code, which accesses _pointer field. /// TreatAsSafe: Only compares; does not expose pointer or do unsafe pointer operations. /// [SecurityCritical, SecurityTreatAsSafe] internal bool PointerEquals(CheckedPointer pointer) { unsafe { return _pointer == pointer._pointer; } } ////// Critical: Contains unsafe code. /// TreatAsSafe: Calls Probe and writes only the amount of data it probed for. /// [SecurityCritical, SecurityTreatAsSafe] internal void WriteBool(bool value) { unsafe { *(bool*)this.Probe(0, sizeof(bool)) = value; } } ////// Critical: Contains unsafe code. /// TreatAsSafe: Calls Probe and reads only the amount of data it probed for. /// [SecurityCritical, SecurityTreatAsSafe] internal bool ReadBool() { unsafe { return *(bool*)this.Probe(0, sizeof(bool)); } } ////// Critical: Holds reference to an unsafe pointer /// [SecurityCritical] private unsafe void * _pointer; ////// Critical: Having an invalid size can cause future calls to Probe() to succeed /// even if the bounds of the buffer is being violated. /// This can cause the program to write to random memory. /// This variable should really be SecurityCriticalDataForSet of int, /// but SecurityCriticalDataForSet is a generic defined in another assembly, /// and using it with a value type will cause JITting to happen. /// [SecurityCritical] private int _size; } ////// HashFn is a port of predefined hash functions from LKRHash /// [FriendAccessAllowed] internal static class HashFn { // Small prime number used as a multiplier in the supplied hash functions private const int HASH_MULTIPLIER = 101; internal static int HashMultiply(int hash) { return hash * HASH_MULTIPLIER; } ////// Distributes accumulated hash value across a range. /// Should be called just before returning hash code from a hash function. /// /// Hash value ///Scrambed hash value internal static int HashScramble(int hash) { // Here are 10 primes slightly greater than 10^9 // 1000000007, 1000000009, 1000000021, 1000000033, 1000000087, // 1000000093, 1000000097, 1000000103, 1000000123, 1000000181. // default value for "scrambling constant" const int RANDOM_CONSTANT = 314159269; // large prime number, also used for scrambling const uint RANDOM_PRIME = 1000000007; // we must cast to uint and back to int to correspond to current C++ behavior for operator% // since we have a matching hash function in native code uint a = (uint)(RANDOM_CONSTANT * hash); int b = (int)(a % RANDOM_PRIME); return b; } ////// Computes a hash code for a block of memory. /// One should not forget to call HashScramble before returning the final hash code value to the client. /// /// Pointer to a block of memory /// Size of the memory block in bytes /// Previous hash code to combine with ///Hash code ////// Critical - as this accesses unsafe code blocks and has the potential to /// corrupt memory. /// [SecurityCritical] internal unsafe static int HashMemory(void * pv, int numBytes, int hash) { byte * pb = (byte*)pv; while (numBytes-- > 0) { hash = HashMultiply(hash) + *pb; ++pb; } return hash; } internal static int HashString(string s, int hash) { foreach (char c in s) { hash = HashMultiply(hash) + (ushort)c; } return hash; } } ////// Utility functions for interaction with font cache service /// [FriendAccessAllowed] internal static class Util { internal const int nullOffset = -1; internal static string CompositeFontExtension { get { return SupportedExtensions[0]; } } private static readonly string[] SupportedExtensions = new string[] { // .COMPOSITEFONT must remain the first entry in this array // because IsSupportedFontExtension and IsCompositeFont relies on this. ".COMPOSITEFONT", ".OTF", ".TTC", ".TTF", ".TTE" }; private static readonly char[] InvalidFileNameChars = Path.GetInvalidFileNameChars(); internal const UriComponents UriWithoutFragment = UriComponents.AbsoluteUri & ~UriComponents.Fragment; private const string WinDir = "windir"; private const string EmptyFontFamilyReference = "#"; private const string EmptyCanonicalName = ""; private static object _dpiLock = new object(); private static int _dpi; private static bool _dpiInitialized = false; ////// Critical: This code elevates to get access to environment variable windir /// and exposes the local Windows fonts directory location. /// TreatAsSafe: This code uses critical fields to store critical data. /// [SecurityCritical, SecurityTreatAsSafe] static Util() { string s; //this code elevates to get access to the windir directory EnvironmentPermission environmentPermission = new EnvironmentPermission(EnvironmentPermissionAccess.Read, "Windir"); environmentPermission.Assert(); //Blessed Assert try { s = Environment.GetEnvironmentVariable(WinDir) + @"\Fonts\"; } finally { EnvironmentPermission.RevertAssert(); } _windowsFontsLocalPath = s.ToUpperInvariant(); _windowsFontsUriObject = new Uri(_windowsFontsLocalPath, UriKind.Absolute); // Characters that have reserved meanings (e.g., '%', '#', '/', etc.) remain escaped in the // string, but all others are not escaped. _windowsFontsUriString = _windowsFontsUriObject.GetComponents(UriComponents.AbsoluteUri, UriFormat.SafeUnescaped); } ////// Windows fonts Uri string in an unescaped form optimized for Uri manipulations. /// ////// Critical: Exposes the local Windows fonts directory location. /// [SecurityCritical] private static readonly string _windowsFontsLocalPath; ////// Critical: Exposes the local Windows fonts directory location. /// internal static string WindowsFontsLocalPath { [SecurityCritical] get { return _windowsFontsLocalPath; } } ////// Gets the number of physical pixels per DIP. For example, if the DPI of the rendering surface is 96 this /// value is 1.0f. If the DPI is 120, this value is 120.0f/96. /// internal static float PixelsPerDip { get { return ((float)Dpi) / 96; } } ////// Critical as this accesses Native methods. /// TreatAsSafe - it would be ok to expose this information - DPI in partial trust /// internal static int Dpi { [SecurityCritical, SecurityTreatAsSafe] get { if (!_dpiInitialized) { lock (_dpiLock) { if (!_dpiInitialized) { HandleRef desktopWnd = new HandleRef(null, IntPtr.Zero); // Win32Exception will get the Win32 error code so we don't have to IntPtr dc = MS.Win32.UnsafeNativeMethods.GetDC(desktopWnd); // Detecting error case from unmanaged call, required by PREsharp to throw a Win32Exception if (dc == IntPtr.Zero) { throw new Win32Exception(); } try { _dpi = MS.Win32.UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), NativeMethods.LOGPIXELSY); _dpiInitialized = true; } finally { MS.Win32.UnsafeNativeMethods.ReleaseDC(desktopWnd, new HandleRef(null, dc)); } } } } return _dpi; } } ////// Windows fonts Uri object. /// ////// Critical: Exposes the local Windows fonts directory location. /// [SecurityCritical] private static readonly Uri _windowsFontsUriObject; ////// Critical: Exposes the local Windows fonts directory location. /// internal static Uri WindowsFontsUriObject { [SecurityCritical] get { return _windowsFontsUriObject; } } ////// Windows fonts Uri string in an unescaped form. /// ////// Critical: Exposes the local Windows fonts directory location. /// [SecurityCritical] private static readonly string _windowsFontsUriString; ////// Critical: Exposes the local Windows fonts directory location. /// internal static string WindowsFontsUriString { [SecurityCritical] get { return _windowsFontsUriString; } } ////// Checks whether the specified location string or font family reference refers to the /// default Windows Fonts folder. /// ////// Returns true if the location part is empty or is a simple file name with no path /// characters (e.g., "#ARIAL" or "arial.ttf#ARIAL"). Returns false if the location /// is invalid or includes a path (e.g., "./#ARIAL", "..#ARIAL", "fonts/#ARIAL"). /// ////// This code is important for correcly interpreting font family references. /// For example if it returns true for ../arial.ttf#Arial (it shouldnt) then downstream code /// could be fooled into skipping a demand when loading data from a ttf file outside the /// Windows Fonts folder. /// ////// Critical - This method parse file paths. /// TreatAsSafe - Does not expose critical information. /// Does not have side effects. /// [SecurityCritical, SecurityTreatAsSafe] internal static bool IsReferenceToWindowsFonts(string s) { // Empty location always refers to Windows Fonts. if (string.IsNullOrEmpty(s) || s[0] == '#') return true; // Get the length of the location part. int length = s.IndexOf('#'); if (length < 0) length = s.Length; // Check for invalid file name characters in the location. These include reserved path // characters ('/', '\\', ':'), wildcards ('?', '*'), control characters, etc. if (s.IndexOfAny(InvalidFileNameChars, 0, length) >= 0) return false; // A font family reference is a URI reference and may contain escaped characters. We need // to check their unescaped values to protect against cases like this: // 1. Create canonical name by concatenating "file:///c:/windows/Fonts/" + "foo%2fbar.ttf#Arial" // 2. Later we create a Uri from the canonical name and pass to FontSource class // 3. FontSource gets Uri.LocalPath which returns "c:\windows\Fonts\foo\bar.ttf" // 4. Doh! for (int i = s.IndexOf('%', 0, length); i >= 0; i = s.IndexOf('%', i, length - i)) { // Get the unescaped character; this always advances i by at least 1. char unescapedChar = Uri.HexUnescape(s, ref i); // Is it a reserved character? if (Array.IndexOf(InvalidFileNameChars, unescapedChar) >= 0) return false; } // Check for special names "." and ".." which represent directories. Also reject // variations like "...", ".. ", etc., as none of these are valid file names. if (s[0] == '.') { // Advance past one or more '.' int i = 1; while (i < length && s[i] == '.') ++i; // advance past trailing spaces, e.g., ".. #Arial" while (i < length && char.IsWhiteSpace(s[i])) ++i; // return false if the whole location is just repeated '. followed by spaces if (i == length) return false; } // If we fall through to here the location part has no reserved or illegal characters. // The "file name" could still be a reserved device name like CON, PRN, etc., but since // none of these will have a known font extension we won't try to open the device. return true; } internal static bool IsSupportedSchemeForAbsoluteFontFamilyUri(Uri absoluteUri) { // The only absolute URIs we allow in font family references are "file:" URIs. return absoluteUri.IsFile; } internal static void SplitFontFaceIndex(Uri fontUri, out Uri fontSourceUri, out int faceIndex) { // extract face index string fragment = fontUri.GetComponents(UriComponents.Fragment, UriFormat.SafeUnescaped); if (!String.IsNullOrEmpty(fragment)) { if (!int.TryParse( fragment, NumberStyles.None, CultureInfo.InvariantCulture, out faceIndex )) { throw new ArgumentException(SR.Get(SRID.FaceIndexMustBePositiveOrZero), "fontUri"); } // face index was specified in a fragment, we need to strip off fragment from the source Uri fontSourceUri = new Uri(fontUri.GetComponents(Util.UriWithoutFragment, UriFormat.SafeUnescaped)); } else { // simple case, no face index specified faceIndex = 0; fontSourceUri = fontUri; } } internal static Uri CombineUriWithFaceIndex(string fontUri, int faceIndex) { if (faceIndex == 0) return new Uri(fontUri); // the Uri roundtrip is necessary for escaping possible '#' symbols in the folder path, // so that they don't conflict with the fragment part. string canonicalPathUri = new Uri(fontUri).GetComponents(UriComponents.AbsoluteUri, UriFormat.SafeUnescaped); string faceIndexString = faceIndex.ToString(CultureInfo.InvariantCulture); return new Uri(canonicalPathUri + '#' + faceIndexString); } internal static bool IsSupportedFontExtension(string extension, out bool isComposite) { for (int i = 0; i < SupportedExtensions.Length; ++i) { string supportedExtension = SupportedExtensions[i]; if (String.Compare(extension, supportedExtension, StringComparison.OrdinalIgnoreCase) == 0) { isComposite = (i == 0); // First array entry is *.CompositeFont return true; } } isComposite = false; return false; } internal static bool IsCompositeFont(string extension) { return (String.Compare(extension, CompositeFontExtension, StringComparison.OrdinalIgnoreCase) == 0); } internal static bool IsEnumerableFontUriScheme(Uri fontLocation) { bool isEnumerable = false; // We only support file:// and pack:// application Uris to reference logical fonts. if (fontLocation.IsAbsoluteUri) { if (fontLocation.IsFile) { // file scheme is always enumerable isEnumerable = true; } else if (fontLocation.Scheme == PackUriHelper.UriSchemePack) { // This is just an arbitrary file name which we use to construct a file URI. const string fakeFileName = "X"; // The fontLocation could be a folder-like URI even though the pack scheme does not allow this. // We simulate the concept of subfolders for packaged fonts. Before calling any PackUriHelper // methods, create a Uri which we know to be a file-like (rather than folder-like) URI. Uri fileUri; if (Uri.TryCreate(fontLocation, fakeFileName, out fileUri)) { isEnumerable = BaseUriHelper.IsPackApplicationUri(fileUri); } } } return isEnumerable; } internal static bool IsAppSpecificUri(Uri fontLocation) { // Only file:// Uris that refer to local drives can be cached across applications. // This function filters out some easy options, such as app specific pack:// Uris and // UNC paths. // Note that we make an assumption here that local drive letters stay the same across user sessions. // Also, we rely on session 0 not having access to any of the mapped drives. return !fontLocation.IsAbsoluteUri || !fontLocation.IsFile || fontLocation.IsUnc; } internal static string GetUriExtension(Uri uri) { string unescapedPath = uri.GetComponents(UriComponents.Path, UriFormat.Unescaped); return Path.GetExtension(unescapedPath); } /// /// Converts the specified portion of a friendly name to a normalized font family reference. /// ////// Friendly names use commas as delimeters so any literal commas must be escaped by /// doubling. If any doubled commas are present in the specified substring they are unescaped /// in the normalized font family reference. /// internal static string GetNormalizedFontFamilyReference(string friendlyName, int startIndex, int length) { if (friendlyName.IndexOf(',', startIndex, length) < 0) { // We don't need to unescape any commas. return NormalizeFontFamilyReference(friendlyName, startIndex, length); } else { // Unescape commas and then convert to normalized form. return NormalizeFontFamilyReference(friendlyName.Substring(startIndex, length).Replace(",,", ",")); } } ////// Converts a font family reference to a normalized form. /// private static string NormalizeFontFamilyReference(string fontFamilyReference) { return NormalizeFontFamilyReference(fontFamilyReference, 0, fontFamilyReference.Length); } ////// Converts a font family reference to normalized form. /// ////// In normalized form, the fragment separator ('#') is always present, and the family /// name part (i.e., everything after the fragment separator) has been converted to /// upper case. However, the case of the location part (if any) is preserved. /// ////// "Arial" --> "#ARIAL" /// "fonts/#My Font" --> "fonts/#MY FONT" /// "/fonts/#My Font" --> "/fonts/#MY FONT" /// private static string NormalizeFontFamilyReference(string fontFamilyReference, int startIndex, int length) { if (length == 0) return EmptyFontFamilyReference; int fragmentIndex = fontFamilyReference.IndexOf('#', startIndex, length); if (fragmentIndex < 0) { // No fragment separator. The entire string is a family name so convert to uppercase // and add a fragment separator at the beginning. return "#" + fontFamilyReference.Substring(startIndex, length).ToUpperInvariant(); } else if (fragmentIndex + 1 == startIndex + length) { // No family name part. return EmptyFontFamilyReference; } else if (fragmentIndex == startIndex) { // Empty location part; convert the whole string to uppercase. return fontFamilyReference.Substring(startIndex, length).ToUpperInvariant(); } else { // Convert the fragment to uppercase, but preserve the case of the location. string location = fontFamilyReference.Substring(startIndex, fragmentIndex - startIndex); string fragment = fontFamilyReference.Substring(fragmentIndex, (startIndex + length) - fragmentIndex); return location + fragment.ToUpperInvariant(); } } ////// Converts a font family name and location to a font family reference. /// /// A font family name with no characters escaped /// An optional location ///Returns a font family reference, which may be either a URI reference or just a fragment /// (with or without the '#' prefix) internal static string ConvertFamilyNameAndLocationToFontFamilyReference(string familyName, string location) { // Escape reserved characters in the family name. In the fragment, we need only // worry about literal percent signs ('%') to avoid confusion with the escape prefix // and literal pound signs ('#') to avoid confusion with the fragment separator. string fontFamilyReference = familyName.Replace("%", "%25").Replace("#", "%23"); // Is there a location part? if (!string.IsNullOrEmpty(location)) { // We just escaped the family name and the location part should already be a valid URI reference. fontFamilyReference = string.Concat( location, "#", fontFamilyReference ); } return fontFamilyReference; } ////// Converts a font family reference to a friendly name by escaping any literal commas. /// /// A font family reference ///Returns a friendly name. ///Single commas delimit multiple font family references in a friendly name so any /// commas in the specified string are replaced with double commas in the return value. /// internal static string ConvertFontFamilyReferenceToFriendlyName(string fontFamilyReference) { return fontFamilyReference.Replace(",", ",,"); } ////// Compares string using character ordinals. /// The comparison is case insensitive based on InvariantCulture. /// We have our own custom wrapper because we need to sort using the same algorithm /// we use in incremental charater search. /// There are subtle things (e.g. surrogates) that String.Compare() does and we don't. /// /// First input string. /// Second input string. ///Same semantics as for String.Compare internal static int CompareOrdinalIgnoreCase(string a, string b) { int aLength = a.Length; int bLength = b.Length; int minLength = Math.Min(aLength, bLength); for (int i = 0; i < minLength; ++i) { int result = CompareOrdinalIgnoreCase(a[i], b[i]); if (result != 0) return result; } return aLength - bLength; } private static int CompareOrdinalIgnoreCase(char a, char b) { char ca = Char.ToUpperInvariant(a); char cb = Char.ToUpperInvariant(b); return ca - cb; } // Makes sure the caller has path discovery permission for full fileName path. private static void ValidateFileNamePermissions(ref string fileName) { if (!SecurityHelper.CallerHasPathDiscoveryPermission(fileName)) { // If the caller didn't have path discovery permission for fileName, we can still give out relative file name. fileName = Path.GetFileName(fileName); } } ////// This function performs job similar to CLR's internal __Error.WinIOError function: /// it maps win32 errors from file I/O to CLR exceptions and includes string where possible. /// However, we're interested only in errors when opening a file for reading. /// /// Win32 error code. /// File name string. ////// Critical - As this function throws exception containing full file name, which can result in Information Disclosure. /// TreatAsSafe - As the function performs permission demand. /// [SecurityCritical, SecurityTreatAsSafe] internal static void ThrowWin32Exception(int errorCode, string fileName) { ValidateFileNamePermissions(ref fileName); switch (errorCode) { case NativeMethods.ERROR_FILE_NOT_FOUND: throw new FileNotFoundException(SR.Get(SRID.FileNotFoundExceptionWithFileName, fileName), fileName); case NativeMethods.ERROR_PATH_NOT_FOUND: throw new DirectoryNotFoundException(SR.Get(SRID.DirectoryNotFoundExceptionWithFileName, fileName)); case NativeMethods.ERROR_ACCESS_DENIED: throw new UnauthorizedAccessException(SR.Get(SRID.UnauthorizedAccessExceptionWithFileName, fileName)); case NativeMethods.ERROR_FILENAME_EXCED_RANGE: throw new PathTooLongException(SR.Get(SRID.PathTooLongExceptionWithFileName, fileName)); default: throw new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName), NativeMethods.MakeHRFromErrorCode(errorCode)); } } ////// Critical - As this function accesses font Uri that contains absolute font path. /// Safe - As we use ValidateFileNamePermissions to strip off the local path part for file Uris. /// [SecurityCritical, SecurityTreatAsSafe] internal static Exception ConvertInPageException(FontSource fontSource, SEHException e) { string fileName; if (fontSource.Uri.IsFile) { fileName = fontSource.Uri.LocalPath; ValidateFileNamePermissions(ref fileName); } else { fileName = fontSource.GetUriString(); } return new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName), e); } } ////// A class that wraps operations with Win32 memory sections and file mappings /// ////// This class could potentially contain critical information for the case /// where the data pointed to by the Mapping is obtained under an elevation. /// We consder FileMapping class itself to be security agnostic. When /// someone creates this instance from critical data, they'll need to mark /// the instance as SecurityCritical and track usage. For example if a call to /// OpenFile is made on an instance of FileMapping, that instance will be /// SecurityCritical. /// [FriendAccessAllowed] internal class FileMapping : UnmanagedMemoryStream { ~FileMapping() { Dispose(false); } ////// Critical: This method acceses critical elements _viewHandle and _mappingHandle /// TreatAsSafe: This data is not exposed and calling dispose is ok /// [SecurityCritical, SecurityTreatAsSafe] protected override void Dispose(bool disposing) { base.Dispose(disposing); if (!_disposed) { if (disposing) { if (_viewHandle != null) _viewHandle.Dispose(); if (_mappingHandle != null) _mappingHandle.Dispose(); } // We only handle flat disk files read only, should never be writeable. Invariant.Assert(!CanWrite); } _disposed = true; } ////// Critical - As this function calls functions CreateFileMapping, /// MapViewOfFileEx and Initialize under elevation. /// Any instance of FileMapping /// object on which this function is called becomes a critical /// instance and its usage will need to be tracked for audit. /// [SecurityCritical] internal void OpenFile(string fileName) { NativeMethods.SECURITY_ATTRIBUTES sa = new NativeMethods.SECURITY_ATTRIBUTES(); using (sa) { unsafe { // Disable PREsharp warning about not calling Marshal.GetLastWin32Error, // because we already check the handle for invalid value and // we are not particularly interested in specific Win32 error. #pragma warning disable 6523 long size; using (SafeFileHandle fileHandle = UnsafeNativeMethods.CreateFile( fileName, NativeMethods.GENERIC_READ, NativeMethods.FILE_SHARE_READ, null, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero )) { if (fileHandle.IsInvalid) { Util.ThrowWin32Exception(Marshal.GetLastWin32Error(), fileName); } UnsafeNativeMethods.LARGE_INTEGER fileSize = new UnsafeNativeMethods.LARGE_INTEGER(); if (!UnsafeNativeMethods.GetFileSizeEx(fileHandle, ref fileSize)) throw new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName)); size = (long)fileSize.QuadPart; if (size == 0) throw new FileFormatException(new Uri(fileName)); _mappingHandle = UnsafeNativeMethods.CreateFileMapping( fileHandle, sa, UnsafeNativeMethods.PAGE_READONLY, 0, 0, null); } if (_mappingHandle.IsInvalid) throw new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName)); _viewHandle = UnsafeNativeMethods.MapViewOfFileEx(_mappingHandle, UnsafeNativeMethods.FILE_MAP_READ, 0, 0, IntPtr.Zero, IntPtr.Zero); if (_viewHandle.IsInvalid) throw new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName)); #pragma warning restore 6523 // Initialize() method demands UnmanagedCode permission, and OpenFile() is already marked as critical. new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); //Blessed Assert try { Initialize((byte*)_viewHandle.Memory, size, size, FileAccess.Read); } finally { SecurityPermission.RevertAssert(); } } } } ////// Critical: This element holds reference to an object retrieved under an elevation /// [SecurityCritical] private UnsafeNativeMethods.SafeViewOfFileHandle _viewHandle; ////// Critical: This element holds reference to an object retrieved under an elevation /// [SecurityCritical] private UnsafeNativeMethods.SafeFileMappingHandle _mappingHandle; private bool _disposed = false; } internal class LocalizedName { internal LocalizedName(XmlLanguage language, string name) : this(language, name, language.GetEquivalentCulture().LCID) {} internal LocalizedName(XmlLanguage language, string name, int originalLCID) { _language = language; _name = name; _originalLCID = originalLCID; } internal XmlLanguage Language { get { return _language; } } internal string Name { get { return _name; } } internal int OriginalLCID { get { return _originalLCID; } } internal static IComparerNameComparer { get { return _nameComparer; } } internal static IComparer LanguageComparer { get { return _languageComparer; } } private class NameComparerClass : IComparer { #region IComparer Members int IComparer .Compare(LocalizedName x, LocalizedName y) { return Util.CompareOrdinalIgnoreCase(x._name, y._name); } #endregion } private class LanguageComparerClass : IComparer { #region IComparer Members int IComparer .Compare(LocalizedName x, LocalizedName y) { return String.Compare(x._language.IetfLanguageTag, y._language.IetfLanguageTag, StringComparison.OrdinalIgnoreCase); } #endregion } private XmlLanguage _language; // the language identifier private string _name; // name converted to Unicode private int _originalLCID; // original LCID, used in cases when we want to preserve it to avoid information loss private static NameComparerClass _nameComparer = new NameComparerClass(); private static LanguageComparerClass _languageComparer = new LanguageComparerClass(); } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // Description: Miscellaneous utility functions for font handling code. // //--------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Packaging; using System.Reflection; using System.Resources; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using System.Windows; using System.Windows.Markup; // for XmlLanguage using System.Windows.Media; using System.Windows.Navigation; using System.Windows.Threading; using MS.Win32; using MS.Internal; using MS.Internal.FontFace; using MS.Internal.PresentationCore; using MS.Internal.Resources; using MS.Utility; using Microsoft.Win32.SafeHandles; // Since we disable PreSharp warnings in this file, we first need to disable warnings about unknown message numbers and unknown pragmas. #pragma warning disable 1634, 1691 namespace MS.Internal.FontCache { /// /// CheckedPointer is used to reference a fixed size memory block. /// The purpose of the class is to protect the memory block from overruns. /// ArgumentOutOfRangeException is thrown when an overrun is detected. /// ////// This class could potentially contain critical information for the case /// where the data pointed to by "pointer" parameter is obtained under an /// elevation. We consder CheckedPointer class itself to be security Agnostic. /// When someone creates this instance from critical data, they'll need to /// mark the instance as SecurityCritical and track usage. /// [FriendAccessAllowed] internal struct CheckedPointer { ////// Critical: This code yields unverifiable demand /// [SecurityCritical] internal unsafe CheckedPointer(void * pointer, int size) { _pointer = pointer; _size = size; } ////// Critical: This calls into PositionPointer which is link demand protected /// also it constructs data for a checked pointer. /// [SecurityCritical] internal CheckedPointer(UnmanagedMemoryStream stream) { Debug.Assert(stream.Position == 0); unsafe { _pointer = stream.PositionPointer; } long length = stream.Length; if (length < 0 || length > int.MaxValue) throw new ArgumentOutOfRangeException(); _size = (int)length; } ////// Critical: Call into unsafe code block /// TreatAsSafe: This is ok to call /// internal bool IsNull { [SecurityCritical,SecurityTreatAsSafe] get { unsafe { return (_pointer == null); } } } ////// Critical: _size is marked as critical. /// TreatAsSafe: _size is critical only for set. /// internal int Size { [SecurityCritical, SecurityTreatAsSafe] get { return _size; } } ////// Critical: This code yields unverifiable demand. This code can be used to expose contents of arbitrary pointers /// [SecurityCritical] internal byte[] ToArray() { byte[] b = new byte[_size]; unsafe { if (_pointer == null) throw new ArgumentOutOfRangeException(); Marshal.Copy((IntPtr)_pointer, b, 0, Size); } return b; } ////// Critical: This code exposes contents of arbitrary pointers. /// [SecurityCritical] internal void CopyTo(CheckedPointer dest) { unsafe { if (_pointer == null) throw new ArgumentOutOfRangeException(); byte* s = (byte*)_pointer; byte * d = (byte *)dest.Probe(0, _size); for (int i = 0; i < _size; ++i) { d[i] = s[i]; } } } // Returns the offset of the given pointer within this mapping, // with a bounds check. The returned offset may be equal to the size, // but not greater. Throws ArgumentOutOfRangeException if pointer // is not within the bounds of the mapping. ////// Critical: The method takes a pointer and performs pointer arithmetic. /// [SecurityCritical] internal unsafe int OffsetOf(void * pointer) { long offset = (byte*)pointer - (byte*)_pointer; if (offset < 0 || offset > _size || _pointer == null || pointer == null) throw new ArgumentOutOfRangeException(); return (int)offset;//no truncation possible since _size is an int } // Returns the offset of the given pointer within this mapping, // with a bounds check. The returned offset may be equal to the size, // but not greater. Throws ArgumentOutOfRangeException if pointer // is not within the bounds of the mapping. ////// Critical: The method contains unsafe code and calls the critical OffsetOf(void*) method. /// [SecurityCritical] internal int OffsetOf(CheckedPointer pointer) { unsafe { return OffsetOf(pointer._pointer); } } ////// Critical: This code yields unverifiable demand /// TreatAsSafe: This code simply overloads the + operator to increment pointer. /// It is safe because it is bounds checked to ensure it does not corrupt memory /// [SecurityCritical, SecurityTreatAsSafe] public static CheckedPointer operator+(CheckedPointer rhs, int offset) { // In future I'll just use checked context. That'll require modifying callers to expect integer overflow exceptions. unsafe { if (offset < 0 || offset > rhs._size || rhs._pointer == null) throw new ArgumentOutOfRangeException(); rhs._pointer = (byte*)rhs._pointer + offset; } rhs._size -= offset; return rhs; } ////// Critical: This code yields unverifiable demand and returns a pointer /// Although it is bounds checked the fact that it returns a pointer makes it risky. /// [SecurityCritical] internal unsafe void * Probe(int offset, int length) { if (_pointer == null || offset < 0 || offset > _size || offset + length > _size || offset + length < 0) throw new ArgumentOutOfRangeException(); return (byte *)_pointer + offset; } ////// Same as Probe, but returns a CheckedPointer instead of an unsafe pointer /// /// /// ////// /// Critical: This calls into unsafe code and also calls into checkedpointer which has a link demand /// TreatAsSafe: This code returns a checkedpointer and accessing the content is critical. Accessing is tracked in Probe /// [SecurityCritical,SecurityTreatAsSafe] internal CheckedPointer CheckedProbe(int offset, int length) { unsafe { if (_pointer == null || offset < 0 || offset > _size || offset + length > _size || offset + length < 0) throw new ArgumentOutOfRangeException(); return new CheckedPointer((byte*)_pointer + offset, length); } } ///Changes the buffer size of this CheckedPointer object. Used when memory block is resizable, ///like in a file mapping. ////// Critical: Passing an invalid size can calls future calls to Probe() to succeed even if the bounds of the buffer /// is being violated. This can cause the program to write to random memory. /// [SecurityCritical] internal void SetSize(int newSize) { _size = newSize; } ////// Critical: Contains unsafe code, which accesses _pointer field. /// TreatAsSafe: Only compares; does not expose pointer or do unsafe pointer operations. /// [SecurityCritical, SecurityTreatAsSafe] internal bool PointerEquals(CheckedPointer pointer) { unsafe { return _pointer == pointer._pointer; } } ////// Critical: Contains unsafe code. /// TreatAsSafe: Calls Probe and writes only the amount of data it probed for. /// [SecurityCritical, SecurityTreatAsSafe] internal void WriteBool(bool value) { unsafe { *(bool*)this.Probe(0, sizeof(bool)) = value; } } ////// Critical: Contains unsafe code. /// TreatAsSafe: Calls Probe and reads only the amount of data it probed for. /// [SecurityCritical, SecurityTreatAsSafe] internal bool ReadBool() { unsafe { return *(bool*)this.Probe(0, sizeof(bool)); } } ////// Critical: Holds reference to an unsafe pointer /// [SecurityCritical] private unsafe void * _pointer; ////// Critical: Having an invalid size can cause future calls to Probe() to succeed /// even if the bounds of the buffer is being violated. /// This can cause the program to write to random memory. /// This variable should really be SecurityCriticalDataForSet of int, /// but SecurityCriticalDataForSet is a generic defined in another assembly, /// and using it with a value type will cause JITting to happen. /// [SecurityCritical] private int _size; } ////// HashFn is a port of predefined hash functions from LKRHash /// [FriendAccessAllowed] internal static class HashFn { // Small prime number used as a multiplier in the supplied hash functions private const int HASH_MULTIPLIER = 101; internal static int HashMultiply(int hash) { return hash * HASH_MULTIPLIER; } ////// Distributes accumulated hash value across a range. /// Should be called just before returning hash code from a hash function. /// /// Hash value ///Scrambed hash value internal static int HashScramble(int hash) { // Here are 10 primes slightly greater than 10^9 // 1000000007, 1000000009, 1000000021, 1000000033, 1000000087, // 1000000093, 1000000097, 1000000103, 1000000123, 1000000181. // default value for "scrambling constant" const int RANDOM_CONSTANT = 314159269; // large prime number, also used for scrambling const uint RANDOM_PRIME = 1000000007; // we must cast to uint and back to int to correspond to current C++ behavior for operator% // since we have a matching hash function in native code uint a = (uint)(RANDOM_CONSTANT * hash); int b = (int)(a % RANDOM_PRIME); return b; } ////// Computes a hash code for a block of memory. /// One should not forget to call HashScramble before returning the final hash code value to the client. /// /// Pointer to a block of memory /// Size of the memory block in bytes /// Previous hash code to combine with ///Hash code ////// Critical - as this accesses unsafe code blocks and has the potential to /// corrupt memory. /// [SecurityCritical] internal unsafe static int HashMemory(void * pv, int numBytes, int hash) { byte * pb = (byte*)pv; while (numBytes-- > 0) { hash = HashMultiply(hash) + *pb; ++pb; } return hash; } internal static int HashString(string s, int hash) { foreach (char c in s) { hash = HashMultiply(hash) + (ushort)c; } return hash; } } ////// Utility functions for interaction with font cache service /// [FriendAccessAllowed] internal static class Util { internal const int nullOffset = -1; internal static string CompositeFontExtension { get { return SupportedExtensions[0]; } } private static readonly string[] SupportedExtensions = new string[] { // .COMPOSITEFONT must remain the first entry in this array // because IsSupportedFontExtension and IsCompositeFont relies on this. ".COMPOSITEFONT", ".OTF", ".TTC", ".TTF", ".TTE" }; private static readonly char[] InvalidFileNameChars = Path.GetInvalidFileNameChars(); internal const UriComponents UriWithoutFragment = UriComponents.AbsoluteUri & ~UriComponents.Fragment; private const string WinDir = "windir"; private const string EmptyFontFamilyReference = "#"; private const string EmptyCanonicalName = ""; private static object _dpiLock = new object(); private static int _dpi; private static bool _dpiInitialized = false; ////// Critical: This code elevates to get access to environment variable windir /// and exposes the local Windows fonts directory location. /// TreatAsSafe: This code uses critical fields to store critical data. /// [SecurityCritical, SecurityTreatAsSafe] static Util() { string s; //this code elevates to get access to the windir directory EnvironmentPermission environmentPermission = new EnvironmentPermission(EnvironmentPermissionAccess.Read, "Windir"); environmentPermission.Assert(); //Blessed Assert try { s = Environment.GetEnvironmentVariable(WinDir) + @"\Fonts\"; } finally { EnvironmentPermission.RevertAssert(); } _windowsFontsLocalPath = s.ToUpperInvariant(); _windowsFontsUriObject = new Uri(_windowsFontsLocalPath, UriKind.Absolute); // Characters that have reserved meanings (e.g., '%', '#', '/', etc.) remain escaped in the // string, but all others are not escaped. _windowsFontsUriString = _windowsFontsUriObject.GetComponents(UriComponents.AbsoluteUri, UriFormat.SafeUnescaped); } ////// Windows fonts Uri string in an unescaped form optimized for Uri manipulations. /// ////// Critical: Exposes the local Windows fonts directory location. /// [SecurityCritical] private static readonly string _windowsFontsLocalPath; ////// Critical: Exposes the local Windows fonts directory location. /// internal static string WindowsFontsLocalPath { [SecurityCritical] get { return _windowsFontsLocalPath; } } ////// Gets the number of physical pixels per DIP. For example, if the DPI of the rendering surface is 96 this /// value is 1.0f. If the DPI is 120, this value is 120.0f/96. /// internal static float PixelsPerDip { get { return ((float)Dpi) / 96; } } ////// Critical as this accesses Native methods. /// TreatAsSafe - it would be ok to expose this information - DPI in partial trust /// internal static int Dpi { [SecurityCritical, SecurityTreatAsSafe] get { if (!_dpiInitialized) { lock (_dpiLock) { if (!_dpiInitialized) { HandleRef desktopWnd = new HandleRef(null, IntPtr.Zero); // Win32Exception will get the Win32 error code so we don't have to IntPtr dc = MS.Win32.UnsafeNativeMethods.GetDC(desktopWnd); // Detecting error case from unmanaged call, required by PREsharp to throw a Win32Exception if (dc == IntPtr.Zero) { throw new Win32Exception(); } try { _dpi = MS.Win32.UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), NativeMethods.LOGPIXELSY); _dpiInitialized = true; } finally { MS.Win32.UnsafeNativeMethods.ReleaseDC(desktopWnd, new HandleRef(null, dc)); } } } } return _dpi; } } ////// Windows fonts Uri object. /// ////// Critical: Exposes the local Windows fonts directory location. /// [SecurityCritical] private static readonly Uri _windowsFontsUriObject; ////// Critical: Exposes the local Windows fonts directory location. /// internal static Uri WindowsFontsUriObject { [SecurityCritical] get { return _windowsFontsUriObject; } } ////// Windows fonts Uri string in an unescaped form. /// ////// Critical: Exposes the local Windows fonts directory location. /// [SecurityCritical] private static readonly string _windowsFontsUriString; ////// Critical: Exposes the local Windows fonts directory location. /// internal static string WindowsFontsUriString { [SecurityCritical] get { return _windowsFontsUriString; } } ////// Checks whether the specified location string or font family reference refers to the /// default Windows Fonts folder. /// ////// Returns true if the location part is empty or is a simple file name with no path /// characters (e.g., "#ARIAL" or "arial.ttf#ARIAL"). Returns false if the location /// is invalid or includes a path (e.g., "./#ARIAL", "..#ARIAL", "fonts/#ARIAL"). /// ////// This code is important for correcly interpreting font family references. /// For example if it returns true for ../arial.ttf#Arial (it shouldnt) then downstream code /// could be fooled into skipping a demand when loading data from a ttf file outside the /// Windows Fonts folder. /// ////// Critical - This method parse file paths. /// TreatAsSafe - Does not expose critical information. /// Does not have side effects. /// [SecurityCritical, SecurityTreatAsSafe] internal static bool IsReferenceToWindowsFonts(string s) { // Empty location always refers to Windows Fonts. if (string.IsNullOrEmpty(s) || s[0] == '#') return true; // Get the length of the location part. int length = s.IndexOf('#'); if (length < 0) length = s.Length; // Check for invalid file name characters in the location. These include reserved path // characters ('/', '\\', ':'), wildcards ('?', '*'), control characters, etc. if (s.IndexOfAny(InvalidFileNameChars, 0, length) >= 0) return false; // A font family reference is a URI reference and may contain escaped characters. We need // to check their unescaped values to protect against cases like this: // 1. Create canonical name by concatenating "file:///c:/windows/Fonts/" + "foo%2fbar.ttf#Arial" // 2. Later we create a Uri from the canonical name and pass to FontSource class // 3. FontSource gets Uri.LocalPath which returns "c:\windows\Fonts\foo\bar.ttf" // 4. Doh! for (int i = s.IndexOf('%', 0, length); i >= 0; i = s.IndexOf('%', i, length - i)) { // Get the unescaped character; this always advances i by at least 1. char unescapedChar = Uri.HexUnescape(s, ref i); // Is it a reserved character? if (Array.IndexOf(InvalidFileNameChars, unescapedChar) >= 0) return false; } // Check for special names "." and ".." which represent directories. Also reject // variations like "...", ".. ", etc., as none of these are valid file names. if (s[0] == '.') { // Advance past one or more '.' int i = 1; while (i < length && s[i] == '.') ++i; // advance past trailing spaces, e.g., ".. #Arial" while (i < length && char.IsWhiteSpace(s[i])) ++i; // return false if the whole location is just repeated '. followed by spaces if (i == length) return false; } // If we fall through to here the location part has no reserved or illegal characters. // The "file name" could still be a reserved device name like CON, PRN, etc., but since // none of these will have a known font extension we won't try to open the device. return true; } internal static bool IsSupportedSchemeForAbsoluteFontFamilyUri(Uri absoluteUri) { // The only absolute URIs we allow in font family references are "file:" URIs. return absoluteUri.IsFile; } internal static void SplitFontFaceIndex(Uri fontUri, out Uri fontSourceUri, out int faceIndex) { // extract face index string fragment = fontUri.GetComponents(UriComponents.Fragment, UriFormat.SafeUnescaped); if (!String.IsNullOrEmpty(fragment)) { if (!int.TryParse( fragment, NumberStyles.None, CultureInfo.InvariantCulture, out faceIndex )) { throw new ArgumentException(SR.Get(SRID.FaceIndexMustBePositiveOrZero), "fontUri"); } // face index was specified in a fragment, we need to strip off fragment from the source Uri fontSourceUri = new Uri(fontUri.GetComponents(Util.UriWithoutFragment, UriFormat.SafeUnescaped)); } else { // simple case, no face index specified faceIndex = 0; fontSourceUri = fontUri; } } internal static Uri CombineUriWithFaceIndex(string fontUri, int faceIndex) { if (faceIndex == 0) return new Uri(fontUri); // the Uri roundtrip is necessary for escaping possible '#' symbols in the folder path, // so that they don't conflict with the fragment part. string canonicalPathUri = new Uri(fontUri).GetComponents(UriComponents.AbsoluteUri, UriFormat.SafeUnescaped); string faceIndexString = faceIndex.ToString(CultureInfo.InvariantCulture); return new Uri(canonicalPathUri + '#' + faceIndexString); } internal static bool IsSupportedFontExtension(string extension, out bool isComposite) { for (int i = 0; i < SupportedExtensions.Length; ++i) { string supportedExtension = SupportedExtensions[i]; if (String.Compare(extension, supportedExtension, StringComparison.OrdinalIgnoreCase) == 0) { isComposite = (i == 0); // First array entry is *.CompositeFont return true; } } isComposite = false; return false; } internal static bool IsCompositeFont(string extension) { return (String.Compare(extension, CompositeFontExtension, StringComparison.OrdinalIgnoreCase) == 0); } internal static bool IsEnumerableFontUriScheme(Uri fontLocation) { bool isEnumerable = false; // We only support file:// and pack:// application Uris to reference logical fonts. if (fontLocation.IsAbsoluteUri) { if (fontLocation.IsFile) { // file scheme is always enumerable isEnumerable = true; } else if (fontLocation.Scheme == PackUriHelper.UriSchemePack) { // This is just an arbitrary file name which we use to construct a file URI. const string fakeFileName = "X"; // The fontLocation could be a folder-like URI even though the pack scheme does not allow this. // We simulate the concept of subfolders for packaged fonts. Before calling any PackUriHelper // methods, create a Uri which we know to be a file-like (rather than folder-like) URI. Uri fileUri; if (Uri.TryCreate(fontLocation, fakeFileName, out fileUri)) { isEnumerable = BaseUriHelper.IsPackApplicationUri(fileUri); } } } return isEnumerable; } internal static bool IsAppSpecificUri(Uri fontLocation) { // Only file:// Uris that refer to local drives can be cached across applications. // This function filters out some easy options, such as app specific pack:// Uris and // UNC paths. // Note that we make an assumption here that local drive letters stay the same across user sessions. // Also, we rely on session 0 not having access to any of the mapped drives. return !fontLocation.IsAbsoluteUri || !fontLocation.IsFile || fontLocation.IsUnc; } internal static string GetUriExtension(Uri uri) { string unescapedPath = uri.GetComponents(UriComponents.Path, UriFormat.Unescaped); return Path.GetExtension(unescapedPath); } /// /// Converts the specified portion of a friendly name to a normalized font family reference. /// ////// Friendly names use commas as delimeters so any literal commas must be escaped by /// doubling. If any doubled commas are present in the specified substring they are unescaped /// in the normalized font family reference. /// internal static string GetNormalizedFontFamilyReference(string friendlyName, int startIndex, int length) { if (friendlyName.IndexOf(',', startIndex, length) < 0) { // We don't need to unescape any commas. return NormalizeFontFamilyReference(friendlyName, startIndex, length); } else { // Unescape commas and then convert to normalized form. return NormalizeFontFamilyReference(friendlyName.Substring(startIndex, length).Replace(",,", ",")); } } ////// Converts a font family reference to a normalized form. /// private static string NormalizeFontFamilyReference(string fontFamilyReference) { return NormalizeFontFamilyReference(fontFamilyReference, 0, fontFamilyReference.Length); } ////// Converts a font family reference to normalized form. /// ////// In normalized form, the fragment separator ('#') is always present, and the family /// name part (i.e., everything after the fragment separator) has been converted to /// upper case. However, the case of the location part (if any) is preserved. /// ////// "Arial" --> "#ARIAL" /// "fonts/#My Font" --> "fonts/#MY FONT" /// "/fonts/#My Font" --> "/fonts/#MY FONT" /// private static string NormalizeFontFamilyReference(string fontFamilyReference, int startIndex, int length) { if (length == 0) return EmptyFontFamilyReference; int fragmentIndex = fontFamilyReference.IndexOf('#', startIndex, length); if (fragmentIndex < 0) { // No fragment separator. The entire string is a family name so convert to uppercase // and add a fragment separator at the beginning. return "#" + fontFamilyReference.Substring(startIndex, length).ToUpperInvariant(); } else if (fragmentIndex + 1 == startIndex + length) { // No family name part. return EmptyFontFamilyReference; } else if (fragmentIndex == startIndex) { // Empty location part; convert the whole string to uppercase. return fontFamilyReference.Substring(startIndex, length).ToUpperInvariant(); } else { // Convert the fragment to uppercase, but preserve the case of the location. string location = fontFamilyReference.Substring(startIndex, fragmentIndex - startIndex); string fragment = fontFamilyReference.Substring(fragmentIndex, (startIndex + length) - fragmentIndex); return location + fragment.ToUpperInvariant(); } } ////// Converts a font family name and location to a font family reference. /// /// A font family name with no characters escaped /// An optional location ///Returns a font family reference, which may be either a URI reference or just a fragment /// (with or without the '#' prefix) internal static string ConvertFamilyNameAndLocationToFontFamilyReference(string familyName, string location) { // Escape reserved characters in the family name. In the fragment, we need only // worry about literal percent signs ('%') to avoid confusion with the escape prefix // and literal pound signs ('#') to avoid confusion with the fragment separator. string fontFamilyReference = familyName.Replace("%", "%25").Replace("#", "%23"); // Is there a location part? if (!string.IsNullOrEmpty(location)) { // We just escaped the family name and the location part should already be a valid URI reference. fontFamilyReference = string.Concat( location, "#", fontFamilyReference ); } return fontFamilyReference; } ////// Converts a font family reference to a friendly name by escaping any literal commas. /// /// A font family reference ///Returns a friendly name. ///Single commas delimit multiple font family references in a friendly name so any /// commas in the specified string are replaced with double commas in the return value. /// internal static string ConvertFontFamilyReferenceToFriendlyName(string fontFamilyReference) { return fontFamilyReference.Replace(",", ",,"); } ////// Compares string using character ordinals. /// The comparison is case insensitive based on InvariantCulture. /// We have our own custom wrapper because we need to sort using the same algorithm /// we use in incremental charater search. /// There are subtle things (e.g. surrogates) that String.Compare() does and we don't. /// /// First input string. /// Second input string. ///Same semantics as for String.Compare internal static int CompareOrdinalIgnoreCase(string a, string b) { int aLength = a.Length; int bLength = b.Length; int minLength = Math.Min(aLength, bLength); for (int i = 0; i < minLength; ++i) { int result = CompareOrdinalIgnoreCase(a[i], b[i]); if (result != 0) return result; } return aLength - bLength; } private static int CompareOrdinalIgnoreCase(char a, char b) { char ca = Char.ToUpperInvariant(a); char cb = Char.ToUpperInvariant(b); return ca - cb; } // Makes sure the caller has path discovery permission for full fileName path. private static void ValidateFileNamePermissions(ref string fileName) { if (!SecurityHelper.CallerHasPathDiscoveryPermission(fileName)) { // If the caller didn't have path discovery permission for fileName, we can still give out relative file name. fileName = Path.GetFileName(fileName); } } ////// This function performs job similar to CLR's internal __Error.WinIOError function: /// it maps win32 errors from file I/O to CLR exceptions and includes string where possible. /// However, we're interested only in errors when opening a file for reading. /// /// Win32 error code. /// File name string. ////// Critical - As this function throws exception containing full file name, which can result in Information Disclosure. /// TreatAsSafe - As the function performs permission demand. /// [SecurityCritical, SecurityTreatAsSafe] internal static void ThrowWin32Exception(int errorCode, string fileName) { ValidateFileNamePermissions(ref fileName); switch (errorCode) { case NativeMethods.ERROR_FILE_NOT_FOUND: throw new FileNotFoundException(SR.Get(SRID.FileNotFoundExceptionWithFileName, fileName), fileName); case NativeMethods.ERROR_PATH_NOT_FOUND: throw new DirectoryNotFoundException(SR.Get(SRID.DirectoryNotFoundExceptionWithFileName, fileName)); case NativeMethods.ERROR_ACCESS_DENIED: throw new UnauthorizedAccessException(SR.Get(SRID.UnauthorizedAccessExceptionWithFileName, fileName)); case NativeMethods.ERROR_FILENAME_EXCED_RANGE: throw new PathTooLongException(SR.Get(SRID.PathTooLongExceptionWithFileName, fileName)); default: throw new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName), NativeMethods.MakeHRFromErrorCode(errorCode)); } } ////// Critical - As this function accesses font Uri that contains absolute font path. /// Safe - As we use ValidateFileNamePermissions to strip off the local path part for file Uris. /// [SecurityCritical, SecurityTreatAsSafe] internal static Exception ConvertInPageException(FontSource fontSource, SEHException e) { string fileName; if (fontSource.Uri.IsFile) { fileName = fontSource.Uri.LocalPath; ValidateFileNamePermissions(ref fileName); } else { fileName = fontSource.GetUriString(); } return new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName), e); } } ////// A class that wraps operations with Win32 memory sections and file mappings /// ////// This class could potentially contain critical information for the case /// where the data pointed to by the Mapping is obtained under an elevation. /// We consder FileMapping class itself to be security agnostic. When /// someone creates this instance from critical data, they'll need to mark /// the instance as SecurityCritical and track usage. For example if a call to /// OpenFile is made on an instance of FileMapping, that instance will be /// SecurityCritical. /// [FriendAccessAllowed] internal class FileMapping : UnmanagedMemoryStream { ~FileMapping() { Dispose(false); } ////// Critical: This method acceses critical elements _viewHandle and _mappingHandle /// TreatAsSafe: This data is not exposed and calling dispose is ok /// [SecurityCritical, SecurityTreatAsSafe] protected override void Dispose(bool disposing) { base.Dispose(disposing); if (!_disposed) { if (disposing) { if (_viewHandle != null) _viewHandle.Dispose(); if (_mappingHandle != null) _mappingHandle.Dispose(); } // We only handle flat disk files read only, should never be writeable. Invariant.Assert(!CanWrite); } _disposed = true; } ////// Critical - As this function calls functions CreateFileMapping, /// MapViewOfFileEx and Initialize under elevation. /// Any instance of FileMapping /// object on which this function is called becomes a critical /// instance and its usage will need to be tracked for audit. /// [SecurityCritical] internal void OpenFile(string fileName) { NativeMethods.SECURITY_ATTRIBUTES sa = new NativeMethods.SECURITY_ATTRIBUTES(); using (sa) { unsafe { // Disable PREsharp warning about not calling Marshal.GetLastWin32Error, // because we already check the handle for invalid value and // we are not particularly interested in specific Win32 error. #pragma warning disable 6523 long size; using (SafeFileHandle fileHandle = UnsafeNativeMethods.CreateFile( fileName, NativeMethods.GENERIC_READ, NativeMethods.FILE_SHARE_READ, null, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero )) { if (fileHandle.IsInvalid) { Util.ThrowWin32Exception(Marshal.GetLastWin32Error(), fileName); } UnsafeNativeMethods.LARGE_INTEGER fileSize = new UnsafeNativeMethods.LARGE_INTEGER(); if (!UnsafeNativeMethods.GetFileSizeEx(fileHandle, ref fileSize)) throw new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName)); size = (long)fileSize.QuadPart; if (size == 0) throw new FileFormatException(new Uri(fileName)); _mappingHandle = UnsafeNativeMethods.CreateFileMapping( fileHandle, sa, UnsafeNativeMethods.PAGE_READONLY, 0, 0, null); } if (_mappingHandle.IsInvalid) throw new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName)); _viewHandle = UnsafeNativeMethods.MapViewOfFileEx(_mappingHandle, UnsafeNativeMethods.FILE_MAP_READ, 0, 0, IntPtr.Zero, IntPtr.Zero); if (_viewHandle.IsInvalid) throw new IOException(SR.Get(SRID.IOExceptionWithFileName, fileName)); #pragma warning restore 6523 // Initialize() method demands UnmanagedCode permission, and OpenFile() is already marked as critical. new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); //Blessed Assert try { Initialize((byte*)_viewHandle.Memory, size, size, FileAccess.Read); } finally { SecurityPermission.RevertAssert(); } } } } ////// Critical: This element holds reference to an object retrieved under an elevation /// [SecurityCritical] private UnsafeNativeMethods.SafeViewOfFileHandle _viewHandle; ////// Critical: This element holds reference to an object retrieved under an elevation /// [SecurityCritical] private UnsafeNativeMethods.SafeFileMappingHandle _mappingHandle; private bool _disposed = false; } internal class LocalizedName { internal LocalizedName(XmlLanguage language, string name) : this(language, name, language.GetEquivalentCulture().LCID) {} internal LocalizedName(XmlLanguage language, string name, int originalLCID) { _language = language; _name = name; _originalLCID = originalLCID; } internal XmlLanguage Language { get { return _language; } } internal string Name { get { return _name; } } internal int OriginalLCID { get { return _originalLCID; } } internal static IComparerNameComparer { get { return _nameComparer; } } internal static IComparer LanguageComparer { get { return _languageComparer; } } private class NameComparerClass : IComparer { #region IComparer Members int IComparer .Compare(LocalizedName x, LocalizedName y) { return Util.CompareOrdinalIgnoreCase(x._name, y._name); } #endregion } private class LanguageComparerClass : IComparer { #region IComparer Members int IComparer .Compare(LocalizedName x, LocalizedName y) { return String.Compare(x._language.IetfLanguageTag, y._language.IetfLanguageTag, StringComparison.OrdinalIgnoreCase); } #endregion } private XmlLanguage _language; // the language identifier private string _name; // name converted to Unicode private int _originalLCID; // original LCID, used in cases when we want to preserve it to avoid information loss private static NameComparerClass _nameComparer = new NameComparerClass(); private static LanguageComparerClass _languageComparer = new LanguageComparerClass(); } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- FaultPropagationRecord.cs
- ExpressionPrefixAttribute.cs
- TypeListConverter.cs
- AspNetHostingPermission.cs
- ArraySubsetEnumerator.cs
- PrimaryKeyTypeConverter.cs
- BamlBinaryReader.cs
- ExplicitDiscriminatorMap.cs
- CacheMemory.cs
- DesignerResources.cs
- SqlConnectionFactory.cs
- ProgramPublisher.cs
- SHA512Cng.cs
- _LocalDataStoreMgr.cs
- AmbientValueAttribute.cs
- DataGridViewToolTip.cs
- DecoderBestFitFallback.cs
- JpegBitmapEncoder.cs
- NullableLongMinMaxAggregationOperator.cs
- SettingsSavedEventArgs.cs
- BrushProxy.cs
- PolicyManager.cs
- AuthenticationConfig.cs
- HttpRawResponse.cs
- XXXInfos.cs
- IdentitySection.cs
- WsatServiceAddress.cs
- SiteMembershipCondition.cs
- DescriptionAttribute.cs
- ManifestResourceInfo.cs
- XmlSchemaImporter.cs
- TemplateComponentConnector.cs
- PassportIdentity.cs
- DrawingBrush.cs
- PersonalizationStateInfo.cs
- TemplateInstanceAttribute.cs
- FormParameter.cs
- WindowsSolidBrush.cs
- DataSetFieldSchema.cs
- ExtentJoinTreeNode.cs
- SpAudioStreamWrapper.cs
- UserNameSecurityTokenProvider.cs
- ConnectionManagementElementCollection.cs
- CssTextWriter.cs
- LifetimeServices.cs
- SessionState.cs
- BinaryExpressionHelper.cs
- DataGridViewRowHeaderCell.cs
- TextBoxView.cs
- xsdvalidator.cs
- ArraySet.cs
- CharacterHit.cs
- Enumerable.cs
- LedgerEntry.cs
- DataGridViewRow.cs
- EntityDataSourceViewSchema.cs
- SmtpNetworkElement.cs
- WebPartEditorApplyVerb.cs
- ConstrainedDataObject.cs
- SHA1Managed.cs
- MasterPageCodeDomTreeGenerator.cs
- DataGridViewSelectedCellCollection.cs
- SymLanguageVendor.cs
- InputLanguage.cs
- NativeMethods.cs
- DataAdapter.cs
- PKCS1MaskGenerationMethod.cs
- GuidelineCollection.cs
- EmbeddedMailObject.cs
- HttpCacheVaryByContentEncodings.cs
- PageRouteHandler.cs
- XmlSerializerAssemblyAttribute.cs
- Win32NamedPipes.cs
- ProxyGenerationError.cs
- OrderByBuilder.cs
- BamlBinaryReader.cs
- VectorAnimation.cs
- Vars.cs
- Int16Animation.cs
- BinaryFormatter.cs
- Repeater.cs
- FunctionCommandText.cs
- TextUtf8RawTextWriter.cs
- ObjectTypeMapping.cs
- SqlTriggerAttribute.cs
- XPathSelectionIterator.cs
- DataReceivedEventArgs.cs
- IfAction.cs
- TileBrush.cs
- Queue.cs
- TimelineGroup.cs
- CompiledXpathExpr.cs
- Context.cs
- TextServicesManager.cs
- peersecurityelement.cs
- CodeSnippetStatement.cs
- altserialization.cs
- AuthStoreRoleProvider.cs
- QueryOpeningEnumerator.cs
- UriSection.cs