WebCore:
authorweinig@apple.com <weinig@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Dec 2007 22:39:51 +0000 (22:39 +0000)
committerweinig@apple.com <weinig@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Dec 2007 22:39:51 +0000 (22:39 +0000)
        Reviewed and landed by Sam Weinig.

        http://bugs.webkit.org/show_bug.cgi?id=15313
        <rdar://problem/5514516>

        The same-origin check was incorrect in two cases (both fixed in this
        patch):

        A) If both the source and the target have set their document.domain
           to the same value, the protocol must also match in order for
           access to be allowed.  Without this requirement, the browser is
           vulnerable to the following attack:

           1) Suppose there is an HTTPS site (www.example.com) that sets
              document.domain = "example.com".
           2) A network attacker redirects the browser to http://www.example.com/
              a) injects script to set document.domain = "example.com", and
              b) opens a window to https://www.example.com/
           3) Now the network attacker can inject script into the HTTPS page,
              stealing cookies and issuing banking transactions.

        B) If only one of the source and target has set document.domain, then
           access should be denied.  With this behavior, the browser is
           vulnerable to the following attack:

           1) Suppose http://foo.example.com/ opens an iframe to
              http://foo.example.com/frame.html that
              a) sets document.domain = "example.com", and
              b) opens an iframe to http://bar.example.com/
              This is a common usage of document.domain for cross-domain
              communication, see for example:
                http://www.collinjackson.com/research/papers/fp801-jackson.pdf
           2) The inner-most iframe, which is from bar.example.com, sets
              document.domain = "example.com".
           3) Now the inner-most iframe can inject script into the middle
              iframe (say via document.write).  This bar.example.com script
              now has access to the outer-most frame (from foo.example.com).

        Both these changes cause WebKit to match the behavior of Firefox 2 and
        IE6 in these cases.  This patch includes regression tests for both
        issues.

        Internet Explorer 7 and Opera 9 are more strict in that they require
        the port numbers to match when both pages have document.domain set.
        Opera 9 allows access when only one page has set document.domain, but
        this is a security vulnerability.

        Tests: http/tests/security/cross-frame-access-child-explicit-domain.html
               http/tests/security/cross-frame-access-parent-explicit-domain.html

        * bindings/js/kjs_window.cpp:
        (KJS::createWindow):
        (KJS::Window::allowsAccessFrom):
        * dom/Document.cpp:
        (WebCore::Document::domain):
        (WebCore::Document::setDomain):
        (WebCore::Document::initSecurityOrigin):
        * dom/Document.h:
        (WebCore::Document::securityOrigin):
        * loader/FrameLoader.cpp:
        (WebCore::FrameLoader::begin):
        (WebCore::FrameLoader::checkCallImplicitClose):
        (WebCore::FrameLoader::shouldAllowNavigation):
        * platform/SecurityOrigin.cpp:
        (WebCore::SecurityOrigin::setForURL):
        (WebCore::SecurityOrigin::createForFrame):
        (WebCore::SecurityOrigin::canAccess):
        * platform/SecurityOrigin.h:
        (WebCore::SecurityOrigin::domain):
        * storage/Database.cpp:
        (WebCore::Database::openDatabase):
        (WebCore::Database::Database):
        (WebCore::Database::securityOriginData):
        * storage/Database.h:
        (WebCore::Database::databaseDebugName):
        * storage/DatabaseTracker.cpp:
        (WebCore::DatabaseTracker::canEstablishDatabase):
        * storage/SQLTransaction.cpp:
        (WebCore::SQLTransaction::postflightAndCommit):
        (WebCore::SQLTransaction::cleanupAfterTransactionErrorCallback):

LayoutTests:

        Reviewed and landed by Sam Weinig.

        Update LayoutTests for http://bugs.webkit.org/show_bug.cgi?id=15313

        * http/tests/security/cross-frame-access-child-explicit-domain-expected.txt: Added.
        * http/tests/security/cross-frame-access-child-explicit-domain.html: Added.
        * http/tests/security/cross-frame-access-custom-expected.txt:
        * http/tests/security/cross-frame-access-parent-explicit-domain-expected.txt: Added.
        * http/tests/security/cross-frame-access-parent-explicit-domain.html: Added.
        * http/tests/security/cross-frame-access-port-explicit-domain-expected.txt:
        * http/tests/security/cross-frame-access-protocol-explicit-domain-expected.txt:
        * http/tests/security/cross-frame-access-protocol-explicit-domain.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@28912 268f45cc-cd09-0410-ab3c-d52691b4dbfc

20 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/security/cross-frame-access-child-explicit-domain-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/cross-frame-access-child-explicit-domain.html [new file with mode: 0644]
LayoutTests/http/tests/security/cross-frame-access-custom-expected.txt
LayoutTests/http/tests/security/cross-frame-access-parent-explicit-domain-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/cross-frame-access-parent-explicit-domain.html [new file with mode: 0644]
LayoutTests/http/tests/security/cross-frame-access-port-explicit-domain-expected.txt
LayoutTests/http/tests/security/cross-frame-access-protocol-explicit-domain-expected.txt
LayoutTests/http/tests/security/cross-frame-access-protocol-explicit-domain.html
WebCore/ChangeLog
WebCore/bindings/js/kjs_window.cpp
WebCore/dom/Document.cpp
WebCore/dom/Document.h
WebCore/loader/FrameLoader.cpp
WebCore/platform/SecurityOrigin.cpp
WebCore/platform/SecurityOrigin.h
WebCore/storage/Database.cpp
WebCore/storage/Database.h
WebCore/storage/DatabaseTracker.cpp
WebCore/storage/SQLTransaction.cpp

index 777bab2247e61609edb82f52b4cac147afd56dc1..7c6fe96d09f9aafbf7f71a98f9abc337791dbc39 100644 (file)
@@ -1,3 +1,18 @@
+2007-12-20  Adam Barth  <hk9565@gmail.com>
+
+        Reviewed and landed by Sam Weinig.
+
+        Update LayoutTests for http://bugs.webkit.org/show_bug.cgi?id=15313
+
+        * http/tests/security/cross-frame-access-child-explicit-domain-expected.txt: Added.
+        * http/tests/security/cross-frame-access-child-explicit-domain.html: Added.
+        * http/tests/security/cross-frame-access-custom-expected.txt:
+        * http/tests/security/cross-frame-access-parent-explicit-domain-expected.txt: Added.
+        * http/tests/security/cross-frame-access-parent-explicit-domain.html: Added.
+        * http/tests/security/cross-frame-access-port-explicit-domain-expected.txt:
+        * http/tests/security/cross-frame-access-protocol-explicit-domain-expected.txt:
+        * http/tests/security/cross-frame-access-protocol-explicit-domain.html:
+
 2007-12-20  johnnyding.webkit  <johnnyding.webkit@gmail.com>
        
         Reviewed by Alexey. Landed by Stephanie.
diff --git a/LayoutTests/http/tests/security/cross-frame-access-child-explicit-domain-expected.txt b/LayoutTests/http/tests/security/cross-frame-access-child-explicit-domain-expected.txt
new file mode 100644 (file)
index 0000000..e35bca1
--- /dev/null
@@ -0,0 +1,16 @@
+CONSOLE MESSAGE: line 1: Unsafe JavaScript attempt to access frame with URL http://127.0.0.1:8000/security/resources/cross-frame-iframe-with-explicit-domain-set.html from frame with URL http://127.0.0.1:8000/security/cross-frame-access-child-explicit-domain.html. Domains, protocols and ports must match.
+
+
+PASS: Cross frame access to frame explicitly setting document.domain was denied.
+
+
+
+--------
+Frame: 'aFrame'
+--------
+Inner iframe with explicit document.domain set.
+
+--------
+Frame: '<!--framePath //<!--frame1-->-->'
+--------
+
diff --git a/LayoutTests/http/tests/security/cross-frame-access-child-explicit-domain.html b/LayoutTests/http/tests/security/cross-frame-access-child-explicit-domain.html
new file mode 100644 (file)
index 0000000..31d74ec
--- /dev/null
@@ -0,0 +1,32 @@
+<html>
+<head>
+    <script src="resources/cross-frame-access.js"></script>
+</head>
+<body>
+<iframe id="aFrame"></iframe>
+<pre id="console"></pre>
+<script>
+    var url = "http://127.0.0.1:8000/security/resources/cross-frame-iframe-with-explicit-domain-set.html";
+    var iframeId ="aFrame";
+    var passMessage = "PASS: Cross frame access to frame explicitly setting document.domain was denied.";
+    var failMessage = "Fail: Cross frame access to frame explicitly setting document.domain was allowed.";
+    cannotAccessFrame(url, iframeId, passMessage, failMessage);
+</script>
+</body>
+</html>
+<html>
+<head>
+    <script src="resources/cross-frame-access.js"></script>
+</head>
+<body>
+<iframe id="aFrame"></iframe>
+<pre id="console"></pre>
+<script>
+    var url = "http://127.0.0.1:8000/security/resources/cross-frame-iframe-with-explicit-domain-set.html";
+    var iframeId ="aFrame";
+    var passMessage = "PASS: Cross frame access to frame explicitly setting document.domain was denied.";
+    var failMessage = "Fail: Cross frame access to frame explicitly setting document.domain was allowed.";
+    cannotAccessFrame(url, iframeId, passMessage, failMessage);
+</script>
+</body>
+</html>
index 74c5c9e951e12b37b778cc5879d9767a90eaa543..fceddb962b5040812ea2fe9e77ea7ea8448ed52f 100644 (file)
@@ -1,3 +1,5 @@
+CONSOLE MESSAGE: line 1: Unsafe JavaScript attempt to access frame with URL http://127.0.0.1:8000/security/resources/cross-frame-iframe-with-explicit-domain-set.html from frame with URL http://127.0.0.1:8000/security/cross-frame-access-child-explicit-domain.html. Domains, protocols and ports must match.
+
 CONSOLE MESSAGE: line 1: Unsafe JavaScript attempt to access frame with URL http://localhost:8000/security/resources/cross-frame-iframe-for-get-test.html from frame with URL http://127.0.0.1:8000/security/cross-frame-access-custom.html. Domains, protocols and ports must match.
 
 CONSOLE MESSAGE: line 1: Unsafe JavaScript attempt to access frame with URL http://localhost:8000/security/resources/cross-frame-iframe-for-get-test.html from frame with URL http://127.0.0.1:8000/security/cross-frame-access-custom.html. Domains, protocols and ports must match.
diff --git a/LayoutTests/http/tests/security/cross-frame-access-parent-explicit-domain-expected.txt b/LayoutTests/http/tests/security/cross-frame-access-parent-explicit-domain-expected.txt
new file mode 100644 (file)
index 0000000..4a51a7c
--- /dev/null
@@ -0,0 +1,23 @@
+CONSOLE MESSAGE: line 1: Unsafe JavaScript attempt to access frame with URL http://127.0.0.1:8000/security/resources/cross-frame-iframe.html from frame with URL http://127.0.0.1:8000/security/cross-frame-access-parent-explicit-domain.html. Domains, protocols and ports must match.
+
+
+PASS: Cross frame access from frame explicitly setting document.domain was denied.
+
+
+
+--------
+Frame: 'aFrame'
+--------
+Inner iframe.
+
+
+
+--------
+Frame: 'flag'
+--------
+
+
+--------
+Frame: '<!--framePath //<!--frame1-->-->'
+--------
+
diff --git a/LayoutTests/http/tests/security/cross-frame-access-parent-explicit-domain.html b/LayoutTests/http/tests/security/cross-frame-access-parent-explicit-domain.html
new file mode 100644 (file)
index 0000000..c349272
--- /dev/null
@@ -0,0 +1,38 @@
+<html>
+<head>
+    <script src="resources/cross-frame-access.js"></script>
+</head>
+<body>
+<iframe id="aFrame"></iframe>
+<pre id="console"></pre>
+<script>
+    // Explicitly set the domain.
+    document.domain = "127.0.0.1";
+
+    var url = "http://127.0.0.1:8000/security/resources/cross-frame-iframe.html";
+    var iframeId ="aFrame";
+    var passMessage = "PASS: Cross frame access from frame explicitly setting document.domain was denied.";
+    var failMessage = "Fail: Cross frame access from frame explicitly setting document.domain was allowed.";
+    cannotAccessFrame(url, iframeId, passMessage, failMessage);
+</script>
+</body>
+</html>
+<html>
+<head>
+    <script src="resources/cross-frame-access.js"></script>
+</head>
+<body>
+<iframe id="aFrame"></iframe>
+<pre id="console"></pre>
+<script>
+    // Explicitly set the domain.
+    document.domain = "127.0.0.1";
+
+    var url = "http://127.0.0.1:8000/security/resources/cross-frame-iframe.html";
+    var iframeId ="aFrame";
+    var passMessage = "PASS: Cross frame access from frame explicitly setting document.domain was denied.";
+    var failMessage = "Fail: Cross frame access from frame explicitly setting document.domain was allowed.";
+    cannotAccessFrame(url, iframeId, passMessage, failMessage);
+</script>
+</body>
+</html>
index a0a43302c74d17d775dc8a5f0e3faa5e745983b4..532719437fdba035e017be0bcc575fb73b8bc016 100644 (file)
@@ -1,3 +1,5 @@
+CONSOLE MESSAGE: line 1: Unsafe JavaScript attempt to access frame with URL http://127.0.0.1:8000/security/resources/cross-frame-iframe.html from frame with URL http://127.0.0.1:8000/security/cross-frame-access-parent-explicit-domain.html. Domains, protocols and ports must match.
+
 This test currently fails because we check the port and protocol even if document.domain is explicitly set (rdar://problem/5366437).
 
 
index 0a043174f35df621298f50dd1b2f2b9c1ad4fd9f..16ed038567d802c1db7958fe6e7e809d1935b1c8 100644 (file)
@@ -1,12 +1,12 @@
+CONSOLE MESSAGE: line 1: Unsafe JavaScript attempt to access frame with URL https://127.0.0.1:8443/security/resources/cross-frame-iframe-with-explicit-domain-set.html from frame with URL http://127.0.0.1:8000/security/cross-frame-access-protocol-explicit-domain.html. Domains, protocols and ports must match.
+
 This test currently fails because we check the port and protocol even if document.domain is explicitly set (rdar://problem/5366437).
 
 
-PASS: Cross frame access to https from http, after explicitly setting document.domain, was allowed!
+PASS: Cross frame access to https from http, after explicitly setting document.domain, was denied.
 
 
 --------
 Frame: 'aFrame'
 --------
-PASS: Cross frame access to https from http, after explicitly setting document.domain, was allowed!
-
 Inner iframe with explicit document.domain set.
index 0ddb3ba3dc5279ab7302afc7c8a270f81d8754b5..6dc5fc3626dff415ea4cb03a38df584fbf9a5340 100644 (file)
@@ -12,9 +12,9 @@
 
     var url = "https://127.0.0.1:8443/security/resources/cross-frame-iframe-with-explicit-domain-set.html";
     var iframeId ="aFrame";
-    var passMessage = "PASS: Cross frame access to https from http, after explicitly setting document.domain, was allowed!";
-    var failMessage = "Fail: Cross frame access to https from http, after explicitly setting document.domain, was denied.";
-    canAccessFrame(url, iframeId, passMessage, failMessage);
+    var passMessage = "PASS: Cross frame access to https from http, after explicitly setting document.domain, was denied.";
+    var failMessage = "Fail: Cross frame access to https from http, after explicitly setting document.domain, was allowed.";
+    cannotAccessFrame(url, iframeId, passMessage, failMessage);
 </script>
 </body>
 </html>
index c3a8a759dde47cb26322e5ab1e76d39f34a43628..40dca49028cd0a76d7ac1fa2404361faf52cfa5c 100644 (file)
@@ -1,3 +1,86 @@
+2007-12-20  Adam Barth  <hk9565@gmail.com>
+
+        Reviewed and landed by Sam Weinig.
+
+        http://bugs.webkit.org/show_bug.cgi?id=15313
+        <rdar://problem/5514516>
+
+        The same-origin check was incorrect in two cases (both fixed in this
+        patch):
+
+        A) If both the source and the target have set their document.domain
+           to the same value, the protocol must also match in order for
+           access to be allowed.  Without this requirement, the browser is
+           vulnerable to the following attack:
+
+           1) Suppose there is an HTTPS site (www.example.com) that sets
+              document.domain = "example.com".
+           2) A network attacker redirects the browser to http://www.example.com/
+              a) injects script to set document.domain = "example.com", and
+              b) opens a window to https://www.example.com/
+           3) Now the network attacker can inject script into the HTTPS page,
+              stealing cookies and issuing banking transactions.
+
+        B) If only one of the source and target has set document.domain, then
+           access should be denied.  With this behavior, the browser is
+           vulnerable to the following attack:
+
+           1) Suppose http://foo.example.com/ opens an iframe to
+              http://foo.example.com/frame.html that
+              a) sets document.domain = "example.com", and
+              b) opens an iframe to http://bar.example.com/
+              This is a common usage of document.domain for cross-domain
+              communication, see for example:
+                http://www.collinjackson.com/research/papers/fp801-jackson.pdf
+           2) The inner-most iframe, which is from bar.example.com, sets
+              document.domain = "example.com".
+           3) Now the inner-most iframe can inject script into the middle
+              iframe (say via document.write).  This bar.example.com script
+              now has access to the outer-most frame (from foo.example.com).
+
+        Both these changes cause WebKit to match the behavior of Firefox 2 and
+        IE6 in these cases.  This patch includes regression tests for both
+        issues.
+
+        Internet Explorer 7 and Opera 9 are more strict in that they require
+        the port numbers to match when both pages have document.domain set.
+        Opera 9 allows access when only one page has set document.domain, but
+        this is a security vulnerability.
+
+        Tests: http/tests/security/cross-frame-access-child-explicit-domain.html
+               http/tests/security/cross-frame-access-parent-explicit-domain.html
+
+        * bindings/js/kjs_window.cpp:
+        (KJS::createWindow):
+        (KJS::Window::allowsAccessFrom):
+        * dom/Document.cpp:
+        (WebCore::Document::domain):
+        (WebCore::Document::setDomain):
+        (WebCore::Document::initSecurityOrigin):
+        * dom/Document.h:
+        (WebCore::Document::securityOrigin):
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::begin):
+        (WebCore::FrameLoader::checkCallImplicitClose):
+        (WebCore::FrameLoader::shouldAllowNavigation):
+        * platform/SecurityOrigin.cpp:
+        (WebCore::SecurityOrigin::setForURL):
+        (WebCore::SecurityOrigin::createForFrame):
+        (WebCore::SecurityOrigin::canAccess):
+        * platform/SecurityOrigin.h:
+        (WebCore::SecurityOrigin::domain):
+        * storage/Database.cpp:
+        (WebCore::Database::openDatabase):
+        (WebCore::Database::Database):
+        (WebCore::Database::securityOriginData):
+        * storage/Database.h:
+        (WebCore::Database::databaseDebugName):
+        * storage/DatabaseTracker.cpp:
+        (WebCore::DatabaseTracker::canEstablishDatabase):
+        * storage/SQLTransaction.cpp:
+        (WebCore::SQLTransaction::postflightAndCommit):
+        (WebCore::SQLTransaction::cleanupAfterTransactionErrorCallback):
+
 2007-12-20  Rodney Dawes  <dobey@wayofthemonkey.com>
 
         Reviewed by Darin Adler.
index ecc0ec936d76aaf89e9e9a87080a1c2d154744bb..d2c24f4cb95f33a8c5b6360df77b8821851beee6 100644 (file)
@@ -355,10 +355,8 @@ static Frame* createWindow(ExecState* exec, Frame* openerFrame, const String& ur
 
         if (created) {
             newFrame->loader()->changeLocation(KURL(completedURL.deprecatedString()), activeFrame->loader()->outgoingReferrer(), false, userGesture);
-            if (Document* oldDoc = openerFrame->document()) {
-                newFrame->document()->setDomainInternal(oldDoc->domain());
+            if (Document* oldDoc = openerFrame->document())
                 newFrame->document()->setBaseURL(oldDoc->baseURL());
-            }
         } else if (!url.isEmpty())
             newFrame->loader()->scheduleLocationChange(completedURL, activeFrame->loader()->outgoingReferrer(), false, userGesture);
     }
@@ -819,10 +817,10 @@ bool Window::allowsAccessFrom(const JSGlobalObject* other) const
 
     WebCore::Document* originDocument = originFrame->document();
 
-    const SecurityOrigin& originSecurityOrigin = originDocument->securityOrigin();
-    const SecurityOrigin& targetSecurityOrigin = targetDocument->securityOrigin();
+    const SecurityOrigin* originSecurityOrigin = originDocument->securityOrigin();
+    const SecurityOrigin* targetSecurityOrigin = targetDocument->securityOrigin();
 
-    if (originSecurityOrigin.canAccess(targetSecurityOrigin))
+    if (originSecurityOrigin->canAccess(targetSecurityOrigin))
         return true;
 
     if (!targetFrame->settings()->privateBrowsingEnabled()) {
index 1cc753cf87e683933c45dcac7dd4ed136cae285a..f476792f36c116b353d103c759880edf177ebae8 100644 (file)
@@ -2594,18 +2594,11 @@ String Document::referrer() const
 
 String Document::domain() const
 {
-    if (m_domain.isEmpty()) // not set yet (we set it on demand to save time and space)
-        m_domain = KURL(url()).host(); // Initially set to the host
-    return m_domain;
+    return m_securityOrigin->domain();
 }
 
 void Document::setDomain(const String& newDomain)
 {
-    // Not set yet (we set it on demand to save time and space)
-    // Initially set to the host
-    if (m_domain.isEmpty())
-        m_domain = KURL(url()).host();
-
     // Both NS and IE specify that changing the domain is only allowed when
     // the new domain is a suffix of the old domain.
 
@@ -2617,35 +2610,29 @@ void Document::setDomain(const String& newDomain)
     // assigns its current domain using document.domain, the page will
     // allow other pages loaded on different ports in the same domain that
     // have also assigned to access this page.
-    if (equalIgnoringCase(m_domain, newDomain)) {
-        m_securityOrigin.setDomainFromDOM(newDomain);
+    if (equalIgnoringCase(domain(), newDomain)) {
+        m_securityOrigin->setDomainFromDOM(newDomain);
         return;
     }
 
-    int oldLength = m_domain.length();
+    int oldLength = domain().length();
     int newLength = newDomain.length();
-    // e.g. newDomain = webkit.org (10) and m_domain = www.webkit.org (14)
+    // e.g. newDomain = webkit.org (10) and domain() = www.webkit.org (14)
     if (newLength >= oldLength)
         return;
 
-    String test = m_domain.copy();
+    String test = domain().copy();
     // Check that it's a subdomain, not e.g. "ebkit.org"
     if (test[oldLength - newLength - 1] != '.')
         return;
 
-    // Now test is "webkit.org" from m_domain
+    // Now test is "webkit.org" from domain()
     // and we check that it's the same thing as newDomain
     test.remove(0, oldLength - newLength);
     if (test != newDomain)
         return;
 
-    m_domain = newDomain;
-    m_securityOrigin.setDomainFromDOM(newDomain);
-}
-
-void Document::setDomainInternal(const String& newDomain)
-{
-    m_domain = newDomain;
+    m_securityOrigin->setDomainFromDOM(newDomain);
 }
 
 String Document::lastModified() const
@@ -3726,9 +3713,7 @@ bool Document::useSecureKeyboardEntryWhenActive() const
 
 void Document::initSecurityOrigin()
 {
-    if (!m_frame)
-        return;
-    m_securityOrigin.setForFrame(m_frame);
+    m_securityOrigin = SecurityOrigin::createForFrame(m_frame);
 }
 
 void Document::updateFocusAppearanceSoon()
index 645c1474f63843eba4b5ecd25adac67efc18ebb1..1ede43e0290a89fc7de546e93e142e599f71cc02 100644 (file)
@@ -553,7 +553,6 @@ public:
 
     String domain() const;
     void setDomain(const String& newDomain);
-    void setDomainInternal(const String& newDomain);
 
     String lastModified() const;
 
@@ -848,7 +847,7 @@ public:
 #endif
 
     void initSecurityOrigin();
-    const SecurityOrigin& securityOrigin() const { return m_securityOrigin; }
+    SecurityOrigin* securityOrigin() const { return m_securityOrigin.get(); }
 
     bool processingLoadEvent() const { return m_processingLoadEvent; }
 
@@ -866,9 +865,7 @@ private:
     void imageLoadEventTimerFired(Timer<Document>*);
     void updateFocusAppearanceTimerFired(Timer<Document>*);
 
-    mutable String m_domain;
-
-    SecurityOrigin m_securityOrigin;
+    RefPtr<SecurityOrigin> m_securityOrigin;
 
     RenderObject* m_savedRenderer;
     int m_secureForms;
index 6d02b32e71d8f8709e22c41e582c2469bd01a545..159cf2578cbe53337f901a597c05e000f81b6152 100644 (file)
@@ -887,7 +887,7 @@ void FrameLoader::begin()
 
 void FrameLoader::begin(const KURL& url, bool dispatch)
 {
-    bool resetScripting = !(m_isDisplayingInitialEmptyDocument && m_frame->document() && m_frame->document()->securityOrigin().isSecureTransitionTo(url));
+    bool resetScripting = !(m_isDisplayingInitialEmptyDocument && m_frame->document() && m_frame->document()->securityOrigin()->isSecureTransitionTo(url));
     clear(resetScripting, resetScripting);
     if (dispatch)
         dispatchWindowObjectAvailable();
@@ -1302,16 +1302,6 @@ void FrameLoader::checkCallImplicitClose()
         if (!child->loader()->m_isComplete) // still got a frame running -> too early
             return;
 
-    // All frames completed -> set their domain to the frameset's domain
-    // This must only be done when loading the frameset initially (#22039),
-    // not when following a link in a frame (#44162).
-    if (m_frame->document()) {
-        String domain = m_frame->document()->domain();
-        for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
-            if (child->document())
-                child->document()->setDomainInternal(domain);
-    }
-
     m_didCallImplicitClose = true;
     m_wasUnloadEventEmitted = false;
     if (m_frame->document())
@@ -2351,14 +2341,14 @@ bool FrameLoader::shouldAllowNavigation(Frame* targetFrame) const
 
     Document* activeDocument = m_frame->document();
     ASSERT(activeDocument);
-    const SecurityOrigin& activeSecurityOrigin = activeDocument->securityOrigin();
+    const SecurityOrigin* activeSecurityOrigin = activeDocument->securityOrigin();
     for (Frame* ancestorFrame = targetFrame; ancestorFrame; ancestorFrame = ancestorFrame->tree()->parent()) {
         Document* ancestorDocument = ancestorFrame->document();
         if (!ancestorDocument)
             return true;
 
-        const SecurityOrigin& ancestorSecurityOrigin = ancestorDocument->securityOrigin();
-        if (activeSecurityOrigin.canAccess(ancestorSecurityOrigin))
+        const SecurityOrigin* ancestorSecurityOrigin = ancestorDocument->securityOrigin();
+        if (activeSecurityOrigin->canAccess(ancestorSecurityOrigin))
             return true;
     }
 
index 0a57936a3242e1c1983c03199e0ec81fa2e1b191..8535c162b575a748acb237bfc5f59ec779c77e7b 100644 (file)
@@ -62,44 +62,58 @@ bool SecurityOrigin::isEmpty() const
     return m_protocol.isEmpty();
 }
 
-void SecurityOrigin::setForFrame(Frame* frame)
+void SecurityOrigin::setForURL(const KURL& url)
 {
     clear();
 
+    if (url.isEmpty())
+      return;
+
+    m_protocol = url.protocol().lower();
+    m_host = url.host().lower();
+    m_port = url.port();
+
+    if (m_port)
+        m_portSet = true;
+
+    // data: URLs are not allowed access to anything other than themselves.
+    if (m_protocol == "data")
+        m_noAccess = true;
+}
+
+PassRefPtr<SecurityOrigin> SecurityOrigin::createForFrame(Frame* frame)
+{
+    RefPtr<SecurityOrigin> origin = new SecurityOrigin();
+
+    if (!frame)
+        return origin;
+
     FrameLoader* loader = frame->loader();
     const KURL& securityPolicyURL = loader->url();
 
-    if (!securityPolicyURL.isEmpty()) {
-        m_protocol = securityPolicyURL.protocol().lower();
-        m_host = securityPolicyURL.host().lower();
-        m_port = securityPolicyURL.port();
-        if (m_port)
-            m_portSet = true;
-
-        // data: URLs are not allowed access to anything other than themselves.
-        if (m_protocol == "data") {
-            m_noAccess = true;
-            return;
-        }
+    origin->setForURL(securityPolicyURL);
 
-        // Only in the case of about:blank or javascript: URLs (which create documents using the "about" 
-        // protocol) do we want to use the parent or openers URL as the origin.
-        if (m_protocol != "about")
-            return;
-    }
+    if (!origin->isEmpty() && origin->m_protocol != "about")
+        return origin;
+
+    // In the case of about:blank or javascript: URLs (which create 
+    // documents using the "about" protocol) do we want to use the
+    // parent or openers origin.
 
     Frame* openerFrame = frame->tree()->parent();
     if (!openerFrame) {
         openerFrame = loader->opener();
         if (!openerFrame)
-            return;
+            return origin;
     }
 
     Document* openerDocument = openerFrame->document();
     if (!openerDocument)
-        return;
+        return origin;
 
-    *this = openerDocument->securityOrigin();
+    // We alias the SecurityOrigins to match Firefox, see Bug 15313
+    // http://bugs.webkit.org/show_bug.cgi?id=15313
+    return openerDocument->securityOrigin();
 }
 
 void SecurityOrigin::setDomainFromDOM(const String& newDomain)
@@ -108,18 +122,46 @@ void SecurityOrigin::setDomainFromDOM(const String& newDomain)
     m_host = newDomain.lower();
 }
 
-bool SecurityOrigin::canAccess(const SecurityOrigin& other) const
+bool SecurityOrigin::canAccess(const SecurityOrigin* other) const
 {
     if (FrameLoader::shouldTreatSchemeAsLocal(m_protocol))
         return true;
 
-    if (m_noAccess || other.m_noAccess)
+    if (m_noAccess || other->m_noAccess)
         return false;
 
-    if (m_domainWasSetInDOM && other.m_domainWasSetInDOM && m_host == other.m_host)
-        return true;
-    return m_host == other.m_host && m_protocol == other.m_protocol && m_port == other.m_port;
+    // Here are two cases where we should permit access:
+    //
+    // 1) Neither document has set document.domain.  In this case, we insist
+    //    that the scheme, host, and port of the URLs match.
+    //
+    // 2) Both documents have set document.domain.  In this case, we insist
+    //    that the documents have set document.domain to the same value and
+    //    that the scheme of the URLs match.
+    //
+    // This matches the behavior of Firefox 2 and Internet Explorer 6.
+    //
+    // Internet Explorer 7 and Opera 9 are more strict in that they require
+    // the port numbers to match when both pages have document.domain set.
+    //
+    // FIXME: Evaluate whether we can tighten this policy to require matched
+    //        port numbers.
+    //
+    // Opera 9 allows access when only one page has set document.domain, but
+    // this is a security vulnerability.
+
+    if (m_protocol == other->m_protocol) {
+        if (!m_domainWasSetInDOM && !other->m_domainWasSetInDOM) {
+            if (m_host == other->m_host && m_port == other->m_port)
+                return true;
+        }
+        if (m_domainWasSetInDOM && other->m_domainWasSetInDOM) {
+            if (m_host == other->m_host)
+                return true;
+        }
+    }
+
+    return false;
 }
 
 bool SecurityOrigin::isSecureTransitionTo(const KURL& url) const
index e3d17b02071a69376238b5bd9d3e69070ae53011..1892444371d6801fa2099c083828acebc879b8f8 100644 (file)
@@ -29,6 +29,9 @@
 #ifndef SecurityOrigin_h
 #define SecurityOrigin_h
 
+#include <wtf/RefCounted.h>
+#include <wtf/PassRefPtr.h>
+
 #include "PlatformString.h"
 
 namespace WebCore {
@@ -37,14 +40,14 @@ namespace WebCore {
     class KURL;
     class SecurityOriginData;
     
-    class SecurityOrigin {
+    class SecurityOrigin : public RefCounted<SecurityOrigin> {
     public:
-        SecurityOrigin();
+        static PassRefPtr<SecurityOrigin> createForFrame(Frame*);
 
-        void setForFrame(Frame*);
         void setDomainFromDOM(const String& newDomain);
+        String domain() const { return m_host; }
 
-        bool canAccess(const SecurityOrigin&) const;
+        bool canAccess(const SecurityOrigin*) const;
         bool isSecureTransitionTo(const KURL&) const;
 
         String toString() const;
@@ -52,9 +55,12 @@ namespace WebCore {
         SecurityOriginData securityOriginData() const;
         
     private:
-        void clear();
+        SecurityOrigin();
         bool isEmpty() const;
 
+        void clear();
+        void setForURL(const KURL& url);
+
         String m_protocol;
         String m_host;
         unsigned short m_port;
index f03fd1dc17845aa2dd4c05007b515110e9da6320..c112492f81ecc21ad6892ae18819d1e8e812d4c7 100644 (file)
@@ -97,7 +97,7 @@ PassRefPtr<Database> Database::openDatabase(Document* document, const String& na
 {
     if (!DatabaseTracker::tracker().canEstablishDatabase(document, name, displayName, estimatedSize)) {
         // There should be an exception raised here in addition to returning a null Database object.  The question has been raised with the WHATWG
-        LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), document->securityOrigin().toString().ascii().data());
+        LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), document->securityOrigin()->toString().ascii().data());
         return 0;
     }
     
@@ -108,7 +108,7 @@ PassRefPtr<Database> Database::openDatabase(Document* document, const String& na
        return 0;
     }
     
-    DatabaseTracker::tracker().setDatabaseDetails(document->securityOrigin().securityOriginData(), name, displayName, estimatedSize);
+    DatabaseTracker::tracker().setDatabaseDetails(document->securityOrigin()->securityOriginData(), name, displayName, estimatedSize);
 
     if (Page* page = document->frame()->page())
         page->inspectorController()->didOpenDatabase(database.get(), document->domain(), name, expectedVersion);
@@ -131,7 +131,7 @@ Database::Database(Document* document, const String& name, const String& expecte
 
     initializeThreading();
 
-    m_guid = guidForOriginAndName(m_securityOrigin.toString(), name);
+    m_guid = guidForOriginAndName(m_securityOrigin->toString(), name);
 
     {
         MutexLocker locker(guidMutex());
@@ -148,7 +148,7 @@ Database::Database(Document* document, const String& name, const String& expecte
     m_databaseThread = document->databaseThread();
     ASSERT(m_databaseThread);
 
-    m_filename = DatabaseTracker::tracker().fullPathForDatabase(m_securityOrigin.securityOriginData(), m_name);
+    m_filename = DatabaseTracker::tracker().fullPathForDatabase(m_securityOrigin->securityOriginData(), m_name);
 }
 
 Database::~Database()
@@ -550,7 +550,7 @@ void Database::setExpectedVersion(const String& version)
 SecurityOriginData Database::securityOriginData() const
 {
     // Return a deep copy for ref counting thread safety
-    return m_securityOrigin.securityOriginData().copy();
+    return m_securityOrigin->securityOriginData().copy();
 }
 
 String Database::stringIdentifier() const
index b436379b837b85df804639e260687947b5b4ad3d..f6b0f6978c162c6972fd364882abf4de9f259682 100644 (file)
@@ -44,6 +44,7 @@
 #include <wtf/HashSet.h>
 #include <wtf/OwnPtr.h>
 #include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
 #include <wtf/Deque.h>
 
 namespace WebCore {
@@ -125,7 +126,7 @@ private:
     void deliverPendingCallback();
 
     Document* m_document;
-    SecurityOrigin m_securityOrigin;
+    RefPtr<SecurityOrigin> m_securityOrigin;
     String m_name;
     int m_guid;
     String m_expectedVersion;
@@ -140,7 +141,7 @@ private:
     RefPtr<SQLTransaction> m_transactionPendingCallback;
 
 #ifndef NDEBUG
-    String databaseDebugName() const { return m_securityOrigin.toString() + "::" + m_name; }
+    String databaseDebugName() const { return m_securityOrigin->toString() + "::" + m_name; }
 #endif
 
     static Mutex& globalCallbackMutex();
index 399c7832071bad04c4cdbbb81b25d1cdf88fd330..cdecc2e4ff2673562ce62be41ab11a30713e828c 100644 (file)
@@ -125,7 +125,7 @@ void DatabaseTracker::openTrackerDatabase()
 
 bool DatabaseTracker::canEstablishDatabase(Document* document, const String& name, const String& displayName, unsigned long estimatedSize)
 {
-    SecurityOriginData originData = document->securityOrigin().securityOriginData();
+    SecurityOriginData originData = document->securityOrigin()->securityOriginData();
     
     // If this origin has no databases yet, establish an entry in the tracker database with the default quota
     if (!hasEntryForOrigin(originData))
index f5ac3e22e97a631415502bd0e3c7786280ffc6bf..c1a6bd11a9c6b44683828be240cf42c70f0ed935 100644 (file)
@@ -334,7 +334,7 @@ void SQLTransaction::postflightAndCommit()
     
     // The commit was successful, notify the delegates if the transaction modified this database
     if (m_modifiedDatabase)
-        DatabaseTracker::tracker().scheduleNotifyDatabaseChanged(m_database->m_securityOrigin.securityOriginData(), m_database->m_name);
+        DatabaseTracker::tracker().scheduleNotifyDatabaseChanged(m_database->m_securityOrigin->securityOriginData(), m_database->m_name);
     
     // Transaction Step 10 - End transaction steps
     // There is no next step
@@ -391,7 +391,7 @@ void SQLTransaction::cleanupAfterTransactionErrorCallback()
             m_sqliteTransaction->rollback();
         } else if (m_modifiedDatabase) {
             // But if the commit was successful, notify the delegates if the transaction modified this database
-            DatabaseTracker::tracker().scheduleNotifyDatabaseChanged(m_database->m_securityOrigin.securityOriginData(), m_database->m_name);
+            DatabaseTracker::tracker().scheduleNotifyDatabaseChanged(m_database->m_securityOrigin->securityOriginData(), m_database->m_name);
         }
         
         m_sqliteTransaction.clear();