Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / System / Windows / Media / Imaging / BitmapDecoder.cs / 1305600 / BitmapDecoder.cs
//------------------------------------------------------------------------------ // Microsoft Avalon // Copyright (c) Microsoft Corporation, 2003 // // File: BitmapDecoder.cs // //----------------------------------------------------------------------------- #pragma warning disable 1634, 1691 // Allow suppression of certain presharp messages using System; using System.IO; using System.IO.Packaging; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Collections.ObjectModel; using System.Reflection; using System.Security; using System.Security.Permissions; using System.Text.RegularExpressions; using MS.Internal; using MS.Internal.AppModel; using MS.Win32.PresentationCore; using Microsoft.Win32.SafeHandles; using System.Diagnostics; using System.Windows.Media; using System.Globalization; using System.Runtime.InteropServices; using System.Threading; using System.Windows.Threading; using System.Windows.Media.Imaging; using MS.Internal.PresentationCore; // SecurityHelper using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; using System.Net; using System.Net.Cache; using System.Text; namespace System.Windows.Media.Imaging { #region BitmapDecoder ////// BitmapDecoder is a container for bitmap frames. Each bitmap frame is an BitmapFrame. /// Any BitmapFrame it returns are frozen /// be immutable. /// public abstract class BitmapDecoder : DispatcherObject { #region Constructors ////// Critical: This code has critical static variables isImageDisabledInitialized /// TreatAsASafe: The variables are not exposed /// [SecurityCritical,SecurityTreatAsSafe] static BitmapDecoder() { isImageDisabledInitialized = false; } ////// Default constructor /// protected BitmapDecoder() { } ////// Internal constructor /// internal BitmapDecoder(bool isBuiltIn) { _isBuiltInDecoder = isBuiltIn; } ////// Constructor /// ////// Critical - access critical resource /// [SecurityCritical] internal BitmapDecoder( Uri bitmapUri, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, Guid expectedClsId ) { Guid clsId = Guid.Empty; bool isOriginalWritable = false; if (bitmapUri == null) { throw new ArgumentNullException("bitmapUri"); } if ((createOptions & BitmapCreateOptions.IgnoreImageCache) != 0) { ImagingCache.RemoveFromDecoderCache(bitmapUri); } BitmapDecoder decoder = CheckCache(bitmapUri, out clsId); if (decoder != null) { _decoderHandle = decoder.InternalDecoder; } else { _decoderHandle = SetupDecoderFromUriOrStream( bitmapUri, null, cacheOption, out clsId, out isOriginalWritable, out _uriStream, out _unmanagedMemoryStream, out _safeFilehandle ); if (_uriStream == null) { GC.SuppressFinalize(this); } } if (clsId != expectedClsId) { throw new FileFormatException(bitmapUri, SR.Get(SRID.Image_CantDealWithUri)); } _uri = bitmapUri; _createOptions = createOptions; _cacheOption = cacheOption; _syncObject = _decoderHandle; _isOriginalWritable = isOriginalWritable; Initialize(decoder); } ////// Constructor /// ////// Critical - access critical resource /// [SecurityCritical] internal BitmapDecoder( Stream bitmapStream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, Guid expectedClsId ) { Guid clsId = Guid.Empty; bool isOriginalWritable = false; if (bitmapStream == null) { throw new ArgumentNullException("bitmapStream"); } _decoderHandle = SetupDecoderFromUriOrStream( null, bitmapStream, cacheOption, out clsId, out isOriginalWritable, out _uriStream, out _unmanagedMemoryStream, out _safeFilehandle ); if (_uriStream == null) { GC.SuppressFinalize(this); } if (clsId != Guid.Empty && clsId != expectedClsId) { throw new FileFormatException(null, SR.Get(SRID.Image_CantDealWithStream)); } _stream = bitmapStream; _createOptions = createOptions; _cacheOption = cacheOption; _syncObject = _decoderHandle; _isOriginalWritable = isOriginalWritable; Initialize(null); } ////// Constructor /// ////// Critical: strictly speaking because the method uses a SafeFileHandle, which is a SecurityCritical /// type (in v4). But it also takes other resources as input that untrusted code shouldn't have /// access to (SafeMILHandle, UnmanagedMemoryStream). /// [SecurityCritical] internal BitmapDecoder( SafeMILHandle decoderHandle, BitmapDecoder decoder, Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, bool insertInDecoderCache, bool isOriginalWritable, Stream uriStream, UnmanagedMemoryStream unmanagedMemoryStream, SafeFileHandle safeFilehandle ) { _decoderHandle = decoderHandle; _baseUri = baseUri; _uri = uri; _stream = stream; _createOptions = createOptions; _cacheOption = cacheOption; _syncObject = decoderHandle; _shouldCacheDecoder = insertInDecoderCache; _isOriginalWritable = isOriginalWritable; _uriStream = uriStream; _unmanagedMemoryStream = unmanagedMemoryStream; _safeFilehandle = safeFilehandle; if (_uriStream == null) { GC.SuppressFinalize(this); } Initialize(decoder); } ////// Close the stream pointing to the file after this BitmapDecoder is no longer used. /// It might have been closed before in Initialize if the cache option was OnLoad or /// on decode if the cache option was OnDemand/Default. In those cases, the finalizer /// would have been suppressed. /// ////// Critical - accesses critical resource _uriStream /// TreatAsSafe - doesn't return the uriStream, just closes it which is safe /// [SecurityCritical, SecurityTreatAsSafe] ~BitmapDecoder() { // // Normally _uriStream gets GCed because this BitmapDecoder is the only thing that // references it, and the stream's finalizer closes it. However, in some cases, other // objects still have references to the _uriStream, so it won't get GCed because this // decoder does. One such case is getting the stream from a pack uri that points to // a resource. In those cases, PackagePart created the streams and is also holding on // to them. It won't let go unless the stream is closed, but the stream won't close // unless it's been let go, so the stream is leaked. Closing the stream removes the // leak. // // _uriStream is always created within SetupDecoderFromUriOrStream. Either it's set // using an out parameter, or it's set in the internal constructor that takes a // uriStream parameter. That internal constructor is called by the sealed subclasses // of BitmapDecoder (such as JpegBitmapDecoder), those subclasses are constructed in // CreateFromUriOrStream, which also gets uriStream from SetupDecoderFromUriOrStream. // if (_uriStream != null) { _uriStream.Close(); } } ////// Create BitmapDecoder from the uri or stream. If both are specified, the uri /// is chosen. /// ////// Critical - Unmanaged code will eventually create a COM object, need to secure it /// Calls one of the XxxBitmapDecoder static internal ctors, which are all SecurityCritical. /// TreatAsSafe - All inputs verified /// The inputs of unsafe types that are passed to XxxBitmapDecoder are produced within this method, /// not taken from outside. /// [SecurityCritical, SecurityTreatAsSafe] internal static BitmapDecoder CreateFromUriOrStream( Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy, bool insertInDecoderCache ) { Guid clsId = Guid.Empty; bool isOriginalWritable = false; SafeMILHandle decoderHandle = null; BitmapDecoder cachedDecoder = null; Uri finalUri = null; Stream uriStream = null; UnmanagedMemoryStream unmanagedMemoryStream = null; SafeFileHandle safeFilehandle = null; // check to ensure that images are allowed in partial trust DemandIfImageBlocked(); if (uri != null) { finalUri = (baseUri != null) ? System.Windows.Navigation.BaseUriHelper.GetResolvedUri(baseUri, uri) : uri; if (insertInDecoderCache) { if ((createOptions & BitmapCreateOptions.IgnoreImageCache) != 0) { ImagingCache.RemoveFromDecoderCache(finalUri); } cachedDecoder = CheckCache( finalUri, out clsId ); } } // try to retrieve the cached decoder if (cachedDecoder != null) { decoderHandle = cachedDecoder.InternalDecoder; } else if ((finalUri != null) && (finalUri.IsAbsoluteUri) && (stream == null) && ((finalUri.Scheme == Uri.UriSchemeHttp) || (finalUri.Scheme == Uri.UriSchemeHttps))) { return new LateBoundBitmapDecoder(baseUri, uri, stream, createOptions, cacheOption, uriCachePolicy); } else if ((stream != null) && (!stream.CanSeek)) { return new LateBoundBitmapDecoder(baseUri, uri, stream, createOptions, cacheOption, uriCachePolicy); } else { // Create an unmanaged decoder decoderHandle = BitmapDecoder.SetupDecoderFromUriOrStream( finalUri, stream, cacheOption, out clsId, out isOriginalWritable, out uriStream, out unmanagedMemoryStream, out safeFilehandle ); } BitmapDecoder decoder = null; // Find out the decoder type and wrap it appropriately and return that if (MILGuidData.GUID_ContainerFormatBmp == clsId) { decoder = new BmpBitmapDecoder( decoderHandle, cachedDecoder, baseUri, uri, stream, createOptions, cacheOption, insertInDecoderCache, isOriginalWritable, uriStream, unmanagedMemoryStream, safeFilehandle ); } else if (MILGuidData.GUID_ContainerFormatGif == clsId) { decoder = new GifBitmapDecoder( decoderHandle, cachedDecoder, baseUri, uri, stream, createOptions, cacheOption, insertInDecoderCache, isOriginalWritable, uriStream, unmanagedMemoryStream, safeFilehandle ); } else if (MILGuidData.GUID_ContainerFormatIco == clsId) { decoder = new IconBitmapDecoder( decoderHandle, cachedDecoder, baseUri, uri, stream, createOptions, cacheOption, insertInDecoderCache, isOriginalWritable, uriStream, unmanagedMemoryStream, safeFilehandle ); } else if (MILGuidData.GUID_ContainerFormatJpeg == clsId) { decoder = new JpegBitmapDecoder( decoderHandle, cachedDecoder, baseUri, uri, stream, createOptions, cacheOption, insertInDecoderCache, isOriginalWritable, uriStream, unmanagedMemoryStream, safeFilehandle ); } else if (MILGuidData.GUID_ContainerFormatPng == clsId) { decoder = new PngBitmapDecoder( decoderHandle, cachedDecoder, baseUri, uri, stream, createOptions, cacheOption, insertInDecoderCache, isOriginalWritable, uriStream, unmanagedMemoryStream, safeFilehandle ); } else if (MILGuidData.GUID_ContainerFormatTiff == clsId) { decoder = new TiffBitmapDecoder( decoderHandle, cachedDecoder, baseUri, uri, stream, createOptions, cacheOption, insertInDecoderCache, isOriginalWritable, uriStream, unmanagedMemoryStream, safeFilehandle ); } else if (MILGuidData.GUID_ContainerFormatWmp == clsId) { decoder = new WmpBitmapDecoder( decoderHandle, cachedDecoder, baseUri, uri, stream, createOptions, cacheOption, insertInDecoderCache, isOriginalWritable, uriStream, unmanagedMemoryStream, safeFilehandle ); } else { decoder = new UnknownBitmapDecoder( decoderHandle, cachedDecoder, baseUri, uri, stream, createOptions, cacheOption, insertInDecoderCache, isOriginalWritable, uriStream, unmanagedMemoryStream, safeFilehandle ); } return decoder; } ////// Create a BitmapDecoder from a Uri with the specified BitmapCreateOptions and /// BitmapCacheOption /// /// Uri to decode /// Bitmap Create Options /// Bitmap Caching Option public static BitmapDecoder Create( Uri bitmapUri, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption ) { return Create(bitmapUri, createOptions, cacheOption, null); } ////// Create a BitmapDecoder from a Uri with the specified BitmapCreateOptions and /// BitmapCacheOption /// /// Uri to decode /// Bitmap Create Options /// Bitmap Caching Option /// Optional web request cache policy public static BitmapDecoder Create( Uri bitmapUri, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy ) { if (bitmapUri == null) { throw new ArgumentNullException("bitmapUri"); } return CreateFromUriOrStream( null, bitmapUri, null, createOptions, cacheOption, uriCachePolicy, true ); } ////// Create a BitmapDecoder from a Stream with the specified BitmapCreateOptions and /// BitmapCacheOption /// /// Stream to decode /// Bitmap Create Options /// Bitmap Caching Option public static BitmapDecoder Create( Stream bitmapStream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption ) { if (bitmapStream == null) { throw new ArgumentNullException("bitmapStream"); } return CreateFromUriOrStream( null, null, bitmapStream, createOptions, cacheOption, null, true ); } #endregion #region Properties ////// If there is an palette, return it. /// Otherwise, return null. /// ////// Critical - Access unmanaged code, codecs /// PublicOK - Getting palette data is OK /// public virtual BitmapPalette Palette { [SecurityCritical] get { VerifyAccess(); EnsureBuiltInDecoder(); if (!_isPaletteCached) { SafeMILHandle /* IWICBitmapPalette */ paletteHandle = BitmapPalette.CreateInternalPalette(); lock (_syncObject) { int hr = UnsafeNativeMethods.WICBitmapDecoder.CopyPalette( _decoderHandle, paletteHandle ); if (hr != (int)WinCodecErrors.WINCODEC_ERR_PALETTEUNAVAILABLE) { HRESULT.Check(hr); _palette = new BitmapPalette(paletteHandle); } } _isPaletteCached = true; } return _palette; } } ////// If there is an embedded color profile, return it. /// Otherwise, return null. /// public virtual ReadOnlyCollectionColorContexts { get { VerifyAccess(); return InternalColorContexts; } } /// /// If there is a global thumbnail, return it. /// Otherwise, return null. The returned source is frozen. /// ////// Critical - Access unmanaged code, codecs /// PublicOK - Getting thumbnail data is OK /// public virtual BitmapSource Thumbnail { [SecurityCritical ] get { VerifyAccess(); EnsureBuiltInDecoder(); if (!_isThumbnailCached) { IntPtr /* IWICBitmapSource */ thumbnail = IntPtr.Zero; // Check if there is embedded thumbnail or not lock (_syncObject) { int hr = UnsafeNativeMethods.WICBitmapDecoder.GetThumbnail( _decoderHandle, out thumbnail ); if (hr != (int)WinCodecErrors.WINCODEC_ERR_CODECNOTHUMBNAIL) { HRESULT.Check(hr); } } if (thumbnail != IntPtr.Zero) { BitmapSourceSafeMILHandle thumbHandle = new BitmapSourceSafeMILHandle(thumbnail); SafeMILHandle unmanagedPalette = BitmapPalette.CreateInternalPalette(); BitmapPalette palette = null; int hr = UnsafeNativeMethods.WICBitmapSource.CopyPalette( thumbHandle, unmanagedPalette ); if (hr == HRESULT.S_OK) { palette = new BitmapPalette(unmanagedPalette); } _thumbnail = new UnmanagedBitmapWrapper( BitmapSource.CreateCachedBitmap( null, thumbHandle, BitmapCreateOptions.PreservePixelFormat, _cacheOption, palette )); _thumbnail.Freeze(); } _isThumbnailCached = true; } return _thumbnail; } } ////// If there is a global metadata, return it. /// Otherwise, return null. The returned source is frozen. /// ////// Critical - Access unmanaged code, codecs /// Public - /// Getting metadata for images from site of origin or bundled with the application is OK /// Getting metatdat for images from cross domain servers triggers a demand for site of origin. /// public virtual BitmapMetadata Metadata { [SecurityCritical] get { VerifyAccess(); EnsureBuiltInDecoder(); // Demand Site Of origin on the URI if it passes then this information is ok to expose CheckIfSiteOfOrigin(); if (!_isMetadataCached) { IntPtr /* IWICMetadataQueryReader */ metadata = IntPtr.Zero; lock (_syncObject) { int hr = UnsafeNativeMethods.WICBitmapDecoder.GetMetadataQueryReader( _decoderHandle, out metadata ); if (hr != (int)WinCodecErrors.WINCODEC_ERR_UNSUPPORTEDOPERATION) { HRESULT.Check(hr); } } if (metadata != IntPtr.Zero) { SafeMILHandle metadataHandle = new SafeMILHandle(metadata); _metadata = new BitmapMetadata(metadataHandle, true, IsMetadataFixedSize, _syncObject); _metadata.Freeze(); } _isMetadataCached = true; } return _metadata; } } ////// The info that identifies this codec. /// ////// Critical - Access unmanaged code, codecs /// PublicOK - Getting codecinfo data is OK /// public virtual BitmapCodecInfo CodecInfo { [SecurityCritical] get { VerifyAccess(); EnsureBuiltInDecoder(); // There should always be a codec info. if (_codecInfo == null) { SafeMILHandle /* IWICBitmapDecoderInfo */ codecInfoHandle = new SafeMILHandle(); HRESULT.Check(UnsafeNativeMethods.WICBitmapDecoder.GetDecoderInfo( _decoderHandle, out codecInfoHandle )); _codecInfo = new BitmapCodecInfoInternal(codecInfoHandle); } return _codecInfo; } } ////// Access to the individual frames. All BitmapFrames are frozen. /// public virtual ReadOnlyCollectionFrames { get { VerifyAccess(); EnsureBuiltInDecoder(); if (_frames == null) { SetupFrames(null, null); } if (_readOnlyFrames == null) { _readOnlyFrames = new ReadOnlyCollection (_frames); } return _readOnlyFrames; } } /// /// If there is a global preview image, return it. /// Otherwise, return null. The returned source is frozen. /// ////// Critical - Access unmanaged code, codecs /// PublicOK - Getting preview data is OK /// public virtual BitmapSource Preview { [SecurityCritical] get { VerifyAccess(); EnsureBuiltInDecoder(); if (!_isPreviewCached) { IntPtr /* IWICBitmapSource */ preview = IntPtr.Zero; lock (_syncObject) { // Check if there is embedded preview or not int hr = UnsafeNativeMethods.WICBitmapDecoder.GetPreview( _decoderHandle, out preview ); if (hr != (int)WinCodecErrors.WINCODEC_ERR_UNSUPPORTEDOPERATION) { HRESULT.Check(hr); } } if (preview != IntPtr.Zero) { BitmapSourceSafeMILHandle previewHandle = new BitmapSourceSafeMILHandle(preview); SafeMILHandle unmanagedPalette = BitmapPalette.CreateInternalPalette(); BitmapPalette palette = null; int hr = UnsafeNativeMethods.WICBitmapSource.CopyPalette( previewHandle, unmanagedPalette ); if (hr == HRESULT.S_OK) { palette = new BitmapPalette(unmanagedPalette); } _preview = new UnmanagedBitmapWrapper( BitmapSource.CreateCachedBitmap( null, previewHandle, BitmapCreateOptions.PreservePixelFormat, _cacheOption, palette )); _preview.Freeze(); } _isPreviewCached = true; } return _preview; } } ////// Returns true if the decoder is downloading content /// public virtual bool IsDownloading { get { VerifyAccess(); EnsureBuiltInDecoder(); return false; } } ////// Raised when decoder has completed downloading content /// May not be raised for all content. /// public virtual event EventHandler DownloadCompleted { add { VerifyAccess(); EnsureBuiltInDecoder(); _downloadEvent.AddEvent(value); } remove { VerifyAccess(); EnsureBuiltInDecoder(); _downloadEvent.RemoveEvent(value); } } ////// Raised when download has progressed /// May not be raised for all content. /// public virtual event EventHandlerDownloadProgress { add { VerifyAccess(); EnsureBuiltInDecoder(); _progressEvent.AddEvent(value); } remove { VerifyAccess(); EnsureBuiltInDecoder(); _progressEvent.RemoveEvent(value); } } /// /// Raised when download has failed /// May not be raised for all content. /// public virtual event EventHandlerDownloadFailed { add { VerifyAccess(); EnsureBuiltInDecoder(); _failedEvent.AddEvent(value); } remove { VerifyAccess(); EnsureBuiltInDecoder(); _failedEvent.RemoveEvent(value); } } #endregion #region Public Properties /// /// Create an in-place bitmap metadata writer. /// public virtual InPlaceBitmapMetadataWriter CreateInPlaceBitmapMetadataWriter() { VerifyAccess(); EnsureBuiltInDecoder(); CheckOriginalWritable(); // Demand Site Of origin on the URI if it passes then this information is ok to expose CheckIfSiteOfOrigin(); return InPlaceBitmapMetadataWriter.CreateFromDecoder(_decoderHandle, _syncObject); } #endregion #region ToString ////// ToString /// public override string ToString() { VerifyAccess(); if (!_isBuiltInDecoder) { return base.ToString(); } if (_uri != null) { if (_baseUri != null) { Uri uri = new Uri(_baseUri, _uri); return BindUriHelper.UriToString(uri); } else { return BindUriHelper.UriToString(_uri); } } else { return SafeSecurityHelper.IMAGE; } } #endregion #region Internal Properties ////// Internal Decoder /// internal SafeMILHandle InternalDecoder { get { return _decoderHandle; } } ////// Returns whether metadata is fixed size or not. /// internal virtual bool IsMetadataFixedSize { get { return false; } } ////// Synchronization Object. Any unmanaged PInvoke/call that requires synchronization /// must lock on the [....] object. This object must be internal or private so as not /// to be publicly lockable. /// internal object SyncObject { get { Debug.Assert(_syncObject != null); return _syncObject; } } ////// Used as a delegate in InternalColorContexts to get the unmanaged IWICColorContexts /// ////// Critical - Calls critical, unmanaged method /// [SecurityCritical] private int GetColorContexts(ref uint numContexts, IntPtr[] colorContextPtrs) { Invariant.Assert(colorContextPtrs == null || numContexts <= colorContextPtrs.Length); return UnsafeNativeMethods.WICBitmapDecoder.GetColorContexts(_decoderHandle, numContexts, colorContextPtrs, out numContexts); } ////// If there is an embedded color profile, return it. /// Otherwise, return null. /// ////// Critical - Access unmanaged code, codecs /// TreatAsSafe - We are exposing specific, non-sensitive data that came from /// a Uri or a bitmap. If the profile data came from a Uri, the /// WebRequest we did to get it demandend the appropriate /// permission and we did not expose the request or response. If it came /// from a bitmap, it may be cross-domain, but the restricted /// nature of the data combined with the fact that it has been /// inspected by WCS which is hardened against malicious data /// makes this OK. /// internal ReadOnlyCollectionInternalColorContexts { [SecurityCritical, SecurityTreatAsSafe] get { EnsureBuiltInDecoder(); if (!_isColorContextCached) { lock (_syncObject) { IList colorContextList = ColorContext.GetColorContextsHelper(GetColorContexts); if (colorContextList != null) { _readOnlycolorContexts = new ReadOnlyCollection (colorContextList); } _isColorContextCached = true; } } return _readOnlycolorContexts; } } /// /// Checks whether the underlying source is writable (useful for in-place metadata editing) /// internal void CheckOriginalWritable() { if (!_isOriginalWritable) { throw new System.InvalidOperationException(SR.Get(SRID.Image_OriginalStreamReadOnly)); } } #endregion #region Internal/Private Methods ////// Critical: This sets and accesses critical members isImageDisabledInitialized,isImageDisabled /// TreatAsSafe: This code does not expose the variables and is not influenced by external paramaters that do not /// require unmanaged code access permission to set /// [SecurityCritical, SecurityTreatAsSafe] private static void DemandIfImageBlocked() { if(!isImageDisabledInitialized) { // a performance optimization to ensure we hit the registry only once in the lifetime of the application isImageDisabled = new SecurityCriticalDataForSet(SafeSecurityHelper.IsFeatureDisabled(SafeSecurityHelper.KeyToRead.MediaImageDisable)); isImageDisabledInitialized = true; } if (isImageDisabled.Value) { // in case the registry key is '1' then demand MediaPermissionImage.AllImage - not granted in Partial Trust SecurityHelper.DemandMediaPermission(MediaPermissionAudio.NoAudio, MediaPermissionVideo.NoVideo, MediaPermissionImage.AllImage); } else { // Images are enabled. Then, demand permissions for safe imaging - granted in Partial Trust by default SecurityHelper.DemandMediaPermission(MediaPermissionAudio.NoAudio, MediaPermissionVideo.NoVideo, MediaPermissionImage.SafeImage); } } /// /// Critical - Unmanaged code will eventually create a COM object, need to secure it /// [SecurityCritical] internal static SafeMILHandle SetupDecoderFromUriOrStream( Uri uri, Stream stream, BitmapCacheOption cacheOption, out Guid clsId, out bool isOriginalWritable, out Stream uriStream, out UnmanagedMemoryStream unmanagedMemoryStream, out SafeFileHandle safeFilehandle ) { SafeMILHandle decoderHandle; IntPtr decoder = IntPtr.Zero; System.IO.Stream bitmapStream = null; string mimeType = String.Empty; // check to ensure that images are allowed in partial trust NOP in full trust DemandIfImageBlocked(); unmanagedMemoryStream = null; safeFilehandle = null; isOriginalWritable = false; uriStream = null; if ((uri != null) && (stream != null)) { // In this case we expect the Uri to be http(s) Debug.Assert((uri.Scheme == Uri.UriSchemeHttp) || (uri.Scheme == Uri.UriSchemeHttps)); Debug.Assert(stream.CanSeek); } // Uri if (uri != null) { if (uri.IsAbsoluteUri) { // In this case we first check to see if the consumer has media permissions for // safe media (Site of Origin + Cross domain) SecurityHelper.DemandMediaPermission(MediaPermissionAudio.NoAudio, MediaPermissionVideo.NoVideo, MediaPermissionImage.SiteOfOriginImage) ; // This code path executes only for pack web requests if (String.Compare(uri.Scheme, PackUriHelper.UriSchemePack, StringComparison.OrdinalIgnoreCase) == 0) { WebResponse response = WpfWebRequestHelper.CreateRequestAndGetResponse(uri); mimeType = response.ContentType; bitmapStream = response.GetResponseStream(); uriStream = bitmapStream; } } if ((bitmapStream == null) || (bitmapStream == System.IO.Stream.Null)) { // We didn't get a stream from the pack web request, so we have // to try to create one ourselves. if (uri.IsAbsoluteUri) { // The Uri class can't tell if it is a file unless it // has an absolute path. int targetZone = SecurityHelper.MapUrlToZoneWrapper(uri); if (targetZone == MS.Win32.NativeMethods.URLZONE_LOCAL_MACHINE) { if (uri.IsFile) { // FileStream does a demand for us, so no need to do a demand bitmapStream = new System.IO.FileStream(uri.LocalPath, FileMode.Open, FileAccess.Read, FileShare.Read); } } else // Any other zone { // UNC path for a file which is not http if (uri.IsFile && uri.IsUnc) // for UNC { bitmapStream = ProcessUncFiles(uri); } else if (uri.Scheme == Uri.UriSchemeHttp) // for http { bitmapStream = ProcessHttpFiles(uri,stream); } else if (uri.Scheme == Uri.UriSchemeHttps) // for https { bitmapStream = ProcessHttpsFiles(uri,stream); } else { // The Uri is a custom Uri. Try to grab the stream from its WebResponse. bitmapStream = WpfWebRequestHelper.CreateRequestAndGetResponseStream(uri); } } } else { #pragma warning disable 6518 // We don't have an absolute URI, so we don't necessarily know // if it is a file, but we'll have to assume it is and try to // create a stream from the original string. bitmapStream = new System.IO.FileStream(uri.OriginalString, FileMode.Open, FileAccess.Read, FileShare.Read); #pragma warning restore 6518 } uriStream = bitmapStream; } } // We need to use the stream created from the Uri. if (bitmapStream != null) { stream = bitmapStream; } else { // Note whether the original stream is writable. isOriginalWritable = stream.CanSeek && stream.CanWrite; } // Make sure we always use a seekable stream to avoid problems with http Uris etc. stream = GetSeekableStream(stream); if (stream is UnmanagedMemoryStream) { unmanagedMemoryStream = stream as UnmanagedMemoryStream; } IntPtr comStream = IntPtr.Zero; if (stream is System.IO.FileStream) { System.IO.FileStream filestream = stream as System.IO.FileStream; try { safeFilehandle = filestream.SafeFileHandle; } catch { // If Filestream doesn't support SafeHandle then revert to old code path. safeFilehandle = null; } } try { Guid vendorMicrosoft = new Guid(MILGuidData.GUID_VendorMicrosoft); UInt32 metadataFlags = (uint)WICMetadataCacheOptions.WICMetadataCacheOnDemand; if (cacheOption == BitmapCacheOption.OnLoad) { metadataFlags = (uint)WICMetadataCacheOptions.WICMetadataCacheOnLoad; } // We have a SafeHandle. if (safeFilehandle != null) { using (FactoryMaker myFactory = new FactoryMaker()) { HRESULT.Check(UnsafeNativeMethods.WICImagingFactory.CreateDecoderFromFileHandle( myFactory.ImagingFactoryPtr, safeFilehandle, ref vendorMicrosoft, metadataFlags, out decoder )); } } else { comStream = BitmapDecoder.GetIStreamFromStream(ref stream); using (FactoryMaker myFactory = new FactoryMaker()) { // This does an add-ref on the comStream HRESULT.Check(UnsafeNativeMethods.WICImagingFactory.CreateDecoderFromStream( myFactory.ImagingFactoryPtr, comStream, ref vendorMicrosoft, metadataFlags, out decoder )); } } Debug.Assert(decoder != IntPtr.Zero); decoderHandle = new SafeMILHandle(decoder); } catch { #pragma warning disable 6500 decoderHandle = null; throw; #pragma warning restore 6500 } finally { UnsafeNativeMethods.MILUnknown.ReleaseInterface(ref comStream); } string decoderMimeTypes; clsId = GetCLSIDFromDecoder(decoderHandle, out decoderMimeTypes); // If the mime type of the file does not match the associated decoder, // and if we are in a Partial trust scenario, throw! if ((mimeType != String.Empty) && (decoderMimeTypes.IndexOf(mimeType, StringComparison.OrdinalIgnoreCase) == -1)) { try { SecurityHelper.DemandUnmanagedCode(); } catch(SecurityException) { throw new ArgumentException(SR.Get(SRID.Image_ContentTypeDoesNotMatchDecoder)); } } return decoderHandle; } ////// Critical: performs an assert for WebPermission potentially outside the site of origin. /// [SecurityCritical] private static Stream ProcessHttpsFiles(Uri uri, Stream stream) { Stream bitmapStream = stream; // This is the condition where the Bitmap has already been downloaded // or the stream is not seekable // using async dowload in that case simply return the original stream // else you download the stream if (bitmapStream == null || !bitmapStream.CanSeek) { WebRequest request = null; // Block XDomain from apps deployed over HTTPS (For HTTP, LMZ and UNC apps, is ok to access images through HTTPS) SecurityHelper.BlockCrossDomainForHttpsApps(uri); // now, we can Assert permissions because we ensured that only apps deployed through non-HTTPS can access XDomain images (new WebPermission(NetworkAccess.Connect, BindUriHelper.UriToString(uri))).Assert(); // BlessedAssert try { request = WpfWebRequestHelper.CreateRequest(uri); } finally { WebPermission.RevertAssert(); } bitmapStream = WpfWebRequestHelper.GetResponseStream(request); } return bitmapStream; } ////// Critical: This code elevates and extracts a bitmap stream /// [SecurityCritical] private static Stream ProcessHttpFiles(Uri uri, Stream stream) { WebRequest request = null; Stream bitmapStream = stream; SecurityHelper.BlockCrossDomainForHttpsApps(uri); // Download only if this content is not already downloaded or stream is not seekable if (bitmapStream == null || !bitmapStream.CanSeek) { // In this case we first check to see if the consumer has media permissions for // safe media (Site of Origin + Cross domain), if it // does we assert and run the code that requires the assert bool fElevate = false; if (SecurityHelper.CallerHasMediaPermission(MediaPermissionAudio.NoAudio, MediaPermissionVideo.NoVideo, MediaPermissionImage.SafeImage)) { fElevate = true; } if (fElevate) { (new WebPermission(NetworkAccess.Connect, BindUriHelper.UriToString(uri))).Assert(); // BlessedAssert } try { request = WpfWebRequestHelper.CreateRequest(uri); } finally { if (fElevate) { WebPermission.RevertAssert(); } } // Download only if this content is not already downloaded or stream is not seekable bitmapStream = WpfWebRequestHelper.GetResponseStream(request); } return bitmapStream; } ////// Critical: This code elevates and extracts a bitmap stream /// [SecurityCritical] private static Stream ProcessUncFiles(Uri uri) { Stream bitmapStream = null; // perform checks for UNC content SecurityHelper.EnforceUncContentAccessRules(uri); // In this case we first check to see if the consumer has media permissions for // safe media (Site of Origin + Cross domain), if it // does then we assert else we run the code without the assert bool fElevate = false; if (SecurityHelper.CallerHasMediaPermission(MediaPermissionAudio.NoAudio, MediaPermissionVideo.NoVideo, MediaPermissionImage.SafeImage)) { fElevate = true; } if(fElevate) { // since the code above ensures that safe image permission is granted we // can now do an assert to allow cross domain web request (new FileIOPermission(FileIOPermissionAccess.Read, uri.LocalPath)).Assert(); // BlessedAssert } try { // FileStream does a demand for us, so no need to do a demand bitmapStream = new System.IO.FileStream(uri.LocalPath, FileMode.Open, FileAccess.Read, FileShare.Read); } finally { if(fElevate) { FileIOPermission.RevertAssert(); } } return bitmapStream; } ////// Critical: This code is used to check and grant access to pixel data and metadata. /// TreatAsSafe: This code does not elevate /// [SecurityCritical, SecurityTreatAsSafe] void CheckIfSiteOfOrigin() { string uri = null; if (CanConvertToString()) { // This call returns the URI either as an absolute URI which the user // passed in, in the first place or as the string "image" // we only allow this code to succeed in the case of Uri and if it is site of // origin or pack:. In all other conditions we fail uri = ToString(); } SecurityHelper.DemandMediaAccessPermission(uri); } /// Returns the decoder's CLSID ////// Critical - access unmanaged code to retrieve CLSID. /// [SecurityCritical] private static Guid GetCLSIDFromDecoder(SafeMILHandle decoderHandle, out string decoderMimeTypes) { Guid clsId; // Get the decoder info SafeMILHandle decoderInfo = new SafeMILHandle(); HRESULT.Check(UnsafeNativeMethods.WICBitmapDecoder.GetDecoderInfo( decoderHandle, out decoderInfo )); // Get CLSID for the decoder HRESULT.Check(UnsafeNativeMethods.WICBitmapCodecInfo.GetContainerFormat(decoderInfo, out clsId)); StringBuilder mimeTypes = null; UInt32 length = 0; // Find the length of the string needed HRESULT.Check(UnsafeNativeMethods.WICBitmapCodecInfo.GetMimeTypes( decoderInfo, 0, mimeTypes, out length )); // get the string back if (length > 0) { mimeTypes = new StringBuilder((int)length); HRESULT.Check(UnsafeNativeMethods.WICBitmapCodecInfo.GetMimeTypes( decoderInfo, length, mimeTypes, out length )); } if (mimeTypes != null) { decoderMimeTypes = mimeTypes.ToString(); } else { decoderMimeTypes = String.Empty; } return clsId; } /// Return a seekable stream if the current one is not seekable private static System.IO.Stream GetSeekableStream(System.IO.Stream bitmapStream) { // MIL codecs require the source stream to be seekable. But if // the source stream is an internet stream, it is not seekable. // The data is probably not in the stream yet. if (bitmapStream.CanSeek) { return bitmapStream; } // If the source is not seekable, we have to download the // stream into a memory stream before we can decode it. // ISSUE-2002/10/03--minliu: later on, if we can make MIL // support progressive JPEG etc., we should take out the // hack here and pass the network stream (CConectStream) // directly to the unmanaged code System.IO.MemoryStream memStream = new System.IO.MemoryStream(); byte[] buffer = new byte[1024]; int read; // Read all the bytes and write it to a memory stream // do { read = bitmapStream.Read(buffer, 0, 1024); if (read <= 0) { break; } memStream.Write(buffer, 0, read); } while (true); // Reset the memory stream pointer back to the begining memStream.Seek(0, System.IO.SeekOrigin.Begin); // Use the new stream return memStream; } /// Check the cache to see if decoder already exists ////// Critical - Eventually calls code that calls unmanaged code. /// TreatAsSafe - Only checks imaging cache to see object exists /// [SecurityCritical, SecurityTreatAsSafe] private static BitmapDecoder CheckCache( Uri uri, out Guid clsId ) { clsId = Guid.Empty; string mimeTypes; if (uri != null) { WeakReference weakRef = ImagingCache.CheckDecoderCache(uri) as WeakReference; if (weakRef != null) { BitmapDecoder bitmapDecoder = weakRef.Target as BitmapDecoder; if ((bitmapDecoder != null) && bitmapDecoder.CheckAccess()) { lock (bitmapDecoder.SyncObject) { clsId = GetCLSIDFromDecoder(bitmapDecoder.InternalDecoder, out mimeTypes); return bitmapDecoder; } } // Remove from the cache if bitmapDecoder is already been collected if (bitmapDecoder == null) { ImagingCache.RemoveFromDecoderCache(uri); } } } return null; } ////// Initialize the codec. /// ////// Critical - Will eventually create a COM object, need to secure it /// [SecurityCritical] private void Initialize(BitmapDecoder decoder) { _isBuiltInDecoder = true; if (decoder != null) { SetupFrames(decoder, null); // // We need to keep the strong reference to the cached decoder for a few reasons: // // The application may release the original cached decoder and then keep a // reference to this decoder only, in which case, the cache can be collected. // This will cause a few undesirable results: // 1. The application may choose to decode the same URI again in which case // we will not retrieve it from the cache even though we have a copy already // decoded. // 2. The original cached decoder holds onto the file stream indirectly which if // collected can cause bad behavior if the entire decoder is not loaded into // memory. // _cachedDecoder = decoder; } else if ((_createOptions & BitmapCreateOptions.DelayCreation) == 0 && _cacheOption == BitmapCacheOption.OnLoad) { SetupFrames(null, null); // Its ok to close the stream since the frames are not delay created and caching is immediate CloseStream(); } if ((_uri != null) && (decoder == null) && _shouldCacheDecoder) { // Add this decoder to the decoder cache ImagingCache.AddToDecoderCache( (_baseUri == null) ? _uri : new Uri(_baseUri, _uri), new WeakReference(this) ); } } ////// Closes the stream if its non-null /// ////// Critical - accesses critical resource _uriStream /// TreatAsSafe - doesn't return the uriStream, just closes it which is safe /// [SecurityCritical, SecurityTreatAsSafe] internal void CloseStream() { if (_uriStream != null) { _uriStream.Close(); _uriStream = null; // The finalizer closes _uriStream, we already closed it so there's no need to run // the finalizer anymore. GC.SuppressFinalize(this); } } ////// Sets up the frames collection. /// Also, ensures that the frame collection for this decoder /// will contain the same underlying frames as the collection /// passed in. This is called by the LateBoundBitmapDecoder /// ////// Critical - access unmanaged code /// TreatAsSafe - only access number of frames in the bitmap, a safe operation /// [SecurityCritical, SecurityTreatAsSafe] internal void SetupFrames(BitmapDecoder decoder, ReadOnlyCollectionframes) { uint numFrames = 1; HRESULT.Check(UnsafeNativeMethods.WICBitmapDecoder.GetFrameCount(_decoderHandle, out numFrames)); _frames = new List ((int)numFrames); // initialize the list of frames to null. // We'll fill it as it's used (pay for play). for (int i = 0; i < (int)numFrames; i++) { if (i > 0 && _cacheOption != BitmapCacheOption.OnLoad) { _createOptions |= BitmapCreateOptions.DelayCreation; } BitmapFrameDecode bfd = null; if ((frames != null) && (frames.Count == (i + 1))) { // If we already have a frames collection, get the BitmapFrame from it bfd = frames[i] as BitmapFrameDecode; bfd.UpdateDecoder(this); } else if (decoder == null) { // All frames should be frozen. bfd = new BitmapFrameDecode( i, _createOptions, _cacheOption, this ); bfd.Freeze(); } else { // if we are creating from an existing cache, use the frames // already stored in that cache // All frames should be frozen. bfd = new BitmapFrameDecode( i, _createOptions, _cacheOption, decoder.Frames[i] as BitmapFrameDecode ); bfd.Freeze(); } _frames.Add(bfd); } } /// /// Checks if the decoder is builtin. If not, throw exception /// private void EnsureBuiltInDecoder() { if (!_isBuiltInDecoder) { throw new NotImplementedException(); } } ////// Note, we must hold onto a reference to the managed stream /// as long as we are using the unmanaged stream in the decoder. /// This method may create a new managed stream. /// [SecurityCritical] private static IntPtr GetIStreamFromStream(ref System.IO.Stream bitmapStream) { IntPtr comStream = IntPtr.Zero; // ensure the stream is seekable bool seekable = bitmapStream.CanSeek; if (bitmapStream is UnmanagedMemoryStream) { UnmanagedMemoryStream memoryStream = bitmapStream as UnmanagedMemoryStream; IntPtr bufferPtr = IntPtr.Zero; int length = 0; unsafe { bufferPtr = (IntPtr) memoryStream.PositionPointer; length = (int) memoryStream.Length; } if (bufferPtr != IntPtr.Zero) { comStream = StreamAsIStream.IStreamFrom(bufferPtr, length); } } else { comStream = StreamAsIStream.IStreamFrom(bitmapStream); if (comStream == IntPtr.Zero) { throw new System.InvalidOperationException( SR.Get(SRID.Image_CantDealWithStream)); } // If the stream is not seekable, we must create a // seekable one for the decoder. if (!seekable || ((!bitmapStream.CanWrite) && (bitmapStream.Length <= 1048576))) { IntPtr memoryStream = StreamAsIStream.IStreamMemoryFrom(comStream); if (memoryStream != IntPtr.Zero) { // we don't need the original stream anymore UnsafeNativeMethods.MILUnknown.ReleaseInterface(ref comStream); bitmapStream = System.IO.Stream.Null; return memoryStream; } else if (!seekable) { throw new System.InvalidOperationException( SR.Get(SRID.Image_CantDealWithStream)); } } } if (comStream == IntPtr.Zero) { throw new System.InvalidOperationException( SR.Get(SRID.Image_CantDealWithStream)); } return comStream; } /// Returns whether decoder can be converted to a string internal bool CanConvertToString() { return (_uri != null); } #endregion #region Internal Abstract /// "Seals" the object internal abstract void SealObject(); #endregion #region Data Members /// check to see if implementation is internal private bool _isBuiltInDecoder; /// Internal Decoder private SafeMILHandle _decoderHandle; /// flag to see if decoder should be inserted in the cache private bool _shouldCacheDecoder = true; /// flag to see if decoder should be inserted in the cache private bool _isOriginalWritable = false; /// If the palette is already cached private bool _isPaletteCached; /// Palette private BitmapPalette _palette = null; /// If the ColorContext is already cached private bool _isColorContextCached = false; /// ColorContexts collection internal ReadOnlyCollection _readOnlycolorContexts; /// If the thumbnail is already cached private bool _isThumbnailCached; /// /// Metadata /// ////// Critical - Access only granted if SiteOfOrigin demanded. /// [SecurityCritical] private BitmapMetadata _metadata; /// If the metadata is already cached private bool _isMetadataCached; /// Thumbnail private BitmapSource _thumbnail = null; /// CodecInfo private BitmapCodecInfo _codecInfo; /// If the preview is already cached private bool _isPreviewCached; /// Preview private BitmapSource _preview = null; /// Frames collection internal List_frames; /// Frames collection internal ReadOnlyCollection _readOnlyFrames; /// Stream internal Stream _stream; /// Uri internal Uri _uri; /// Base Uri, only stored internally. Not used for serialization internal Uri _baseUri; /// Uri Stream -- this is the stream that was created from the passed in Uri /// /// Critical: Obtained under elevation /// [SecurityCritical] internal Stream _uriStream; /// CreateOptions internal BitmapCreateOptions _createOptions; /// CacheOption internal BitmapCacheOption _cacheOption; /// Event helper for download completed event internal UniqueEventHelper _downloadEvent = new UniqueEventHelper(); /// Event helper for download progress event internal UniqueEventHelper_progressEvent = new UniqueEventHelper (); /// Event helper for download failed event internal UniqueEventHelper _failedEvent = new UniqueEventHelper (); /// SyncObject private object _syncObject = new Object(); // this is data that we cache as a performance optimization. It is ok to do so since we do not want to // handle this key change in the lifetime of this app. private static SecurityCriticalDataForSet isImageDisabled; /// /// Critical: This variable guards against reinitialization of the cached registry value /// [SecurityCritical] private static bool isImageDisabledInitialized; // For UnmanagedMemoryStream we want to make sure that buffer // its pointing to is not getting release until decoder is alive private UnmanagedMemoryStream _unmanagedMemoryStream; ////// Critical: This variable guards against closing of the filehandle /// which gets passed to unmanaged decoder. /// [SecurityCritical] private SafeFileHandle _safeFilehandle; private BitmapDecoder _cachedDecoder; #endregion } #endregion // BitmapDecoder } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ // Microsoft Avalon // Copyright (c) Microsoft Corporation, 2003 // // File: BitmapDecoder.cs // //----------------------------------------------------------------------------- #pragma warning disable 1634, 1691 // Allow suppression of certain presharp messages using System; using System.IO; using System.IO.Packaging; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Collections.ObjectModel; using System.Reflection; using System.Security; using System.Security.Permissions; using System.Text.RegularExpressions; using MS.Internal; using MS.Internal.AppModel; using MS.Win32.PresentationCore; using Microsoft.Win32.SafeHandles; using System.Diagnostics; using System.Windows.Media; using System.Globalization; using System.Runtime.InteropServices; using System.Threading; using System.Windows.Threading; using System.Windows.Media.Imaging; using MS.Internal.PresentationCore; // SecurityHelper using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; using System.Net; using System.Net.Cache; using System.Text; namespace System.Windows.Media.Imaging { #region BitmapDecoder ////// BitmapDecoder is a container for bitmap frames. Each bitmap frame is an BitmapFrame. /// Any BitmapFrame it returns are frozen /// be immutable. /// public abstract class BitmapDecoder : DispatcherObject { #region Constructors ////// Critical: This code has critical static variables isImageDisabledInitialized /// TreatAsASafe: The variables are not exposed /// [SecurityCritical,SecurityTreatAsSafe] static BitmapDecoder() { isImageDisabledInitialized = false; } ////// Default constructor /// protected BitmapDecoder() { } ////// Internal constructor /// internal BitmapDecoder(bool isBuiltIn) { _isBuiltInDecoder = isBuiltIn; } ////// Constructor /// ////// Critical - access critical resource /// [SecurityCritical] internal BitmapDecoder( Uri bitmapUri, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, Guid expectedClsId ) { Guid clsId = Guid.Empty; bool isOriginalWritable = false; if (bitmapUri == null) { throw new ArgumentNullException("bitmapUri"); } if ((createOptions & BitmapCreateOptions.IgnoreImageCache) != 0) { ImagingCache.RemoveFromDecoderCache(bitmapUri); } BitmapDecoder decoder = CheckCache(bitmapUri, out clsId); if (decoder != null) { _decoderHandle = decoder.InternalDecoder; } else { _decoderHandle = SetupDecoderFromUriOrStream( bitmapUri, null, cacheOption, out clsId, out isOriginalWritable, out _uriStream, out _unmanagedMemoryStream, out _safeFilehandle ); if (_uriStream == null) { GC.SuppressFinalize(this); } } if (clsId != expectedClsId) { throw new FileFormatException(bitmapUri, SR.Get(SRID.Image_CantDealWithUri)); } _uri = bitmapUri; _createOptions = createOptions; _cacheOption = cacheOption; _syncObject = _decoderHandle; _isOriginalWritable = isOriginalWritable; Initialize(decoder); } ////// Constructor /// ////// Critical - access critical resource /// [SecurityCritical] internal BitmapDecoder( Stream bitmapStream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, Guid expectedClsId ) { Guid clsId = Guid.Empty; bool isOriginalWritable = false; if (bitmapStream == null) { throw new ArgumentNullException("bitmapStream"); } _decoderHandle = SetupDecoderFromUriOrStream( null, bitmapStream, cacheOption, out clsId, out isOriginalWritable, out _uriStream, out _unmanagedMemoryStream, out _safeFilehandle ); if (_uriStream == null) { GC.SuppressFinalize(this); } if (clsId != Guid.Empty && clsId != expectedClsId) { throw new FileFormatException(null, SR.Get(SRID.Image_CantDealWithStream)); } _stream = bitmapStream; _createOptions = createOptions; _cacheOption = cacheOption; _syncObject = _decoderHandle; _isOriginalWritable = isOriginalWritable; Initialize(null); } ////// Constructor /// ////// Critical: strictly speaking because the method uses a SafeFileHandle, which is a SecurityCritical /// type (in v4). But it also takes other resources as input that untrusted code shouldn't have /// access to (SafeMILHandle, UnmanagedMemoryStream). /// [SecurityCritical] internal BitmapDecoder( SafeMILHandle decoderHandle, BitmapDecoder decoder, Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, bool insertInDecoderCache, bool isOriginalWritable, Stream uriStream, UnmanagedMemoryStream unmanagedMemoryStream, SafeFileHandle safeFilehandle ) { _decoderHandle = decoderHandle; _baseUri = baseUri; _uri = uri; _stream = stream; _createOptions = createOptions; _cacheOption = cacheOption; _syncObject = decoderHandle; _shouldCacheDecoder = insertInDecoderCache; _isOriginalWritable = isOriginalWritable; _uriStream = uriStream; _unmanagedMemoryStream = unmanagedMemoryStream; _safeFilehandle = safeFilehandle; if (_uriStream == null) { GC.SuppressFinalize(this); } Initialize(decoder); } ////// Close the stream pointing to the file after this BitmapDecoder is no longer used. /// It might have been closed before in Initialize if the cache option was OnLoad or /// on decode if the cache option was OnDemand/Default. In those cases, the finalizer /// would have been suppressed. /// ////// Critical - accesses critical resource _uriStream /// TreatAsSafe - doesn't return the uriStream, just closes it which is safe /// [SecurityCritical, SecurityTreatAsSafe] ~BitmapDecoder() { // // Normally _uriStream gets GCed because this BitmapDecoder is the only thing that // references it, and the stream's finalizer closes it. However, in some cases, other // objects still have references to the _uriStream, so it won't get GCed because this // decoder does. One such case is getting the stream from a pack uri that points to // a resource. In those cases, PackagePart created the streams and is also holding on // to them. It won't let go unless the stream is closed, but the stream won't close // unless it's been let go, so the stream is leaked. Closing the stream removes the // leak. // // _uriStream is always created within SetupDecoderFromUriOrStream. Either it's set // using an out parameter, or it's set in the internal constructor that takes a // uriStream parameter. That internal constructor is called by the sealed subclasses // of BitmapDecoder (such as JpegBitmapDecoder), those subclasses are constructed in // CreateFromUriOrStream, which also gets uriStream from SetupDecoderFromUriOrStream. // if (_uriStream != null) { _uriStream.Close(); } } ////// Create BitmapDecoder from the uri or stream. If both are specified, the uri /// is chosen. /// ////// Critical - Unmanaged code will eventually create a COM object, need to secure it /// Calls one of the XxxBitmapDecoder static internal ctors, which are all SecurityCritical. /// TreatAsSafe - All inputs verified /// The inputs of unsafe types that are passed to XxxBitmapDecoder are produced within this method, /// not taken from outside. /// [SecurityCritical, SecurityTreatAsSafe] internal static BitmapDecoder CreateFromUriOrStream( Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy, bool insertInDecoderCache ) { Guid clsId = Guid.Empty; bool isOriginalWritable = false; SafeMILHandle decoderHandle = null; BitmapDecoder cachedDecoder = null; Uri finalUri = null; Stream uriStream = null; UnmanagedMemoryStream unmanagedMemoryStream = null; SafeFileHandle safeFilehandle = null; // check to ensure that images are allowed in partial trust DemandIfImageBlocked(); if (uri != null) { finalUri = (baseUri != null) ? System.Windows.Navigation.BaseUriHelper.GetResolvedUri(baseUri, uri) : uri; if (insertInDecoderCache) { if ((createOptions & BitmapCreateOptions.IgnoreImageCache) != 0) { ImagingCache.RemoveFromDecoderCache(finalUri); } cachedDecoder = CheckCache( finalUri, out clsId ); } } // try to retrieve the cached decoder if (cachedDecoder != null) { decoderHandle = cachedDecoder.InternalDecoder; } else if ((finalUri != null) && (finalUri.IsAbsoluteUri) && (stream == null) && ((finalUri.Scheme == Uri.UriSchemeHttp) || (finalUri.Scheme == Uri.UriSchemeHttps))) { return new LateBoundBitmapDecoder(baseUri, uri, stream, createOptions, cacheOption, uriCachePolicy); } else if ((stream != null) && (!stream.CanSeek)) { return new LateBoundBitmapDecoder(baseUri, uri, stream, createOptions, cacheOption, uriCachePolicy); } else { // Create an unmanaged decoder decoderHandle = BitmapDecoder.SetupDecoderFromUriOrStream( finalUri, stream, cacheOption, out clsId, out isOriginalWritable, out uriStream, out unmanagedMemoryStream, out safeFilehandle ); } BitmapDecoder decoder = null; // Find out the decoder type and wrap it appropriately and return that if (MILGuidData.GUID_ContainerFormatBmp == clsId) { decoder = new BmpBitmapDecoder( decoderHandle, cachedDecoder, baseUri, uri, stream, createOptions, cacheOption, insertInDecoderCache, isOriginalWritable, uriStream, unmanagedMemoryStream, safeFilehandle ); } else if (MILGuidData.GUID_ContainerFormatGif == clsId) { decoder = new GifBitmapDecoder( decoderHandle, cachedDecoder, baseUri, uri, stream, createOptions, cacheOption, insertInDecoderCache, isOriginalWritable, uriStream, unmanagedMemoryStream, safeFilehandle ); } else if (MILGuidData.GUID_ContainerFormatIco == clsId) { decoder = new IconBitmapDecoder( decoderHandle, cachedDecoder, baseUri, uri, stream, createOptions, cacheOption, insertInDecoderCache, isOriginalWritable, uriStream, unmanagedMemoryStream, safeFilehandle ); } else if (MILGuidData.GUID_ContainerFormatJpeg == clsId) { decoder = new JpegBitmapDecoder( decoderHandle, cachedDecoder, baseUri, uri, stream, createOptions, cacheOption, insertInDecoderCache, isOriginalWritable, uriStream, unmanagedMemoryStream, safeFilehandle ); } else if (MILGuidData.GUID_ContainerFormatPng == clsId) { decoder = new PngBitmapDecoder( decoderHandle, cachedDecoder, baseUri, uri, stream, createOptions, cacheOption, insertInDecoderCache, isOriginalWritable, uriStream, unmanagedMemoryStream, safeFilehandle ); } else if (MILGuidData.GUID_ContainerFormatTiff == clsId) { decoder = new TiffBitmapDecoder( decoderHandle, cachedDecoder, baseUri, uri, stream, createOptions, cacheOption, insertInDecoderCache, isOriginalWritable, uriStream, unmanagedMemoryStream, safeFilehandle ); } else if (MILGuidData.GUID_ContainerFormatWmp == clsId) { decoder = new WmpBitmapDecoder( decoderHandle, cachedDecoder, baseUri, uri, stream, createOptions, cacheOption, insertInDecoderCache, isOriginalWritable, uriStream, unmanagedMemoryStream, safeFilehandle ); } else { decoder = new UnknownBitmapDecoder( decoderHandle, cachedDecoder, baseUri, uri, stream, createOptions, cacheOption, insertInDecoderCache, isOriginalWritable, uriStream, unmanagedMemoryStream, safeFilehandle ); } return decoder; } ////// Create a BitmapDecoder from a Uri with the specified BitmapCreateOptions and /// BitmapCacheOption /// /// Uri to decode /// Bitmap Create Options /// Bitmap Caching Option public static BitmapDecoder Create( Uri bitmapUri, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption ) { return Create(bitmapUri, createOptions, cacheOption, null); } ////// Create a BitmapDecoder from a Uri with the specified BitmapCreateOptions and /// BitmapCacheOption /// /// Uri to decode /// Bitmap Create Options /// Bitmap Caching Option /// Optional web request cache policy public static BitmapDecoder Create( Uri bitmapUri, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy ) { if (bitmapUri == null) { throw new ArgumentNullException("bitmapUri"); } return CreateFromUriOrStream( null, bitmapUri, null, createOptions, cacheOption, uriCachePolicy, true ); } ////// Create a BitmapDecoder from a Stream with the specified BitmapCreateOptions and /// BitmapCacheOption /// /// Stream to decode /// Bitmap Create Options /// Bitmap Caching Option public static BitmapDecoder Create( Stream bitmapStream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption ) { if (bitmapStream == null) { throw new ArgumentNullException("bitmapStream"); } return CreateFromUriOrStream( null, null, bitmapStream, createOptions, cacheOption, null, true ); } #endregion #region Properties ////// If there is an palette, return it. /// Otherwise, return null. /// ////// Critical - Access unmanaged code, codecs /// PublicOK - Getting palette data is OK /// public virtual BitmapPalette Palette { [SecurityCritical] get { VerifyAccess(); EnsureBuiltInDecoder(); if (!_isPaletteCached) { SafeMILHandle /* IWICBitmapPalette */ paletteHandle = BitmapPalette.CreateInternalPalette(); lock (_syncObject) { int hr = UnsafeNativeMethods.WICBitmapDecoder.CopyPalette( _decoderHandle, paletteHandle ); if (hr != (int)WinCodecErrors.WINCODEC_ERR_PALETTEUNAVAILABLE) { HRESULT.Check(hr); _palette = new BitmapPalette(paletteHandle); } } _isPaletteCached = true; } return _palette; } } ////// If there is an embedded color profile, return it. /// Otherwise, return null. /// public virtual ReadOnlyCollectionColorContexts { get { VerifyAccess(); return InternalColorContexts; } } /// /// If there is a global thumbnail, return it. /// Otherwise, return null. The returned source is frozen. /// ////// Critical - Access unmanaged code, codecs /// PublicOK - Getting thumbnail data is OK /// public virtual BitmapSource Thumbnail { [SecurityCritical ] get { VerifyAccess(); EnsureBuiltInDecoder(); if (!_isThumbnailCached) { IntPtr /* IWICBitmapSource */ thumbnail = IntPtr.Zero; // Check if there is embedded thumbnail or not lock (_syncObject) { int hr = UnsafeNativeMethods.WICBitmapDecoder.GetThumbnail( _decoderHandle, out thumbnail ); if (hr != (int)WinCodecErrors.WINCODEC_ERR_CODECNOTHUMBNAIL) { HRESULT.Check(hr); } } if (thumbnail != IntPtr.Zero) { BitmapSourceSafeMILHandle thumbHandle = new BitmapSourceSafeMILHandle(thumbnail); SafeMILHandle unmanagedPalette = BitmapPalette.CreateInternalPalette(); BitmapPalette palette = null; int hr = UnsafeNativeMethods.WICBitmapSource.CopyPalette( thumbHandle, unmanagedPalette ); if (hr == HRESULT.S_OK) { palette = new BitmapPalette(unmanagedPalette); } _thumbnail = new UnmanagedBitmapWrapper( BitmapSource.CreateCachedBitmap( null, thumbHandle, BitmapCreateOptions.PreservePixelFormat, _cacheOption, palette )); _thumbnail.Freeze(); } _isThumbnailCached = true; } return _thumbnail; } } ////// If there is a global metadata, return it. /// Otherwise, return null. The returned source is frozen. /// ////// Critical - Access unmanaged code, codecs /// Public - /// Getting metadata for images from site of origin or bundled with the application is OK /// Getting metatdat for images from cross domain servers triggers a demand for site of origin. /// public virtual BitmapMetadata Metadata { [SecurityCritical] get { VerifyAccess(); EnsureBuiltInDecoder(); // Demand Site Of origin on the URI if it passes then this information is ok to expose CheckIfSiteOfOrigin(); if (!_isMetadataCached) { IntPtr /* IWICMetadataQueryReader */ metadata = IntPtr.Zero; lock (_syncObject) { int hr = UnsafeNativeMethods.WICBitmapDecoder.GetMetadataQueryReader( _decoderHandle, out metadata ); if (hr != (int)WinCodecErrors.WINCODEC_ERR_UNSUPPORTEDOPERATION) { HRESULT.Check(hr); } } if (metadata != IntPtr.Zero) { SafeMILHandle metadataHandle = new SafeMILHandle(metadata); _metadata = new BitmapMetadata(metadataHandle, true, IsMetadataFixedSize, _syncObject); _metadata.Freeze(); } _isMetadataCached = true; } return _metadata; } } ////// The info that identifies this codec. /// ////// Critical - Access unmanaged code, codecs /// PublicOK - Getting codecinfo data is OK /// public virtual BitmapCodecInfo CodecInfo { [SecurityCritical] get { VerifyAccess(); EnsureBuiltInDecoder(); // There should always be a codec info. if (_codecInfo == null) { SafeMILHandle /* IWICBitmapDecoderInfo */ codecInfoHandle = new SafeMILHandle(); HRESULT.Check(UnsafeNativeMethods.WICBitmapDecoder.GetDecoderInfo( _decoderHandle, out codecInfoHandle )); _codecInfo = new BitmapCodecInfoInternal(codecInfoHandle); } return _codecInfo; } } ////// Access to the individual frames. All BitmapFrames are frozen. /// public virtual ReadOnlyCollectionFrames { get { VerifyAccess(); EnsureBuiltInDecoder(); if (_frames == null) { SetupFrames(null, null); } if (_readOnlyFrames == null) { _readOnlyFrames = new ReadOnlyCollection (_frames); } return _readOnlyFrames; } } /// /// If there is a global preview image, return it. /// Otherwise, return null. The returned source is frozen. /// ////// Critical - Access unmanaged code, codecs /// PublicOK - Getting preview data is OK /// public virtual BitmapSource Preview { [SecurityCritical] get { VerifyAccess(); EnsureBuiltInDecoder(); if (!_isPreviewCached) { IntPtr /* IWICBitmapSource */ preview = IntPtr.Zero; lock (_syncObject) { // Check if there is embedded preview or not int hr = UnsafeNativeMethods.WICBitmapDecoder.GetPreview( _decoderHandle, out preview ); if (hr != (int)WinCodecErrors.WINCODEC_ERR_UNSUPPORTEDOPERATION) { HRESULT.Check(hr); } } if (preview != IntPtr.Zero) { BitmapSourceSafeMILHandle previewHandle = new BitmapSourceSafeMILHandle(preview); SafeMILHandle unmanagedPalette = BitmapPalette.CreateInternalPalette(); BitmapPalette palette = null; int hr = UnsafeNativeMethods.WICBitmapSource.CopyPalette( previewHandle, unmanagedPalette ); if (hr == HRESULT.S_OK) { palette = new BitmapPalette(unmanagedPalette); } _preview = new UnmanagedBitmapWrapper( BitmapSource.CreateCachedBitmap( null, previewHandle, BitmapCreateOptions.PreservePixelFormat, _cacheOption, palette )); _preview.Freeze(); } _isPreviewCached = true; } return _preview; } } ////// Returns true if the decoder is downloading content /// public virtual bool IsDownloading { get { VerifyAccess(); EnsureBuiltInDecoder(); return false; } } ////// Raised when decoder has completed downloading content /// May not be raised for all content. /// public virtual event EventHandler DownloadCompleted { add { VerifyAccess(); EnsureBuiltInDecoder(); _downloadEvent.AddEvent(value); } remove { VerifyAccess(); EnsureBuiltInDecoder(); _downloadEvent.RemoveEvent(value); } } ////// Raised when download has progressed /// May not be raised for all content. /// public virtual event EventHandlerDownloadProgress { add { VerifyAccess(); EnsureBuiltInDecoder(); _progressEvent.AddEvent(value); } remove { VerifyAccess(); EnsureBuiltInDecoder(); _progressEvent.RemoveEvent(value); } } /// /// Raised when download has failed /// May not be raised for all content. /// public virtual event EventHandlerDownloadFailed { add { VerifyAccess(); EnsureBuiltInDecoder(); _failedEvent.AddEvent(value); } remove { VerifyAccess(); EnsureBuiltInDecoder(); _failedEvent.RemoveEvent(value); } } #endregion #region Public Properties /// /// Create an in-place bitmap metadata writer. /// public virtual InPlaceBitmapMetadataWriter CreateInPlaceBitmapMetadataWriter() { VerifyAccess(); EnsureBuiltInDecoder(); CheckOriginalWritable(); // Demand Site Of origin on the URI if it passes then this information is ok to expose CheckIfSiteOfOrigin(); return InPlaceBitmapMetadataWriter.CreateFromDecoder(_decoderHandle, _syncObject); } #endregion #region ToString ////// ToString /// public override string ToString() { VerifyAccess(); if (!_isBuiltInDecoder) { return base.ToString(); } if (_uri != null) { if (_baseUri != null) { Uri uri = new Uri(_baseUri, _uri); return BindUriHelper.UriToString(uri); } else { return BindUriHelper.UriToString(_uri); } } else { return SafeSecurityHelper.IMAGE; } } #endregion #region Internal Properties ////// Internal Decoder /// internal SafeMILHandle InternalDecoder { get { return _decoderHandle; } } ////// Returns whether metadata is fixed size or not. /// internal virtual bool IsMetadataFixedSize { get { return false; } } ////// Synchronization Object. Any unmanaged PInvoke/call that requires synchronization /// must lock on the [....] object. This object must be internal or private so as not /// to be publicly lockable. /// internal object SyncObject { get { Debug.Assert(_syncObject != null); return _syncObject; } } ////// Used as a delegate in InternalColorContexts to get the unmanaged IWICColorContexts /// ////// Critical - Calls critical, unmanaged method /// [SecurityCritical] private int GetColorContexts(ref uint numContexts, IntPtr[] colorContextPtrs) { Invariant.Assert(colorContextPtrs == null || numContexts <= colorContextPtrs.Length); return UnsafeNativeMethods.WICBitmapDecoder.GetColorContexts(_decoderHandle, numContexts, colorContextPtrs, out numContexts); } ////// If there is an embedded color profile, return it. /// Otherwise, return null. /// ////// Critical - Access unmanaged code, codecs /// TreatAsSafe - We are exposing specific, non-sensitive data that came from /// a Uri or a bitmap. If the profile data came from a Uri, the /// WebRequest we did to get it demandend the appropriate /// permission and we did not expose the request or response. If it came /// from a bitmap, it may be cross-domain, but the restricted /// nature of the data combined with the fact that it has been /// inspected by WCS which is hardened against malicious data /// makes this OK. /// internal ReadOnlyCollectionInternalColorContexts { [SecurityCritical, SecurityTreatAsSafe] get { EnsureBuiltInDecoder(); if (!_isColorContextCached) { lock (_syncObject) { IList colorContextList = ColorContext.GetColorContextsHelper(GetColorContexts); if (colorContextList != null) { _readOnlycolorContexts = new ReadOnlyCollection (colorContextList); } _isColorContextCached = true; } } return _readOnlycolorContexts; } } /// /// Checks whether the underlying source is writable (useful for in-place metadata editing) /// internal void CheckOriginalWritable() { if (!_isOriginalWritable) { throw new System.InvalidOperationException(SR.Get(SRID.Image_OriginalStreamReadOnly)); } } #endregion #region Internal/Private Methods ////// Critical: This sets and accesses critical members isImageDisabledInitialized,isImageDisabled /// TreatAsSafe: This code does not expose the variables and is not influenced by external paramaters that do not /// require unmanaged code access permission to set /// [SecurityCritical, SecurityTreatAsSafe] private static void DemandIfImageBlocked() { if(!isImageDisabledInitialized) { // a performance optimization to ensure we hit the registry only once in the lifetime of the application isImageDisabled = new SecurityCriticalDataForSet(SafeSecurityHelper.IsFeatureDisabled(SafeSecurityHelper.KeyToRead.MediaImageDisable)); isImageDisabledInitialized = true; } if (isImageDisabled.Value) { // in case the registry key is '1' then demand MediaPermissionImage.AllImage - not granted in Partial Trust SecurityHelper.DemandMediaPermission(MediaPermissionAudio.NoAudio, MediaPermissionVideo.NoVideo, MediaPermissionImage.AllImage); } else { // Images are enabled. Then, demand permissions for safe imaging - granted in Partial Trust by default SecurityHelper.DemandMediaPermission(MediaPermissionAudio.NoAudio, MediaPermissionVideo.NoVideo, MediaPermissionImage.SafeImage); } } /// /// Critical - Unmanaged code will eventually create a COM object, need to secure it /// [SecurityCritical] internal static SafeMILHandle SetupDecoderFromUriOrStream( Uri uri, Stream stream, BitmapCacheOption cacheOption, out Guid clsId, out bool isOriginalWritable, out Stream uriStream, out UnmanagedMemoryStream unmanagedMemoryStream, out SafeFileHandle safeFilehandle ) { SafeMILHandle decoderHandle; IntPtr decoder = IntPtr.Zero; System.IO.Stream bitmapStream = null; string mimeType = String.Empty; // check to ensure that images are allowed in partial trust NOP in full trust DemandIfImageBlocked(); unmanagedMemoryStream = null; safeFilehandle = null; isOriginalWritable = false; uriStream = null; if ((uri != null) && (stream != null)) { // In this case we expect the Uri to be http(s) Debug.Assert((uri.Scheme == Uri.UriSchemeHttp) || (uri.Scheme == Uri.UriSchemeHttps)); Debug.Assert(stream.CanSeek); } // Uri if (uri != null) { if (uri.IsAbsoluteUri) { // In this case we first check to see if the consumer has media permissions for // safe media (Site of Origin + Cross domain) SecurityHelper.DemandMediaPermission(MediaPermissionAudio.NoAudio, MediaPermissionVideo.NoVideo, MediaPermissionImage.SiteOfOriginImage) ; // This code path executes only for pack web requests if (String.Compare(uri.Scheme, PackUriHelper.UriSchemePack, StringComparison.OrdinalIgnoreCase) == 0) { WebResponse response = WpfWebRequestHelper.CreateRequestAndGetResponse(uri); mimeType = response.ContentType; bitmapStream = response.GetResponseStream(); uriStream = bitmapStream; } } if ((bitmapStream == null) || (bitmapStream == System.IO.Stream.Null)) { // We didn't get a stream from the pack web request, so we have // to try to create one ourselves. if (uri.IsAbsoluteUri) { // The Uri class can't tell if it is a file unless it // has an absolute path. int targetZone = SecurityHelper.MapUrlToZoneWrapper(uri); if (targetZone == MS.Win32.NativeMethods.URLZONE_LOCAL_MACHINE) { if (uri.IsFile) { // FileStream does a demand for us, so no need to do a demand bitmapStream = new System.IO.FileStream(uri.LocalPath, FileMode.Open, FileAccess.Read, FileShare.Read); } } else // Any other zone { // UNC path for a file which is not http if (uri.IsFile && uri.IsUnc) // for UNC { bitmapStream = ProcessUncFiles(uri); } else if (uri.Scheme == Uri.UriSchemeHttp) // for http { bitmapStream = ProcessHttpFiles(uri,stream); } else if (uri.Scheme == Uri.UriSchemeHttps) // for https { bitmapStream = ProcessHttpsFiles(uri,stream); } else { // The Uri is a custom Uri. Try to grab the stream from its WebResponse. bitmapStream = WpfWebRequestHelper.CreateRequestAndGetResponseStream(uri); } } } else { #pragma warning disable 6518 // We don't have an absolute URI, so we don't necessarily know // if it is a file, but we'll have to assume it is and try to // create a stream from the original string. bitmapStream = new System.IO.FileStream(uri.OriginalString, FileMode.Open, FileAccess.Read, FileShare.Read); #pragma warning restore 6518 } uriStream = bitmapStream; } } // We need to use the stream created from the Uri. if (bitmapStream != null) { stream = bitmapStream; } else { // Note whether the original stream is writable. isOriginalWritable = stream.CanSeek && stream.CanWrite; } // Make sure we always use a seekable stream to avoid problems with http Uris etc. stream = GetSeekableStream(stream); if (stream is UnmanagedMemoryStream) { unmanagedMemoryStream = stream as UnmanagedMemoryStream; } IntPtr comStream = IntPtr.Zero; if (stream is System.IO.FileStream) { System.IO.FileStream filestream = stream as System.IO.FileStream; try { safeFilehandle = filestream.SafeFileHandle; } catch { // If Filestream doesn't support SafeHandle then revert to old code path. safeFilehandle = null; } } try { Guid vendorMicrosoft = new Guid(MILGuidData.GUID_VendorMicrosoft); UInt32 metadataFlags = (uint)WICMetadataCacheOptions.WICMetadataCacheOnDemand; if (cacheOption == BitmapCacheOption.OnLoad) { metadataFlags = (uint)WICMetadataCacheOptions.WICMetadataCacheOnLoad; } // We have a SafeHandle. if (safeFilehandle != null) { using (FactoryMaker myFactory = new FactoryMaker()) { HRESULT.Check(UnsafeNativeMethods.WICImagingFactory.CreateDecoderFromFileHandle( myFactory.ImagingFactoryPtr, safeFilehandle, ref vendorMicrosoft, metadataFlags, out decoder )); } } else { comStream = BitmapDecoder.GetIStreamFromStream(ref stream); using (FactoryMaker myFactory = new FactoryMaker()) { // This does an add-ref on the comStream HRESULT.Check(UnsafeNativeMethods.WICImagingFactory.CreateDecoderFromStream( myFactory.ImagingFactoryPtr, comStream, ref vendorMicrosoft, metadataFlags, out decoder )); } } Debug.Assert(decoder != IntPtr.Zero); decoderHandle = new SafeMILHandle(decoder); } catch { #pragma warning disable 6500 decoderHandle = null; throw; #pragma warning restore 6500 } finally { UnsafeNativeMethods.MILUnknown.ReleaseInterface(ref comStream); } string decoderMimeTypes; clsId = GetCLSIDFromDecoder(decoderHandle, out decoderMimeTypes); // If the mime type of the file does not match the associated decoder, // and if we are in a Partial trust scenario, throw! if ((mimeType != String.Empty) && (decoderMimeTypes.IndexOf(mimeType, StringComparison.OrdinalIgnoreCase) == -1)) { try { SecurityHelper.DemandUnmanagedCode(); } catch(SecurityException) { throw new ArgumentException(SR.Get(SRID.Image_ContentTypeDoesNotMatchDecoder)); } } return decoderHandle; } ////// Critical: performs an assert for WebPermission potentially outside the site of origin. /// [SecurityCritical] private static Stream ProcessHttpsFiles(Uri uri, Stream stream) { Stream bitmapStream = stream; // This is the condition where the Bitmap has already been downloaded // or the stream is not seekable // using async dowload in that case simply return the original stream // else you download the stream if (bitmapStream == null || !bitmapStream.CanSeek) { WebRequest request = null; // Block XDomain from apps deployed over HTTPS (For HTTP, LMZ and UNC apps, is ok to access images through HTTPS) SecurityHelper.BlockCrossDomainForHttpsApps(uri); // now, we can Assert permissions because we ensured that only apps deployed through non-HTTPS can access XDomain images (new WebPermission(NetworkAccess.Connect, BindUriHelper.UriToString(uri))).Assert(); // BlessedAssert try { request = WpfWebRequestHelper.CreateRequest(uri); } finally { WebPermission.RevertAssert(); } bitmapStream = WpfWebRequestHelper.GetResponseStream(request); } return bitmapStream; } ////// Critical: This code elevates and extracts a bitmap stream /// [SecurityCritical] private static Stream ProcessHttpFiles(Uri uri, Stream stream) { WebRequest request = null; Stream bitmapStream = stream; SecurityHelper.BlockCrossDomainForHttpsApps(uri); // Download only if this content is not already downloaded or stream is not seekable if (bitmapStream == null || !bitmapStream.CanSeek) { // In this case we first check to see if the consumer has media permissions for // safe media (Site of Origin + Cross domain), if it // does we assert and run the code that requires the assert bool fElevate = false; if (SecurityHelper.CallerHasMediaPermission(MediaPermissionAudio.NoAudio, MediaPermissionVideo.NoVideo, MediaPermissionImage.SafeImage)) { fElevate = true; } if (fElevate) { (new WebPermission(NetworkAccess.Connect, BindUriHelper.UriToString(uri))).Assert(); // BlessedAssert } try { request = WpfWebRequestHelper.CreateRequest(uri); } finally { if (fElevate) { WebPermission.RevertAssert(); } } // Download only if this content is not already downloaded or stream is not seekable bitmapStream = WpfWebRequestHelper.GetResponseStream(request); } return bitmapStream; } ////// Critical: This code elevates and extracts a bitmap stream /// [SecurityCritical] private static Stream ProcessUncFiles(Uri uri) { Stream bitmapStream = null; // perform checks for UNC content SecurityHelper.EnforceUncContentAccessRules(uri); // In this case we first check to see if the consumer has media permissions for // safe media (Site of Origin + Cross domain), if it // does then we assert else we run the code without the assert bool fElevate = false; if (SecurityHelper.CallerHasMediaPermission(MediaPermissionAudio.NoAudio, MediaPermissionVideo.NoVideo, MediaPermissionImage.SafeImage)) { fElevate = true; } if(fElevate) { // since the code above ensures that safe image permission is granted we // can now do an assert to allow cross domain web request (new FileIOPermission(FileIOPermissionAccess.Read, uri.LocalPath)).Assert(); // BlessedAssert } try { // FileStream does a demand for us, so no need to do a demand bitmapStream = new System.IO.FileStream(uri.LocalPath, FileMode.Open, FileAccess.Read, FileShare.Read); } finally { if(fElevate) { FileIOPermission.RevertAssert(); } } return bitmapStream; } ////// Critical: This code is used to check and grant access to pixel data and metadata. /// TreatAsSafe: This code does not elevate /// [SecurityCritical, SecurityTreatAsSafe] void CheckIfSiteOfOrigin() { string uri = null; if (CanConvertToString()) { // This call returns the URI either as an absolute URI which the user // passed in, in the first place or as the string "image" // we only allow this code to succeed in the case of Uri and if it is site of // origin or pack:. In all other conditions we fail uri = ToString(); } SecurityHelper.DemandMediaAccessPermission(uri); } /// Returns the decoder's CLSID ////// Critical - access unmanaged code to retrieve CLSID. /// [SecurityCritical] private static Guid GetCLSIDFromDecoder(SafeMILHandle decoderHandle, out string decoderMimeTypes) { Guid clsId; // Get the decoder info SafeMILHandle decoderInfo = new SafeMILHandle(); HRESULT.Check(UnsafeNativeMethods.WICBitmapDecoder.GetDecoderInfo( decoderHandle, out decoderInfo )); // Get CLSID for the decoder HRESULT.Check(UnsafeNativeMethods.WICBitmapCodecInfo.GetContainerFormat(decoderInfo, out clsId)); StringBuilder mimeTypes = null; UInt32 length = 0; // Find the length of the string needed HRESULT.Check(UnsafeNativeMethods.WICBitmapCodecInfo.GetMimeTypes( decoderInfo, 0, mimeTypes, out length )); // get the string back if (length > 0) { mimeTypes = new StringBuilder((int)length); HRESULT.Check(UnsafeNativeMethods.WICBitmapCodecInfo.GetMimeTypes( decoderInfo, length, mimeTypes, out length )); } if (mimeTypes != null) { decoderMimeTypes = mimeTypes.ToString(); } else { decoderMimeTypes = String.Empty; } return clsId; } /// Return a seekable stream if the current one is not seekable private static System.IO.Stream GetSeekableStream(System.IO.Stream bitmapStream) { // MIL codecs require the source stream to be seekable. But if // the source stream is an internet stream, it is not seekable. // The data is probably not in the stream yet. if (bitmapStream.CanSeek) { return bitmapStream; } // If the source is not seekable, we have to download the // stream into a memory stream before we can decode it. // ISSUE-2002/10/03--minliu: later on, if we can make MIL // support progressive JPEG etc., we should take out the // hack here and pass the network stream (CConectStream) // directly to the unmanaged code System.IO.MemoryStream memStream = new System.IO.MemoryStream(); byte[] buffer = new byte[1024]; int read; // Read all the bytes and write it to a memory stream // do { read = bitmapStream.Read(buffer, 0, 1024); if (read <= 0) { break; } memStream.Write(buffer, 0, read); } while (true); // Reset the memory stream pointer back to the begining memStream.Seek(0, System.IO.SeekOrigin.Begin); // Use the new stream return memStream; } /// Check the cache to see if decoder already exists ////// Critical - Eventually calls code that calls unmanaged code. /// TreatAsSafe - Only checks imaging cache to see object exists /// [SecurityCritical, SecurityTreatAsSafe] private static BitmapDecoder CheckCache( Uri uri, out Guid clsId ) { clsId = Guid.Empty; string mimeTypes; if (uri != null) { WeakReference weakRef = ImagingCache.CheckDecoderCache(uri) as WeakReference; if (weakRef != null) { BitmapDecoder bitmapDecoder = weakRef.Target as BitmapDecoder; if ((bitmapDecoder != null) && bitmapDecoder.CheckAccess()) { lock (bitmapDecoder.SyncObject) { clsId = GetCLSIDFromDecoder(bitmapDecoder.InternalDecoder, out mimeTypes); return bitmapDecoder; } } // Remove from the cache if bitmapDecoder is already been collected if (bitmapDecoder == null) { ImagingCache.RemoveFromDecoderCache(uri); } } } return null; } ////// Initialize the codec. /// ////// Critical - Will eventually create a COM object, need to secure it /// [SecurityCritical] private void Initialize(BitmapDecoder decoder) { _isBuiltInDecoder = true; if (decoder != null) { SetupFrames(decoder, null); // // We need to keep the strong reference to the cached decoder for a few reasons: // // The application may release the original cached decoder and then keep a // reference to this decoder only, in which case, the cache can be collected. // This will cause a few undesirable results: // 1. The application may choose to decode the same URI again in which case // we will not retrieve it from the cache even though we have a copy already // decoded. // 2. The original cached decoder holds onto the file stream indirectly which if // collected can cause bad behavior if the entire decoder is not loaded into // memory. // _cachedDecoder = decoder; } else if ((_createOptions & BitmapCreateOptions.DelayCreation) == 0 && _cacheOption == BitmapCacheOption.OnLoad) { SetupFrames(null, null); // Its ok to close the stream since the frames are not delay created and caching is immediate CloseStream(); } if ((_uri != null) && (decoder == null) && _shouldCacheDecoder) { // Add this decoder to the decoder cache ImagingCache.AddToDecoderCache( (_baseUri == null) ? _uri : new Uri(_baseUri, _uri), new WeakReference(this) ); } } ////// Closes the stream if its non-null /// ////// Critical - accesses critical resource _uriStream /// TreatAsSafe - doesn't return the uriStream, just closes it which is safe /// [SecurityCritical, SecurityTreatAsSafe] internal void CloseStream() { if (_uriStream != null) { _uriStream.Close(); _uriStream = null; // The finalizer closes _uriStream, we already closed it so there's no need to run // the finalizer anymore. GC.SuppressFinalize(this); } } ////// Sets up the frames collection. /// Also, ensures that the frame collection for this decoder /// will contain the same underlying frames as the collection /// passed in. This is called by the LateBoundBitmapDecoder /// ////// Critical - access unmanaged code /// TreatAsSafe - only access number of frames in the bitmap, a safe operation /// [SecurityCritical, SecurityTreatAsSafe] internal void SetupFrames(BitmapDecoder decoder, ReadOnlyCollectionframes) { uint numFrames = 1; HRESULT.Check(UnsafeNativeMethods.WICBitmapDecoder.GetFrameCount(_decoderHandle, out numFrames)); _frames = new List ((int)numFrames); // initialize the list of frames to null. // We'll fill it as it's used (pay for play). for (int i = 0; i < (int)numFrames; i++) { if (i > 0 && _cacheOption != BitmapCacheOption.OnLoad) { _createOptions |= BitmapCreateOptions.DelayCreation; } BitmapFrameDecode bfd = null; if ((frames != null) && (frames.Count == (i + 1))) { // If we already have a frames collection, get the BitmapFrame from it bfd = frames[i] as BitmapFrameDecode; bfd.UpdateDecoder(this); } else if (decoder == null) { // All frames should be frozen. bfd = new BitmapFrameDecode( i, _createOptions, _cacheOption, this ); bfd.Freeze(); } else { // if we are creating from an existing cache, use the frames // already stored in that cache // All frames should be frozen. bfd = new BitmapFrameDecode( i, _createOptions, _cacheOption, decoder.Frames[i] as BitmapFrameDecode ); bfd.Freeze(); } _frames.Add(bfd); } } /// /// Checks if the decoder is builtin. If not, throw exception /// private void EnsureBuiltInDecoder() { if (!_isBuiltInDecoder) { throw new NotImplementedException(); } } ////// Note, we must hold onto a reference to the managed stream /// as long as we are using the unmanaged stream in the decoder. /// This method may create a new managed stream. /// [SecurityCritical] private static IntPtr GetIStreamFromStream(ref System.IO.Stream bitmapStream) { IntPtr comStream = IntPtr.Zero; // ensure the stream is seekable bool seekable = bitmapStream.CanSeek; if (bitmapStream is UnmanagedMemoryStream) { UnmanagedMemoryStream memoryStream = bitmapStream as UnmanagedMemoryStream; IntPtr bufferPtr = IntPtr.Zero; int length = 0; unsafe { bufferPtr = (IntPtr) memoryStream.PositionPointer; length = (int) memoryStream.Length; } if (bufferPtr != IntPtr.Zero) { comStream = StreamAsIStream.IStreamFrom(bufferPtr, length); } } else { comStream = StreamAsIStream.IStreamFrom(bitmapStream); if (comStream == IntPtr.Zero) { throw new System.InvalidOperationException( SR.Get(SRID.Image_CantDealWithStream)); } // If the stream is not seekable, we must create a // seekable one for the decoder. if (!seekable || ((!bitmapStream.CanWrite) && (bitmapStream.Length <= 1048576))) { IntPtr memoryStream = StreamAsIStream.IStreamMemoryFrom(comStream); if (memoryStream != IntPtr.Zero) { // we don't need the original stream anymore UnsafeNativeMethods.MILUnknown.ReleaseInterface(ref comStream); bitmapStream = System.IO.Stream.Null; return memoryStream; } else if (!seekable) { throw new System.InvalidOperationException( SR.Get(SRID.Image_CantDealWithStream)); } } } if (comStream == IntPtr.Zero) { throw new System.InvalidOperationException( SR.Get(SRID.Image_CantDealWithStream)); } return comStream; } /// Returns whether decoder can be converted to a string internal bool CanConvertToString() { return (_uri != null); } #endregion #region Internal Abstract /// "Seals" the object internal abstract void SealObject(); #endregion #region Data Members /// check to see if implementation is internal private bool _isBuiltInDecoder; /// Internal Decoder private SafeMILHandle _decoderHandle; /// flag to see if decoder should be inserted in the cache private bool _shouldCacheDecoder = true; /// flag to see if decoder should be inserted in the cache private bool _isOriginalWritable = false; /// If the palette is already cached private bool _isPaletteCached; /// Palette private BitmapPalette _palette = null; /// If the ColorContext is already cached private bool _isColorContextCached = false; /// ColorContexts collection internal ReadOnlyCollection _readOnlycolorContexts; /// If the thumbnail is already cached private bool _isThumbnailCached; /// /// Metadata /// ////// Critical - Access only granted if SiteOfOrigin demanded. /// [SecurityCritical] private BitmapMetadata _metadata; /// If the metadata is already cached private bool _isMetadataCached; /// Thumbnail private BitmapSource _thumbnail = null; /// CodecInfo private BitmapCodecInfo _codecInfo; /// If the preview is already cached private bool _isPreviewCached; /// Preview private BitmapSource _preview = null; /// Frames collection internal List_frames; /// Frames collection internal ReadOnlyCollection _readOnlyFrames; /// Stream internal Stream _stream; /// Uri internal Uri _uri; /// Base Uri, only stored internally. Not used for serialization internal Uri _baseUri; /// Uri Stream -- this is the stream that was created from the passed in Uri /// /// Critical: Obtained under elevation /// [SecurityCritical] internal Stream _uriStream; /// CreateOptions internal BitmapCreateOptions _createOptions; /// CacheOption internal BitmapCacheOption _cacheOption; /// Event helper for download completed event internal UniqueEventHelper _downloadEvent = new UniqueEventHelper(); /// Event helper for download progress event internal UniqueEventHelper_progressEvent = new UniqueEventHelper (); /// Event helper for download failed event internal UniqueEventHelper _failedEvent = new UniqueEventHelper (); /// SyncObject private object _syncObject = new Object(); // this is data that we cache as a performance optimization. It is ok to do so since we do not want to // handle this key change in the lifetime of this app. private static SecurityCriticalDataForSet isImageDisabled; /// /// Critical: This variable guards against reinitialization of the cached registry value /// [SecurityCritical] private static bool isImageDisabledInitialized; // For UnmanagedMemoryStream we want to make sure that buffer // its pointing to is not getting release until decoder is alive private UnmanagedMemoryStream _unmanagedMemoryStream; ////// Critical: This variable guards against closing of the filehandle /// which gets passed to unmanaged decoder. /// [SecurityCritical] private SafeFileHandle _safeFilehandle; private BitmapDecoder _cachedDecoder; #endregion } #endregion // BitmapDecoder } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ResourceProperty.cs
- TcpChannelHelper.cs
- FileLevelControlBuilderAttribute.cs
- LinqDataSourceDeleteEventArgs.cs
- UrlMappingsSection.cs
- VisualCollection.cs
- OutputCacheProfileCollection.cs
- ListViewItemMouseHoverEvent.cs
- PointLightBase.cs
- JapaneseLunisolarCalendar.cs
- SamlAuthorityBinding.cs
- AncillaryOps.cs
- Pair.cs
- ConfigXmlText.cs
- ReaderWriterLockWrapper.cs
- PreProcessInputEventArgs.cs
- DataService.cs
- Sequence.cs
- EraserBehavior.cs
- BamlBinaryReader.cs
- BevelBitmapEffect.cs
- ExpressionVisitor.cs
- StringStorage.cs
- XPathAxisIterator.cs
- Icon.cs
- CopyOfAction.cs
- WindowsPrincipal.cs
- ImageFormat.cs
- ClientFormsAuthenticationCredentials.cs
- MetadataItemEmitter.cs
- RectangleGeometry.cs
- PixelFormatConverter.cs
- HyperLinkField.cs
- AdRotatorDesigner.cs
- EnlistmentState.cs
- WebPartConnection.cs
- XmlCompatibilityReader.cs
- HtmlShimManager.cs
- MetadataProperty.cs
- SourceItem.cs
- GetUserPreferenceRequest.cs
- EncodingInfo.cs
- WindowsClientElement.cs
- ReadOnlyPropertyMetadata.cs
- Material.cs
- DynamicPropertyReader.cs
- Viewport3DAutomationPeer.cs
- RelationshipWrapper.cs
- BitmapPalettes.cs
- PathTooLongException.cs
- PointCollection.cs
- CodeDOMUtility.cs
- SoapSchemaExporter.cs
- CompilerInfo.cs
- Double.cs
- _SslState.cs
- HttpWebResponse.cs
- FontFamily.cs
- DPCustomTypeDescriptor.cs
- VideoDrawing.cs
- ComponentManagerBroker.cs
- Marshal.cs
- RelatedView.cs
- PenContexts.cs
- ProjectionCamera.cs
- XsdValidatingReader.cs
- IdentityManager.cs
- _UriTypeConverter.cs
- MetadataException.cs
- RenderingEventArgs.cs
- DefaultAssemblyResolver.cs
- PermissionListSet.cs
- CommonProperties.cs
- __Filters.cs
- GroupStyle.cs
- Aggregates.cs
- TextSegment.cs
- GeometryGroup.cs
- TypeExtension.cs
- TypeUtil.cs
- SingleConverter.cs
- IsolatedStorageException.cs
- ConstNode.cs
- _SecureChannel.cs
- OLEDB_Enum.cs
- baseshape.cs
- MouseGestureValueSerializer.cs
- listviewsubitemcollectioneditor.cs
- WebBrowserProgressChangedEventHandler.cs
- SqlReferenceCollection.cs
- IndentedTextWriter.cs
- ToolStripPanelDesigner.cs
- DataIdProcessor.cs
- ProviderConnectionPoint.cs
- Signature.cs
- BaseCodePageEncoding.cs
- PageThemeBuildProvider.cs
- SelectionEditor.cs
- WebScriptMetadataFormatter.cs
- StateMachineWorkflow.cs