Propagate the principal responsible an image load all the way to
authorbzbarsky@mit.edu
Tue, 03 Jul 2007 11:45:39 -0700
changeset 3088 d943d68c972a3b1772a705ac3424c600554c55f5
parent 3087 f55a021be699628199202262e7e7e1e0590d6dff
child 3089 87d3a9dc90e95975dfe4b8196fa46d3827735911
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs310165
milestone1.9a7pre
Propagate the principal responsible an image load all the way to CanLoadImage/LoadImage in nsContentUtils, and use it for security checks there. Bug 310165, r=sicking, sr=dbaron
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
content/base/src/nsImageLoadingContent.cpp
content/xbl/src/nsXBLResourceLoader.cpp
layout/style/nsCSSStyleSheet.cpp
layout/style/nsCSSStyleSheet.h
layout/style/nsCSSValue.cpp
layout/xul/base/src/nsImageBoxFrame.cpp
layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -526,40 +526,45 @@ public:
                                 nsIAtom* aName);
 
   /**
    * Method to do security and content policy checks on the image URI
    *
    * @param aURI uri of the image to be loaded
    * @param aContext the context the image is loaded in (eg an element)
    * @param aLoadingDocument the document we belong to
+   * @param aLoadingPrincipal the principal doing the load
    * @param aImageBlockingStatus the nsIContentPolicy blocking status for this
    *        image.  This will be set even if a security check fails for the
    *        image, to some reasonable REJECT_* value.  This out param will only
    *        be set if it's non-null.
    * @return PR_TRUE if the load can proceed, or PR_FALSE if it is blocked.
    *         Note that aImageBlockingStatus, if set will always be an ACCEPT
    *         status if PR_TRUE is returned and always be a REJECT_* status if
    *         PR_FALSE is returned.
    */
   static PRBool CanLoadImage(nsIURI* aURI,
                              nsISupports* aContext,
                              nsIDocument* aLoadingDocument,
+                             nsIPrincipal* aLoadingPrincipal,
                              PRInt16* aImageBlockingStatus = nsnull);
   /**
    * Method to start an image load.  This does not do any security checks.
    *
    * @param aURI uri of the image to be loaded
    * @param aLoadingDocument the document we belong to
+   * @param aLoadingPrincipal the principal doing the load
+   * @param aReferrer the referrer URI
    * @param aObserver the observer for the image load
    * @param aLoadFlags the load flags to use.  See nsIRequest
    * @return the imgIRequest for the image load
    */
   static nsresult LoadImage(nsIURI* aURI,
                             nsIDocument* aLoadingDocument,
+                            nsIPrincipal* aLoadingPrincipal,
                             nsIURI* aReferrer,
                             imgIDecoderObserver* aObserver,
                             PRInt32 aLoadFlags,
                             imgIRequest** aRequest);
 
   /**
    * Method to get an nsIImage from an image loading content
    *
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -2051,20 +2051,22 @@ nsContentUtils::SplitExpatName(const PRU
   *aLocalName = NS_NewAtom(NS_ConvertUTF16toUTF8(nameStart,
                                                  nameEnd - nameStart));
 }
 
 // static
 PRBool
 nsContentUtils::CanLoadImage(nsIURI* aURI, nsISupports* aContext,
                              nsIDocument* aLoadingDocument,
+                             nsIPrincipal* aLoadingPrincipal,
                              PRInt16* aImageBlockingStatus)
 {
   NS_PRECONDITION(aURI, "Must have a URI");
   NS_PRECONDITION(aLoadingDocument, "Must have a document");
+  NS_PRECONDITION(aLoadingPrincipal, "Must have a loading principal");
 
   nsresult rv;
 
   PRUint32 appType = nsIDocShell::APP_TYPE_UNKNOWN;
 
   {
     nsCOMPtr<nsISupports> container = aLoadingDocument->GetContainer();
     nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
@@ -2079,68 +2081,78 @@ nsContentUtils::CanLoadImage(nsIURI* aUR
       if (!docShell || NS_FAILED(docShell->GetAppType(&appType))) {
         appType = nsIDocShell::APP_TYPE_UNKNOWN;
       }
     }
   }
 
   if (appType != nsIDocShell::APP_TYPE_EDITOR) {
     // Editor apps get special treatment here, editors can load images
-    // from anywhere.
+    // from anywhere.  This allows editor to insert images from file://
+    // into documents that are being edited.
     rv = sSecurityManager->
-      CheckLoadURIWithPrincipal(aLoadingDocument->NodePrincipal(), aURI,
+      CheckLoadURIWithPrincipal(aLoadingPrincipal, aURI,
                                 nsIScriptSecurityManager::ALLOW_CHROME);
     if (NS_FAILED(rv)) {
       if (aImageBlockingStatus) {
         // Reject the request itself, not all requests to the relevant
         // server...
         *aImageBlockingStatus = nsIContentPolicy::REJECT_REQUEST;
       }
       return PR_FALSE;
     }
   }
 
+  nsCOMPtr<nsIURI> loadingURI;
+  nsresult rv = aLoadingPrincipal->GetURI(getter_AddRefs(loadingURI));
+  NS_ENSURE_SUCCESS(rv, PR_FALSE);
+
   PRInt16 decision = nsIContentPolicy::ACCEPT;
 
   rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_IMAGE,
                                  aURI,
-                                 aLoadingDocument->GetDocumentURI(),
+                                 loadingURI,
                                  aContext,
                                  EmptyCString(), //mime guess
                                  nsnull,         //extra
                                  &decision,
                                  GetContentPolicy());
 
   if (aImageBlockingStatus) {
     *aImageBlockingStatus =
       NS_FAILED(rv) ? nsIContentPolicy::REJECT_REQUEST : decision;
   }
   return NS_FAILED(rv) ? PR_FALSE : NS_CP_ACCEPTED(decision);
 }
 
 // static
 nsresult
 nsContentUtils::LoadImage(nsIURI* aURI, nsIDocument* aLoadingDocument,
-                          nsIURI* aReferrer, imgIDecoderObserver* aObserver,
-                          PRInt32 aLoadFlags, imgIRequest** aRequest)
+                          nsIPrincipal* aLoadingPrincipal, nsIURI* aReferrer,
+                          imgIDecoderObserver* aObserver, PRInt32 aLoadFlags,
+                          imgIRequest** aRequest)
 {
   NS_PRECONDITION(aURI, "Must have a URI");
   NS_PRECONDITION(aLoadingDocument, "Must have a document");
+  NS_PRECONDITION(aLoadingPrincipal, "Must have a principal");
   NS_PRECONDITION(aRequest, "Null out param");
 
   if (!sImgLoader) {
     // nothing we can do here
     return NS_OK;
   }
 
   nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
   NS_ASSERTION(loadGroup, "Could not get loadgroup; onload may fire too early");
 
   nsIURI *documentURI = aLoadingDocument->GetDocumentURI();
 
+  // We don't use aLoadingPrincipal for anything here yet... but we
+  // will.  See bug 377092.
+
   // XXXbz using "documentURI" for the initialDocumentURI is not quite
   // right, but the best we can do here...
   return sImgLoader->LoadImage(aURI,                 /* uri to load */
                                documentURI,          /* initialDocumentURI */
                                aReferrer,            /* referrer */
                                loadGroup,            /* loadgroup */
                                aObserver,            /* imgIDecoderObserver */
                                aLoadingDocument,     /* uniquification key */
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -528,26 +528,36 @@ nsImageLoadingContent::LoadImage(nsIURI*
       return NS_OK;
     }
   }
 
   // From this point on, our state could change before return, so make
   // sure to notify if it does.
   AutoStateChanger changer(this, aNotify);
 
+  // Use the principal of aDocument to avoid having to QI |this| an extra time.
+  // It should be the same as the principal of this node in any case.
+#ifdef DEBUG
+  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
+  NS_ASSERTION(thisContent &&
+               thisContent->NodePrincipal() == aDocument->NodePrincipal(),
+               "Principal mismatch?");
+#endif
+  
   // If we'll be loading a new image, we want to cancel our existing
   // requests; the question is what reason to pass in.  If everything
   // is going smoothly, that reason should be
   // NS_ERROR_IMAGE_SRC_CHANGED so that our frame (if any) will know
   // not to show the broken image icon.  If the load is blocked by the
   // content policy or security manager, we will want to cancel with
   // the error code from those.
 
   PRInt16 newImageStatus;
   PRBool loadImage = nsContentUtils::CanLoadImage(aNewURI, this, aDocument,
+                                                  aDocument->NodePrincipal(),
                                                   &newImageStatus);
   NS_ASSERTION(loadImage || !NS_CP_ACCEPTED(newImageStatus),
                "CanLoadImage lied");
 
   nsresult cancelResult = loadImage ? NS_ERROR_IMAGE_SRC_CHANGED
                                     : NS_ERROR_IMAGE_BLOCKED;
 
   CancelImageRequests(cancelResult, PR_FALSE, newImageStatus);
@@ -564,16 +574,17 @@ nsImageLoadingContent::LoadImage(nsIURI*
     // Don't actually load anything!  This was blocked by CanLoadImage.
     FireEvent(NS_LITERAL_STRING("error"));
     return NS_OK;
   }
 
   nsCOMPtr<imgIRequest> & req = mCurrentRequest ? mPendingRequest : mCurrentRequest;
 
   rv = nsContentUtils::LoadImage(aNewURI, aDocument,
+                                 aDocument->NodePrincipal(),
                                  aDocument->GetDocumentURI(),
                                  this, aLoadFlags,
                                  getter_AddRefs(req));
   if (NS_FAILED(rv)) {
     FireEvent(NS_LITERAL_STRING("error"));
     return NS_OK;
   }
 
--- a/content/xbl/src/nsXBLResourceLoader.cpp
+++ b/content/xbl/src/nsXBLResourceLoader.cpp
@@ -117,26 +117,26 @@ nsXBLResourceLoader::LoadResources(PRBoo
     if (curr->mSrc.IsEmpty())
       continue;
 
     if (NS_FAILED(NS_NewURI(getter_AddRefs(url), curr->mSrc,
                             doc->GetDocumentCharacterSet().get(), docURL)))
       continue;
 
     if (curr->mType == nsGkAtoms::image) {
-      if (!nsContentUtils::CanLoadImage(url, doc, doc)) {
+      if (!nsContentUtils::CanLoadImage(url, doc, doc, doc->NodePrincipal())) {
         // We're not permitted to load this image, move on...
         continue;
       }
 
       // Now kick off the image load...
       // Passing NULL for pretty much everything -- cause we don't care!
       // XXX: initialDocumentURI is NULL! 
       nsCOMPtr<imgIRequest> req;
-      nsContentUtils::LoadImage(url, doc, docURL, nsnull,
+      nsContentUtils::LoadImage(url, doc, doc->NodePrincipal(), docURL, nsnull,
                                 nsIRequest::LOAD_BACKGROUND,
                                 getter_AddRefs(req));
     }
     else if (curr->mType == nsGkAtoms::stylesheet) {
       // Kick off the load of the stylesheet.
 
       // Always load chrome synchronously
       PRBool chrome;
--- a/layout/style/nsCSSStyleSheet.cpp
+++ b/layout/style/nsCSSStyleSheet.cpp
@@ -1133,17 +1133,17 @@ nsCSSStyleSheet::StyleRuleCount(PRInt32&
   aCount = mInner->mOrderedRules.Count();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSSStyleSheet::GetStyleRuleAt(PRInt32 aIndex, nsICSSRule*& aRule) const
 {
   // Important: If this function is ever made scriptable, we must add
-  // a security check here. See GetCSSRules below for an example.
+  // a security check here. See GetCssRules below for an example.
   aRule = mInner->mOrderedRules.SafeObjectAt(aIndex);
   if (aRule) {
     NS_ADDREF(aRule);
     return NS_OK;
   }
 
   return NS_ERROR_ILLEGAL_VALUE;
 }
@@ -1316,16 +1316,45 @@ nsCSSStyleSheet::WillDirty()
 
 void
 nsCSSStyleSheet::DidDirty()
 {
   ClearRuleCascades();
   mDirty = PR_TRUE;
 }
 
+nsresult
+nsCSSStyleSheet::SubjectSubsumesInnerPrincipal() const
+{
+  // Get the security manager and do the subsumes check
+  nsIScriptSecurityManager *securityManager =
+    nsContentUtils::GetSecurityManager();
+
+  nsCOMPtr<nsIPrincipal> subjectPrincipal;
+  securityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
+
+  if (!subjectPrincipal) {
+    return NS_OK;
+  }
+
+  PRBool subsumes;
+  nsresult rv = subjectPrincipal->Subsumes(mInner->mPrincipal, &subsumes);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (subsumes) {
+    return NS_OK;
+  }
+  
+  if (!nsContentUtils::IsCallerTrustedForWrite()) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  return NS_OK;
+}
+
 NS_IMETHODIMP 
 nsCSSStyleSheet::IsModified(PRBool* aSheetModified) const
 {
   *aSheetModified = mDirty;
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -1449,37 +1478,18 @@ nsCSSStyleSheet::GetCssRules(nsIDOMCSSRu
   PRBool complete;
   GetComplete(complete);
   if (!complete) {
     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   }
   
   //-- Security check: Only scripts whose principal subsumes that of the
   //   style sheet can access rule collections.
-
-  // Get the security manager and do the subsumes check
-  nsIScriptSecurityManager *securityManager =
-    nsContentUtils::GetSecurityManager();
-
-  nsCOMPtr<nsIPrincipal> subjectPrincipal;
-  securityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
-
-  nsresult rv = NS_OK;
-  if (subjectPrincipal) {
-    PRBool subsumes;
-    rv = subjectPrincipal->Subsumes(mInner->mPrincipal, &subsumes);
-    if (NS_SUCCEEDED(rv) && !subsumes &&
-        !nsContentUtils::IsCallerTrustedForRead()) {
-      rv = NS_ERROR_DOM_SECURITY_ERR;
-    }
-
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-  }
+  nsresult rv = SubjectSubsumesInnerPrincipal();
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // OK, security check passed, so get the rule collection
   if (nsnull == mRuleCollection) {
     mRuleCollection = new CSSRuleListImpl(this);
     if (nsnull == mRuleCollection) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
     NS_ADDREF(mRuleCollection);
@@ -1498,16 +1508,21 @@ nsCSSStyleSheet::InsertRule(const nsAStr
 {
   // No doing this if the sheet is not complete!
   PRBool complete;
   GetComplete(complete);
   if (!complete) {
     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   }
 
+  //-- Security check: Only scripts whose principal subsumes that of the
+  //   style sheet can modify rule collections.
+  nsresult rv = SubjectSubsumesInnerPrincipal();
+  NS_ENSURE_SUCCESS(rv, rv);
+
   if (aRule.IsEmpty()) {
     // Nothing to do here
     return NS_OK;
   }
   
   nsresult result;
   result = WillDirty();
   if (NS_FAILED(result))
@@ -1668,16 +1683,21 @@ nsCSSStyleSheet::DeleteRule(PRUint32 aIn
   nsresult result = NS_ERROR_DOM_INDEX_SIZE_ERR;
   // No doing this if the sheet is not complete!
   PRBool complete;
   GetComplete(complete);
   if (!complete) {
     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   }
 
+  //-- Security check: Only scripts whose principal subsumes that of the
+  //   style sheet can modify rule collections.
+  nsresult rv = SubjectSubsumesInnerPrincipal();
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // XXX TBI: handle @rule types
   mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, PR_TRUE);
     
   result = WillDirty();
 
   if (NS_SUCCEEDED(result)) {
     if (aIndex >= PRUint32(mInner->mOrderedRules.Count()))
       return NS_ERROR_DOM_INDEX_SIZE_ERR;
--- a/layout/style/nsCSSStyleSheet.h
+++ b/layout/style/nsCSSStyleSheet.h
@@ -181,16 +181,21 @@ private:
 protected:
   virtual ~nsCSSStyleSheet();
 
   void ClearRuleCascades();
 
   nsresult WillDirty();
   void     DidDirty();
 
+  // Return success if the subject principal subsumes the principal of our
+  // inner, error otherwise.  This will also succeed if the subject has
+  // UniversalBrowserWrite.
+  nsresult SubjectSubsumesInnerPrincipal() const;
+
 protected:
   nsString              mTitle;
   nsCOMPtr<nsMediaList> mMedia;
   nsCSSStyleSheet*      mFirstChild;
   nsCSSStyleSheet*      mNext;
   nsICSSStyleSheet*     mParent;    // weak ref
   nsICSSImportRule*     mOwnerRule; // weak ref
 
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -430,19 +430,20 @@ nsCSSValue::URL::operator==(const URL& a
 nsCSSValue::Image::Image(nsIURI* aURI, nsStringBuffer* aString,
                          nsIURI* aReferrer, nsIPrincipal* aOriginPrincipal,
                          nsIDocument* aDocument)
   : URL(aURI, aString, aReferrer, aOriginPrincipal)
 {
   MOZ_COUNT_CTOR(nsCSSValue::Image);
 
   if (mURI &&
-      nsContentUtils::CanLoadImage(mURI, aDocument, aDocument)) {
-    nsContentUtils::LoadImage(mURI, aDocument, aReferrer, nsnull,
-                              nsIRequest::LOAD_NORMAL,
+      nsContentUtils::CanLoadImage(mURI, aDocument, aDocument,
+                                   aOriginPrincipal)) {
+    nsContentUtils::LoadImage(mURI, aDocument, aOriginPrincipal, aReferrer,
+                              nsnull, nsIRequest::LOAD_NORMAL,
                               getter_AddRefs(mRequest));
   }
 }
 
 nsCSSValue::Image::~Image()
 {
   MOZ_COUNT_DTOR(nsCSSValue::Image);
 }
--- a/layout/xul/base/src/nsImageBoxFrame.cpp
+++ b/layout/xul/base/src/nsImageBoxFrame.cpp
@@ -260,19 +260,20 @@ nsImageBoxFrame::UpdateImage()
     }
     nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI();
     nsCOMPtr<nsIURI> uri;
     nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
                                               src,
                                               doc,
                                               baseURI);
 
-    if (uri && nsContentUtils::CanLoadImage(uri, mContent, doc)) {
-      nsContentUtils::LoadImage(uri, doc, doc->GetDocumentURI(),
-                                mListener, mLoadFlags,
+    if (uri && nsContentUtils::CanLoadImage(uri, mContent, doc,
+                                            mContent->NodePrincipal())) {
+      nsContentUtils::LoadImage(uri, doc, mContent->NodePrincipal(),
+                                doc->GetDocumentURI(), mListener, mLoadFlags,
                                 getter_AddRefs(mImageRequest));
     }
   } else {
     // Only get the list-style-image if we aren't being drawn
     // by a native theme.
     PRUint8 appearance = GetStyleDisplay()->mAppearance;
     if (!(appearance && nsBox::gTheme && 
           nsBox::gTheme->ThemeSupportsWidget(nsnull, this, appearance))) {
--- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
+++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
@@ -2120,19 +2120,23 @@ nsTreeBodyFrame::GetImage(PRInt32 aRowIn
       nsCOMPtr<nsIURI> srcURI;
       nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(srcURI),
                                                 imageSrc,
                                                 doc,
                                                 baseURI);
       if (!srcURI)
         return NS_ERROR_FAILURE;
 
-      if (nsContentUtils::CanLoadImage(srcURI, mContent, doc)) {
+      // XXXbz what's the origin principal for this stuff that comes from our
+      // view?  I guess we should assume that it's the node's principal...
+      if (nsContentUtils::CanLoadImage(srcURI, mContent, doc,
+                                       mContent->NodePrincipal())) {
         nsresult rv = nsContentUtils::LoadImage(srcURI,
                                                 doc,
+                                                mContent->NodePrincipal(),
                                                 doc->GetDocumentURI(),
                                                 imgDecoderObserver,
                                                 nsIRequest::LOAD_NORMAL,
                                                 getter_AddRefs(imageRequest));
         NS_ENSURE_SUCCESS(rv, rv);
                                   
       }
     }