From 79143ccfc158ec4fffc49eee600d600edb342b16 Mon Sep 17 00:00:00 2001 From: Konstantin Tokarev Date: Thu, 11 Jan 2018 05:56:18 +0300 Subject: Import WebKit commit a8b574fb3cd509a2d3f2a1568ad0a66d1bf0f6e8 Change-Id: I66add69e6d08b74111ec8e7e4401e4d813501206 Reviewed-by: Konstantin Tokarev --- Source/WebCore/CMakeLists.txt | 1 + Source/WebCore/PlatformQt.cmake | 2 + Source/WebCore/crypto/CryptoDigest.h | 57 ----------- .../crypto/algorithms/CryptoAlgorithmSHA1.cpp | 2 +- .../crypto/algorithms/CryptoAlgorithmSHA224.cpp | 2 +- .../crypto/algorithms/CryptoAlgorithmSHA256.cpp | 2 +- .../crypto/algorithms/CryptoAlgorithmSHA384.cpp | 2 +- .../crypto/algorithms/CryptoAlgorithmSHA512.cpp | 2 +- Source/WebCore/dom/InlineStyleSheetOwner.cpp | 6 +- Source/WebCore/dom/ScriptElement.cpp | 12 ++- Source/WebCore/dom/StyledElement.cpp | 2 +- Source/WebCore/html/HTMLLinkElement.cpp | 8 ++ Source/WebCore/html/HTMLScriptElement.idl | 2 +- Source/WebCore/html/HTMLStyleElement.idl | 2 + Source/WebCore/page/csp/ContentSecurityPolicy.cpp | 101 ++++++++++++++++++- Source/WebCore/page/csp/ContentSecurityPolicy.h | 22 ++++- .../csp/ContentSecurityPolicyDirectiveList.cpp | 94 ++++++++++++------ .../page/csp/ContentSecurityPolicyDirectiveList.h | 10 +- .../WebCore/page/csp/ContentSecurityPolicyHash.h | 69 +++++++++++++ .../page/csp/ContentSecurityPolicySourceList.cpp | 110 +++++++++++++++++++++ .../page/csp/ContentSecurityPolicySourceList.h | 20 +++- .../ContentSecurityPolicySourceListDirective.cpp | 10 ++ .../csp/ContentSecurityPolicySourceListDirective.h | 4 + Source/WebCore/platform/crypto/CryptoDigest.h | 60 +++++++++++ .../WebCore/platform/crypto/qt/CryptoDigestQt.cpp | 93 +++++++++++++++++ Source/WebKit/PlatformQt.cmake | 2 +- Source/WebKit2/PlatformQt.cmake | 12 ++- .../WebKit2/Shared/qt/ProcessExecutablePathQt.cpp | 7 ++ Source/WebKit2/UIProcess/API/qt/qquickwebview.cpp | 1 + .../UIProcess/Launcher/qt/ProcessLauncherQt.cpp | 6 ++ Source/cmake/OptionsQt.cmake | 55 ++++++----- Source/cmake/WebKitMacros.cmake | 2 + Tools/qmake/projects/run_cmake.pro | 5 +- 33 files changed, 646 insertions(+), 139 deletions(-) delete mode 100644 Source/WebCore/crypto/CryptoDigest.h create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicyHash.h create mode 100644 Source/WebCore/platform/crypto/CryptoDigest.h create mode 100644 Source/WebCore/platform/crypto/qt/CryptoDigestQt.cpp diff --git a/Source/WebCore/CMakeLists.txt b/Source/WebCore/CMakeLists.txt index 02f9d3bf3..e9fc4e54e 100644 --- a/Source/WebCore/CMakeLists.txt +++ b/Source/WebCore/CMakeLists.txt @@ -68,6 +68,7 @@ set(WebCore_INCLUDE_DIRECTORIES "${WEBCORE_DIR}/platform" "${WEBCORE_DIR}/platform/animation" "${WEBCORE_DIR}/platform/audio" + "${WEBCORE_DIR}/platform/crypto" "${WEBCORE_DIR}/platform/graphics" "${WEBCORE_DIR}/platform/graphics/cpu/arm" "${WEBCORE_DIR}/platform/graphics/cpu/arm/filters" diff --git a/Source/WebCore/PlatformQt.cmake b/Source/WebCore/PlatformQt.cmake index d237a83a6..49c76a8de 100644 --- a/Source/WebCore/PlatformQt.cmake +++ b/Source/WebCore/PlatformQt.cmake @@ -73,6 +73,8 @@ list(APPEND WebCore_SOURCES platform/audio/qt/AudioBusQt.cpp + platform/crypto/qt/CryptoDigestQt.cpp + platform/graphics/ImageSource.cpp platform/graphics/PlatformDisplay.cpp platform/graphics/WOFFFileFormat.cpp diff --git a/Source/WebCore/crypto/CryptoDigest.h b/Source/WebCore/crypto/CryptoDigest.h deleted file mode 100644 index 353b13e82..000000000 --- a/Source/WebCore/crypto/CryptoDigest.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef CryptoDigest_h -#define CryptoDigest_h - -#include "CryptoAlgorithmIdentifier.h" -#include -#include - -#if ENABLE(SUBTLE_CRYPTO) - -namespace WebCore { - -struct CryptoDigestContext; - -class CryptoDigest { - WTF_MAKE_NONCOPYABLE(CryptoDigest); -public: - static std::unique_ptr create(CryptoAlgorithmIdentifier); - ~CryptoDigest(); - - void addBytes(const void* input, size_t length); - Vector computeHash(); - -private: - CryptoDigest(); - - std::unique_ptr m_context; -}; - -} // namespace WebCore - -#endif // ENABLE(SUBTLE_CRYPTO) -#endif // CryptoDigest_h diff --git a/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA1.cpp b/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA1.cpp index bc94aa3de..54dcd03fb 100644 --- a/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA1.cpp +++ b/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA1.cpp @@ -54,7 +54,7 @@ CryptoAlgorithmIdentifier CryptoAlgorithmSHA1::identifier() const void CryptoAlgorithmSHA1::digest(const CryptoAlgorithmParameters&, const CryptoOperationData& data, VectorCallback&& callback, VoidCallback&& failureCallback, ExceptionCode&) { - std::unique_ptr digest = CryptoDigest::create(CryptoAlgorithmIdentifier::SHA_1); + std::unique_ptr digest = CryptoDigest::create(CryptoDigest::Algorithm::SHA_1); if (!digest) { failureCallback(); return; diff --git a/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA224.cpp b/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA224.cpp index 62cc5a8c2..3878227d9 100644 --- a/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA224.cpp +++ b/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA224.cpp @@ -54,7 +54,7 @@ CryptoAlgorithmIdentifier CryptoAlgorithmSHA224::identifier() const void CryptoAlgorithmSHA224::digest(const CryptoAlgorithmParameters&, const CryptoOperationData& data, VectorCallback&& callback, VoidCallback&& failureCallback, ExceptionCode&) { - std::unique_ptr digest = CryptoDigest::create(CryptoAlgorithmIdentifier::SHA_224); + std::unique_ptr digest = CryptoDigest::create(CryptoDigest::Algorithm::SHA_224); if (!digest) { failureCallback(); return; diff --git a/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA256.cpp b/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA256.cpp index 27fe8aa0a..0acaf0905 100644 --- a/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA256.cpp +++ b/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA256.cpp @@ -54,7 +54,7 @@ CryptoAlgorithmIdentifier CryptoAlgorithmSHA256::identifier() const void CryptoAlgorithmSHA256::digest(const CryptoAlgorithmParameters&, const CryptoOperationData& data, VectorCallback&& callback, VoidCallback&& failureCallback, ExceptionCode&) { - std::unique_ptr digest = CryptoDigest::create(CryptoAlgorithmIdentifier::SHA_256); + std::unique_ptr digest = CryptoDigest::create(CryptoDigest::Algorithm::SHA_256); if (!digest) { failureCallback(); return; diff --git a/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA384.cpp b/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA384.cpp index 6eba234f3..b1bfbe157 100644 --- a/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA384.cpp +++ b/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA384.cpp @@ -54,7 +54,7 @@ CryptoAlgorithmIdentifier CryptoAlgorithmSHA384::identifier() const void CryptoAlgorithmSHA384::digest(const CryptoAlgorithmParameters&, const CryptoOperationData& data, VectorCallback&& callback, VoidCallback&& failureCallback, ExceptionCode&) { - std::unique_ptr digest = CryptoDigest::create(CryptoAlgorithmIdentifier::SHA_384); + std::unique_ptr digest = CryptoDigest::create(CryptoDigest::Algorithm::SHA_384); if (!digest) { failureCallback(); return; diff --git a/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA512.cpp b/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA512.cpp index 87d62d445..7ddf1d4d2 100644 --- a/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA512.cpp +++ b/Source/WebCore/crypto/algorithms/CryptoAlgorithmSHA512.cpp @@ -54,7 +54,7 @@ CryptoAlgorithmIdentifier CryptoAlgorithmSHA512::identifier() const void CryptoAlgorithmSHA512::digest(const CryptoAlgorithmParameters&, const CryptoOperationData& data, VectorCallback&& callback, VoidCallback&& failureCallback, ExceptionCode&) { - std::unique_ptr digest = CryptoDigest::create(CryptoAlgorithmIdentifier::SHA_512); + std::unique_ptr digest = CryptoDigest::create(CryptoDigest::Algorithm::SHA_512); if (!digest) { failureCallback(); return; diff --git a/Source/WebCore/dom/InlineStyleSheetOwner.cpp b/Source/WebCore/dom/InlineStyleSheetOwner.cpp index 83574842c..a487f6041 100644 --- a/Source/WebCore/dom/InlineStyleSheetOwner.cpp +++ b/Source/WebCore/dom/InlineStyleSheetOwner.cpp @@ -137,7 +137,11 @@ void InlineStyleSheetOwner::createSheet(Element& element, const String& text) if (!isValidCSSContentType(element, m_contentType)) return; - if (!document.contentSecurityPolicy()->allowInlineStyle(document.url(), m_startTextPosition.m_line, element.isInUserAgentShadowTree())) + + ASSERT(document.contentSecurityPolicy()); + const ContentSecurityPolicy& contentSecurityPolicy = *document.contentSecurityPolicy(); + bool hasKnownNonce = contentSecurityPolicy.allowStyleWithNonce(element.fastGetAttribute(HTMLNames::nonceAttr), element.isInUserAgentShadowTree()); + if (!contentSecurityPolicy.allowInlineStyle(document.url(), m_startTextPosition.m_line, text, hasKnownNonce)) return; RefPtr mediaQueries; diff --git a/Source/WebCore/dom/ScriptElement.cpp b/Source/WebCore/dom/ScriptElement.cpp index 521028195..f9c70e326 100644 --- a/Source/WebCore/dom/ScriptElement.cpp +++ b/Source/WebCore/dom/ScriptElement.cpp @@ -258,8 +258,9 @@ bool ScriptElement::requestScript(const String& sourceUrl) ASSERT(!m_cachedScript); if (!stripLeadingAndTrailingHTMLSpaces(sourceUrl).isEmpty()) { + bool hasKnownNonce = m_element.document().contentSecurityPolicy()->allowScriptWithNonce(m_element.fastGetAttribute(HTMLNames::nonceAttr), m_element.isInUserAgentShadowTree()); ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions(); - options.setContentSecurityPolicyImposition(m_element.isInUserAgentShadowTree() ? ContentSecurityPolicyImposition::SkipPolicyCheck : ContentSecurityPolicyImposition::DoPolicyCheck); + options.setContentSecurityPolicyImposition(hasKnownNonce ? ContentSecurityPolicyImposition::SkipPolicyCheck : ContentSecurityPolicyImposition::DoPolicyCheck); CachedResourceRequest request(ResourceRequest(m_element.document().completeURL(sourceUrl)), options); @@ -293,8 +294,13 @@ void ScriptElement::executeScript(const ScriptSourceCode& sourceCode) if (sourceCode.isEmpty()) return; - if (!m_isExternalScript && !m_element.document().contentSecurityPolicy()->allowInlineScript(m_element.document().url(), m_startLineNumber, m_element.isInUserAgentShadowTree())) - return; + if (!m_isExternalScript) { + ASSERT(m_element.document().contentSecurityPolicy()); + const ContentSecurityPolicy& contentSecurityPolicy = *m_element.document().contentSecurityPolicy(); + bool hasKnownNonce = contentSecurityPolicy.allowScriptWithNonce(m_element.fastGetAttribute(HTMLNames::nonceAttr), m_element.isInUserAgentShadowTree()); + if (!contentSecurityPolicy.allowInlineScript(m_element.document().url(), m_startLineNumber, sourceCode.source().toStringWithoutCopying(), hasKnownNonce)) + return; + } #if ENABLE(NOSNIFF) if (m_isExternalScript && m_cachedScript && !m_cachedScript->mimeTypeAllowedByNosniff()) { diff --git a/Source/WebCore/dom/StyledElement.cpp b/Source/WebCore/dom/StyledElement.cpp index e5d06633d..e7c328a44 100644 --- a/Source/WebCore/dom/StyledElement.cpp +++ b/Source/WebCore/dom/StyledElement.cpp @@ -202,7 +202,7 @@ void StyledElement::styleAttributeChanged(const AtomicString& newStyleString, At if (PropertySetCSSStyleDeclaration* cssomWrapper = inlineStyleCSSOMWrapper()) cssomWrapper->clearParentElement(); ensureUniqueElementData().m_inlineStyle = nullptr; - } else if (reason == ModifiedByCloning || document().contentSecurityPolicy()->allowInlineStyle(document().url(), startLineNumber, isInUserAgentShadowTree())) + } else if (reason == ModifiedByCloning || document().contentSecurityPolicy()->allowInlineStyle(document().url(), startLineNumber, String(), isInUserAgentShadowTree())) setInlineStyleFromString(newStyleString); elementData()->setStyleAttributeIsDirty(false); diff --git a/Source/WebCore/html/HTMLLinkElement.cpp b/Source/WebCore/html/HTMLLinkElement.cpp index 49c15ce12..4a73e530d 100644 --- a/Source/WebCore/html/HTMLLinkElement.cpp +++ b/Source/WebCore/html/HTMLLinkElement.cpp @@ -32,6 +32,7 @@ #include "CachedResource.h" #include "CachedResourceLoader.h" #include "CachedResourceRequest.h" +#include "ContentSecurityPolicy.h" #include "Document.h" #include "Event.h" #include "EventSender.h" @@ -245,6 +246,13 @@ void HTMLLinkElement::process() priority = ResourceLoadPriority::VeryLow; CachedResourceRequest request(ResourceRequest(document().completeURL(url)), charset, priority); request.setInitiator(this); + + if (document().contentSecurityPolicy()->allowStyleWithNonce(fastGetAttribute(HTMLNames::nonceAttr))) { + ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions(); + options.setContentSecurityPolicyImposition(ContentSecurityPolicyImposition::SkipPolicyCheck); + request.setOptions(options); + } + m_cachedSheet = document().cachedResourceLoader().requestCSSStyleSheet(request); if (m_cachedSheet) diff --git a/Source/WebCore/html/HTMLScriptElement.idl b/Source/WebCore/html/HTMLScriptElement.idl index 1365d12bf..ff0b30aac 100644 --- a/Source/WebCore/html/HTMLScriptElement.idl +++ b/Source/WebCore/html/HTMLScriptElement.idl @@ -27,5 +27,5 @@ interface HTMLScriptElement : HTMLElement { [Reflect, URL] attribute DOMString src; [Reflect] attribute DOMString type; [Reflect] attribute DOMString crossOrigin; - [Reflect, Conditional=CSP_NEXT] attribute DOMString nonce; + [Reflect] attribute DOMString nonce; }; diff --git a/Source/WebCore/html/HTMLStyleElement.idl b/Source/WebCore/html/HTMLStyleElement.idl index 7abf3839f..fd3f7c0d3 100644 --- a/Source/WebCore/html/HTMLStyleElement.idl +++ b/Source/WebCore/html/HTMLStyleElement.idl @@ -25,5 +25,7 @@ interface HTMLStyleElement : HTMLElement { // DOM Level 2 Style readonly attribute StyleSheet sheet; + + [Reflect] attribute DOMString nonce; }; diff --git a/Source/WebCore/page/csp/ContentSecurityPolicy.cpp b/Source/WebCore/page/csp/ContentSecurityPolicy.cpp index 19bf207fa..9e726d5fe 100644 --- a/Source/WebCore/page/csp/ContentSecurityPolicy.cpp +++ b/Source/WebCore/page/csp/ContentSecurityPolicy.cpp @@ -29,14 +29,17 @@ #include "ContentSecurityPolicyDirective.h" #include "ContentSecurityPolicyDirectiveList.h" +#include "ContentSecurityPolicyHash.h" #include "ContentSecurityPolicySource.h" #include "ContentSecurityPolicySourceList.h" +#include "CryptoDigest.h" #include "DOMStringList.h" #include "Document.h" #include "DocumentLoader.h" #include "FormData.h" #include "FormDataList.h" #include "Frame.h" +#include "HTMLParserIdioms.h" #include "InspectorInstrumentation.h" #include "JSMainThreadExecState.h" #include "ParsingUtilities.h" @@ -45,6 +48,7 @@ #include "SchemeRegistry.h" #include "SecurityOrigin.h" #include "SecurityPolicyViolationEvent.h" +#include "TextEncoding.h" #include #include #include @@ -193,6 +197,48 @@ bool isAllowedByAllWithContext(const CSPDirectiveListVector& policies, const Str return true; } +template +static bool isAllowedByAllWithNonce(const CSPDirectiveListVector& policies, const String& nonce) +{ + for (auto& policy : policies) { + if (!(policy.get()->*allowed)(nonce)) + return false; + } + return true; +} + +static CryptoDigest::Algorithm toCryptoDigestAlgorithm(ContentSecurityPolicyHashAlgorithm algorithm) +{ + switch (algorithm) { + case ContentSecurityPolicyHashAlgorithm::SHA_256: + return CryptoDigest::Algorithm::SHA_256; + case ContentSecurityPolicyHashAlgorithm::SHA_384: + return CryptoDigest::Algorithm::SHA_384; + case ContentSecurityPolicyHashAlgorithm::SHA_512: + return CryptoDigest::Algorithm::SHA_512; + } + ASSERT_NOT_REACHED(); + return CryptoDigest::Algorithm::SHA_512; +} + +template +bool isAllowedByAllWithHashFromContent(const CSPDirectiveListVector& policies, const String& content, const TextEncoding& encoding, OptionSet algorithms) +{ + // FIXME: Compute the digest with respect to the raw bytes received from the page. + // See . + CString contentCString = encoding.encode(content, EntitiesForUnencodables); + for (auto algorithm : algorithms) { + auto cryptoDigest = CryptoDigest::create(toCryptoDigestAlgorithm(algorithm)); + cryptoDigest->addBytes(contentCString.data(), contentCString.length()); + Vector digest = cryptoDigest->computeHash(); + for (auto& policy : policies) { + if ((policy.get()->*allowed)(std::make_pair(algorithm, digest))) + return true; + } + } + return false; +} + template bool isAllowedByAllWithURL(const CSPDirectiveListVector& policies, const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) { @@ -216,14 +262,61 @@ bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, c return overrideContentSecurityPolicy || isAllowedByAllWithContext<&ContentSecurityPolicyDirectiveList::allowInlineEventHandlers>(m_policies, contextURL, contextLine, reportingStatus); } -bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const +// FIXME: We should compute the document encoding once and cache it instead of computing it on each invocation. +const TextEncoding& ContentSecurityPolicy::documentEncoding() const { - return overrideContentSecurityPolicy || isAllowedByAllWithContext<&ContentSecurityPolicyDirectiveList::allowInlineScript>(m_policies, contextURL, contextLine, reportingStatus); + if (!is(m_scriptExecutionContext)) + return UTF8Encoding(); + Document& document = downcast(*m_scriptExecutionContext); + if (TextResourceDecoder* decoder = document.decoder()) + return decoder->encoding(); + return UTF8Encoding(); } -bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const +bool ContentSecurityPolicy::allowScriptWithNonce(const String& nonce, bool overrideContentSecurityPolicy) const { - return overrideContentSecurityPolicy || m_overrideInlineStyleAllowed || isAllowedByAllWithContext<&ContentSecurityPolicyDirectiveList::allowInlineStyle>(m_policies, contextURL, contextLine, reportingStatus); + if (overrideContentSecurityPolicy) + return true; + String strippedNonce = stripLeadingAndTrailingHTMLSpaces(nonce); + if (strippedNonce.isEmpty()) + return false; + if (isAllowedByAllWithNonce<&ContentSecurityPolicyDirectiveList::allowScriptWithNonce>(m_policies, strippedNonce)) + return true; + return false; +} + +bool ContentSecurityPolicy::allowStyleWithNonce(const String& nonce, bool overrideContentSecurityPolicy) const +{ + if (overrideContentSecurityPolicy) + return true; + String strippedNonce = stripLeadingAndTrailingHTMLSpaces(nonce); + if (strippedNonce.isEmpty()) + return false; + if (isAllowedByAllWithNonce<&ContentSecurityPolicyDirectiveList::allowStyleWithNonce>(m_policies, strippedNonce)) + return true; + return false; +} + +bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& scriptContent, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const +{ + if (overrideContentSecurityPolicy) + return true; + if (!m_hashAlgorithmsForInlineScripts.isEmpty() && !scriptContent.isEmpty() + && isAllowedByAllWithHashFromContent<&ContentSecurityPolicyDirectiveList::allowInlineScriptWithHash>(m_policies, scriptContent, documentEncoding(), m_hashAlgorithmsForInlineScripts)) + return true; + return isAllowedByAllWithContext<&ContentSecurityPolicyDirectiveList::allowInlineScript>(m_policies, contextURL, contextLine, reportingStatus); +} + +bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& styleContent, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const +{ + if (overrideContentSecurityPolicy) + return true; + if (m_overrideInlineStyleAllowed) + return true; + if (!m_hashAlgorithmsForInlineStylesheets.isEmpty() && !styleContent.isEmpty() + && isAllowedByAllWithHashFromContent<&ContentSecurityPolicyDirectiveList::allowInlineStyleWithHash>(m_policies, styleContent, documentEncoding(), m_hashAlgorithmsForInlineStylesheets)) + return true; + return isAllowedByAllWithContext<&ContentSecurityPolicyDirectiveList::allowInlineStyle>(m_policies, contextURL, contextLine, reportingStatus); } bool ContentSecurityPolicy::allowEval(JSC::ExecState* state, bool overrideContentSecurityPolicy, ContentSecurityPolicy::ReportingStatus reportingStatus) const diff --git a/Source/WebCore/page/csp/ContentSecurityPolicy.h b/Source/WebCore/page/csp/ContentSecurityPolicy.h index 14c60f5c4..dcc05a173 100644 --- a/Source/WebCore/page/csp/ContentSecurityPolicy.h +++ b/Source/WebCore/page/csp/ContentSecurityPolicy.h @@ -29,6 +29,7 @@ #include "ContentSecurityPolicyResponseHeaders.h" #include "ScriptState.h" +#include #include #include @@ -43,8 +44,11 @@ class ContentSecurityPolicySource; class DOMStringList; class ScriptExecutionContext; class SecurityOrigin; +class TextEncoding; class URL; +enum class ContentSecurityPolicyHashAlgorithm; + typedef Vector> CSPDirectiveListVector; typedef int SandboxFlags; @@ -82,8 +86,10 @@ public: }; bool allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy = false, ContentSecurityPolicy::ReportingStatus = ContentSecurityPolicy::ReportingStatus::SendReport) const; bool allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy = false, ContentSecurityPolicy::ReportingStatus = ContentSecurityPolicy::ReportingStatus::SendReport) const; - bool allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy = false, ContentSecurityPolicy::ReportingStatus = ContentSecurityPolicy::ReportingStatus::SendReport) const; - bool allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy = false, ContentSecurityPolicy::ReportingStatus = ContentSecurityPolicy::ReportingStatus::SendReport) const; + bool allowScriptWithNonce(const String& nonce, bool overrideContentSecurityPolicy = false) const; + bool allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& scriptContent, bool overrideContentSecurityPolicy = false, ContentSecurityPolicy::ReportingStatus = ContentSecurityPolicy::ReportingStatus::SendReport) const; + bool allowStyleWithNonce(const String& nonce, bool overrideContentSecurityPolicy = false) const; + bool allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& styleContent, bool overrideContentSecurityPolicy = false, ContentSecurityPolicy::ReportingStatus = ContentSecurityPolicy::ReportingStatus::SendReport) const; bool allowEval(JSC::ExecState* = nullptr, bool overrideContentSecurityPolicy = false, ContentSecurityPolicy::ReportingStatus = ContentSecurityPolicy::ReportingStatus::SendReport) const; bool allowPluginType(const String& type, const String& typeAttribute, const URL&, bool overrideContentSecurityPolicy = false, ContentSecurityPolicy::ReportingStatus = ContentSecurityPolicy::ReportingStatus::SendReport) const; bool allowScriptFromSource(const URL&, bool overrideContentSecurityPolicy = false, ContentSecurityPolicy::ReportingStatus = ContentSecurityPolicy::ReportingStatus::SendReport) const; @@ -134,6 +140,14 @@ public: void reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const URL& blockedURL, const Vector& reportURIs, const String& header, const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), JSC::ExecState* = nullptr) const; void reportBlockedScriptExecutionToInspector(const String& directiveText) const; void enforceSandboxFlags(SandboxFlags sandboxFlags) { m_sandboxFlags |= sandboxFlags; } + void addHashAlgorithmsForInlineScripts(OptionSet hashAlgorithmsForInlineScripts) + { + m_hashAlgorithmsForInlineScripts |= hashAlgorithmsForInlineScripts; + } + void addHashAlgorithmsForInlineStylesheets(OptionSet hashAlgorithmsForInlineStylesheets) + { + m_hashAlgorithmsForInlineStylesheets |= hashAlgorithmsForInlineStylesheets; + } // Used by ContentSecurityPolicySource bool protocolMatchesSelf(const URL&) const; @@ -145,6 +159,8 @@ private: void didReceiveHeader(const String&, ContentSecurityPolicyHeaderType, ContentSecurityPolicy::PolicyFrom); + const TextEncoding& documentEncoding() const; + ScriptExecutionContext* m_scriptExecutionContext { nullptr }; std::unique_ptr m_selfSource; String m_selfSourceProtocol; @@ -152,6 +168,8 @@ private: String m_lastPolicyEvalDisabledErrorMessage; SandboxFlags m_sandboxFlags; bool m_overrideInlineStyleAllowed { false }; + OptionSet m_hashAlgorithmsForInlineScripts; + OptionSet m_hashAlgorithmsForInlineStylesheets; }; } diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp index 01cfa5134..47f5da456 100644 --- a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp +++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp @@ -105,6 +105,40 @@ static bool isNotASCIISpace(UChar c) return !isASCIISpace(c); } +static inline bool checkEval(ContentSecurityPolicySourceListDirective* directive) +{ + return !directive || directive->allowEval(); +} + +static inline bool checkInline(ContentSecurityPolicySourceListDirective* directive) +{ + return !directive || directive->allowInline(); +} + +static inline bool checkSource(ContentSecurityPolicySourceListDirective* directive, const URL& url) +{ + return !directive || directive->allows(url); +} + +static inline bool checkHash(ContentSecurityPolicySourceListDirective* directive, const ContentSecurityPolicyHash& hash) +{ + return !directive || directive->allows(hash); +} + +static inline bool checkNonce(ContentSecurityPolicySourceListDirective* directive, const String& nonce) +{ + return !directive || directive->allows(nonce); +} + +static inline bool checkMediaType(ContentSecurityPolicyMediaListDirective* directive, const String& type, const String& typeAttribute) +{ + if (!directive) + return true; + if (typeAttribute.isEmpty() || typeAttribute.stripWhiteSpace() != type) + return false; + return directive->allows(type); +} + ContentSecurityPolicyDirectiveList::ContentSecurityPolicyDirectiveList(ContentSecurityPolicy& policy, ContentSecurityPolicyHeaderType type) : m_policy(policy) , m_headerType(type) @@ -120,7 +154,7 @@ std::unique_ptr ContentSecurityPolicyDirecti auto directives = std::make_unique(policy, type); directives->parse(header, from); - if (!directives->checkEval(directives->operativeDirective(directives->m_scriptSrc.get()))) { + if (!checkEval(directives->operativeDirective(directives->m_scriptSrc.get()))) { String message = makeString("Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: \"", directives->operativeDirective(directives->m_scriptSrc.get())->text(), "\".\n"); directives->setEvalDisabledErrorMessage(message); } @@ -137,30 +171,6 @@ void ContentSecurityPolicyDirectiveList::reportViolation(const String& directive m_policy.reportViolation(directiveText, effectiveDirective, message, blockedURL, m_reportURIs, m_header, contextURL, contextLine, state); } -bool ContentSecurityPolicyDirectiveList::checkEval(ContentSecurityPolicySourceListDirective* directive) const -{ - return !directive || directive->allowEval(); -} - -bool ContentSecurityPolicyDirectiveList::checkInline(ContentSecurityPolicySourceListDirective* directive) const -{ - return !directive || directive->allowInline(); -} - -bool ContentSecurityPolicyDirectiveList::checkSource(ContentSecurityPolicySourceListDirective* directive, const URL& url) const -{ - return !directive || directive->allows(url); -} - -bool ContentSecurityPolicyDirectiveList::checkMediaType(ContentSecurityPolicyMediaListDirective* directive, const String& type, const String& typeAttribute) const -{ - if (!directive) - return true; - if (typeAttribute.isEmpty() || typeAttribute.stripWhiteSpace() != type) - return false; - return directive->allows(type); -} - ContentSecurityPolicySourceListDirective* ContentSecurityPolicyDirectiveList::operativeDirective(ContentSecurityPolicySourceListDirective* directive) const { return directive ? directive : m_defaultSrc.get(); @@ -278,6 +288,16 @@ bool ContentSecurityPolicyDirectiveList::allowInlineScript(const String& context return m_reportOnly || checkInline(operativeDirective(m_scriptSrc.get())); } +bool ContentSecurityPolicyDirectiveList::allowInlineScriptWithHash(const ContentSecurityPolicyHash& hash) const +{ + return checkHash(operativeDirective(m_scriptSrc.get()), hash); +} + +bool ContentSecurityPolicyDirectiveList::allowScriptWithNonce(const String& nonce) const +{ + return checkNonce(operativeDirective(m_scriptSrc.get()), nonce); +} + bool ContentSecurityPolicyDirectiveList::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const { static NeverDestroyed consoleMessage(ASCIILiteral("Refused to apply inline style because it violates the following Content Security Policy directive: ")); @@ -286,6 +306,16 @@ bool ContentSecurityPolicyDirectiveList::allowInlineStyle(const String& contextU return m_reportOnly || checkInline(operativeDirective(m_styleSrc.get())); } +bool ContentSecurityPolicyDirectiveList::allowInlineStyleWithHash(const ContentSecurityPolicyHash& hash) const +{ + return checkHash(operativeDirective(m_styleSrc.get()), hash); +} + +bool ContentSecurityPolicyDirectiveList::allowStyleWithNonce(const String& nonce) const +{ + return checkNonce(operativeDirective(m_styleSrc.get()), nonce); +} + bool ContentSecurityPolicyDirectiveList::allowEval(JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus reportingStatus) const { static NeverDestroyed consoleMessage(ASCIILiteral("Refused to evaluate script because it violates the following Content Security Policy directive: ")); @@ -579,18 +609,22 @@ void ContentSecurityPolicyDirectiveList::addDirective(const String& name, const { ASSERT(!name.isEmpty()); - if (equalLettersIgnoringASCIICase(name, defaultSrc)) + if (equalLettersIgnoringASCIICase(name, defaultSrc)) { setCSPDirective(name, value, m_defaultSrc); - else if (equalLettersIgnoringASCIICase(name, scriptSrc)) + m_policy.addHashAlgorithmsForInlineScripts(m_defaultSrc->hashAlgorithmsUsed()); + m_policy.addHashAlgorithmsForInlineStylesheets(m_defaultSrc->hashAlgorithmsUsed()); + } else if (equalLettersIgnoringASCIICase(name, scriptSrc)) { setCSPDirective(name, value, m_scriptSrc); - else if (equalLettersIgnoringASCIICase(name, objectSrc)) + m_policy.addHashAlgorithmsForInlineScripts(m_scriptSrc->hashAlgorithmsUsed()); + } else if (equalLettersIgnoringASCIICase(name, styleSrc)) { + setCSPDirective(name, value, m_styleSrc); + m_policy.addHashAlgorithmsForInlineStylesheets(m_styleSrc->hashAlgorithmsUsed()); + } else if (equalLettersIgnoringASCIICase(name, objectSrc)) setCSPDirective(name, value, m_objectSrc); else if (equalLettersIgnoringASCIICase(name, frameSrc)) setCSPDirective(name, value, m_frameSrc); else if (equalLettersIgnoringASCIICase(name, imgSrc)) setCSPDirective(name, value, m_imgSrc); - else if (equalLettersIgnoringASCIICase(name, styleSrc)) - setCSPDirective(name, value, m_styleSrc); else if (equalLettersIgnoringASCIICase(name, fontSrc)) setCSPDirective(name, value, m_fontSrc); else if (equalLettersIgnoringASCIICase(name, mediaSrc)) diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h index 2a98b2d09..997c2a5a6 100644 --- a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h +++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h @@ -28,6 +28,7 @@ #define ContentSecurityPolicyDirectiveList_h #include "ContentSecurityPolicy.h" +#include "ContentSecurityPolicyHash.h" #include "ContentSecurityPolicyMediaListDirective.h" #include "ContentSecurityPolicySourceListDirective.h" #include "URL.h" @@ -50,7 +51,11 @@ public: bool allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const; bool allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const; bool allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const; + bool allowInlineScriptWithHash(const ContentSecurityPolicyHash&) const; + bool allowScriptWithNonce(const String& nonce) const; bool allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const; + bool allowInlineStyleWithHash(const ContentSecurityPolicyHash&) const; + bool allowStyleWithNonce(const String& nonce) const; bool allowEval(JSC::ExecState*, ContentSecurityPolicy::ReportingStatus) const; bool allowPluginType(const String& type, const String& typeAttribute, const URL&, ContentSecurityPolicy::ReportingStatus) const; @@ -87,11 +92,6 @@ private: ContentSecurityPolicySourceListDirective* operativeDirective(ContentSecurityPolicySourceListDirective*) const; void reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const URL& blockedURL = URL(), const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), JSC::ExecState* = nullptr) const; - bool checkEval(ContentSecurityPolicySourceListDirective*) const; - bool checkInline(ContentSecurityPolicySourceListDirective*) const; - bool checkSource(ContentSecurityPolicySourceListDirective*, const URL&) const; - bool checkMediaType(ContentSecurityPolicyMediaListDirective*, const String& type, const String& typeAttribute) const; - void setEvalDisabledErrorMessage(const String& errorMessage) { m_evalDisabledErrorMessage = errorMessage; } bool checkEvalAndReportViolation(ContentSecurityPolicySourceListDirective*, const String& consoleMessage, const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), JSC::ExecState* = nullptr) const; diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyHash.h b/Source/WebCore/page/csp/ContentSecurityPolicyHash.h new file mode 100644 index 000000000..12a94f261 --- /dev/null +++ b/Source/WebCore/page/csp/ContentSecurityPolicyHash.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ContentSecurityPolicyHash_h +#define ContentSecurityPolicyHash_h + +#include +#include +#include + +namespace WebCore { + +// Keep this synchronized with the constant maximumContentSecurityPolicyDigestLength below. +enum class ContentSecurityPolicyHashAlgorithm { + SHA_256 = 1 << 0, + SHA_384 = 1 << 1, + SHA_512 = 1 << 2, +}; + +const size_t maximumContentSecurityPolicyDigestLength = 64; // bytes to hold SHA-512 digest + +typedef Vector ContentSecurityPolicyDigest; +typedef std::pair ContentSecurityPolicyHash; + +} + +namespace WTF { + +template<> struct DefaultHash { typedef IntHash Hash; }; +template<> struct HashTraits : StrongEnumHashTraits { }; +template<> struct DefaultHash { + struct Hash { + static unsigned hash(const WebCore::ContentSecurityPolicyDigest& digest) + { + return StringHasher::computeHashAndMaskTop8Bits(digest.data(), digest.size()); + } + static bool equal(const WebCore::ContentSecurityPolicyDigest& a, const WebCore::ContentSecurityPolicyDigest& b) + { + return a == b; + } + static const bool safeToCompareToEmptyOrDeleted = true; + }; +}; + +} + +#endif // ContentSecurityPolicyHash_h diff --git a/Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp b/Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp index 408b40e2b..8044ae8bf 100644 --- a/Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp +++ b/Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp @@ -33,6 +33,8 @@ #include "SecurityOrigin.h" #include "URL.h" #include +#include +#include namespace WebCore { @@ -125,6 +127,16 @@ bool ContentSecurityPolicySourceList::matches(const URL& url) return false; } +bool ContentSecurityPolicySourceList::matches(const ContentSecurityPolicyHash& hash) const +{ + return m_hashes.contains(hash); +} + +bool ContentSecurityPolicySourceList::matches(const String& nonce) const +{ + return m_nonces.contains(nonce); +} + // source-list = *WSP [ source *( 1*WSP source ) *WSP ] // / *WSP "'none'" *WSP // @@ -145,6 +157,12 @@ void ContentSecurityPolicySourceList::parse(const UChar* begin, const UChar* end bool hostHasWildcard = false; bool portHasWildcard = false; + if (parseNonceSource(beginSource, position)) + continue; + + if (parseHashSource(beginSource, position)) + continue; + if (parseSource(beginSource, position, scheme, host, port, path, hostHasWildcard, portHasWildcard)) { // Wildcard hosts and keyword sources ('self', 'unsafe-inline', // etc.) aren't stored in m_list, but as attributes on the source @@ -385,4 +403,96 @@ bool ContentSecurityPolicySourceList::parsePort(const UChar* begin, const UChar* return ok; } +static bool isBase64Character(UChar c) +{ + return isASCIIAlphanumeric(c) || c == '+' || c == '/' || c == '-' || c == '_'; +} + +// Match Blink's behavior of allowing an equal sign to appear anywhere in the value of the nonce +// even though this does not match the behavior of Content Security Policy Level 3 spec., +// (29 February 2016). +static bool isNonceCharacter(UChar c) +{ + return isBase64Character(c) || c == '='; +} + +// nonce-source = "'nonce-" nonce-value "'" +// nonce-value = base64-value +bool ContentSecurityPolicySourceList::parseNonceSource(const UChar* begin, const UChar* end) +{ + static NeverDestroyed noncePrefix("'nonce-", String::ConstructFromLiteral); + if (!StringView(begin, end - begin).startsWithIgnoringASCIICase(noncePrefix.get())) + return false; + const UChar* position = begin + noncePrefix.get().length(); + const UChar* beginNonceValue = position; + skipWhile(position, end); + if (position >= end || position == beginNonceValue || *position != '\'') + return false; + m_nonces.add(String(beginNonceValue, position - beginNonceValue)); + return true; +} + +static bool parseHashAlgorithmAdvancingPosition(const UChar*& position, size_t length, ContentSecurityPolicyHashAlgorithm& algorithm) +{ + static struct { + NeverDestroyed label; + ContentSecurityPolicyHashAlgorithm algorithm; + } labelToHashAlgorithmTable[] { + { ASCIILiteral("sha256"), ContentSecurityPolicyHashAlgorithm::SHA_256 }, + { ASCIILiteral("sha384"), ContentSecurityPolicyHashAlgorithm::SHA_384 }, + { ASCIILiteral("sha512"), ContentSecurityPolicyHashAlgorithm::SHA_512 }, + }; + + StringView stringView(position, length); + for (auto& entry : labelToHashAlgorithmTable) { + String& label = entry.label.get(); + if (!stringView.startsWithIgnoringASCIICase(label)) + continue; + position += label.length(); + algorithm = entry.algorithm; + return true; + } + return false; +} + +// hash-source = "'" hash-algorithm "-" base64-value "'" +// hash-algorithm = "sha256" / "sha384" / "sha512" +// base64-value = 1*( ALPHA / DIGIT / "+" / "/" / "-" / "_" )*2( "=" ) +bool ContentSecurityPolicySourceList::parseHashSource(const UChar* begin, const UChar* end) +{ + if (begin == end) + return false; + + const UChar* position = begin; + if (!skipExactly(position, end, '\'')) + return false; + + ContentSecurityPolicyHashAlgorithm algorithm; + if (!parseHashAlgorithmAdvancingPosition(position, end - position, algorithm)) + return false; + + if (!skipExactly(position, end, '-')) + return false; + + const UChar* beginHashValue = position; + skipWhile(position, end); + skipExactly(position, end, '='); + skipExactly(position, end, '='); + if (position >= end || position == beginHashValue || *position != '\'') + return false; + Vector digest; + StringView hashValue(beginHashValue, position - beginHashValue); // base64url or base64 encoded + // FIXME: Normalize Base64URL to Base64 instead of decoding twice. See . + if (!base64Decode(hashValue.toStringWithoutCopying(), digest, Base64ValidatePadding)) { + if (!base64URLDecode(hashValue.toStringWithoutCopying(), digest)) + return false; + } + if (digest.size() > maximumContentSecurityPolicyDigestLength) + return false; + + m_hashes.add(std::make_pair(algorithm, digest)); + m_hashAlgorithmsUsed |= algorithm; + return true; +} + } // namespace WebCore diff --git a/Source/WebCore/page/csp/ContentSecurityPolicySourceList.h b/Source/WebCore/page/csp/ContentSecurityPolicySourceList.h index fdb9b7e09..811b6cec1 100644 --- a/Source/WebCore/page/csp/ContentSecurityPolicySourceList.h +++ b/Source/WebCore/page/csp/ContentSecurityPolicySourceList.h @@ -27,8 +27,11 @@ #ifndef ContentSecurityPolicySourceList_h #define ContentSecurityPolicySourceList_h +#include "ContentSecurityPolicyHash.h" #include "ContentSecurityPolicySource.h" -#include +#include +#include +#include #include namespace WebCore { @@ -41,8 +44,14 @@ public: ContentSecurityPolicySourceList(const ContentSecurityPolicy&, const String& directiveName); void parse(const String&); + bool matches(const URL&); - bool allowInline() const { return m_allowInline; } + bool matches(const ContentSecurityPolicyHash&) const; + bool matches(const String& nonce) const; + + OptionSet hashAlgorithmsUsed() const { return m_hashAlgorithmsUsed; } + + bool allowInline() const { return m_allowInline && m_hashes.isEmpty() && m_nonces.isEmpty(); } bool allowEval() const { return m_allowEval; } bool allowSelf() const { return m_allowSelf; } @@ -55,10 +64,17 @@ private: bool parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard); bool parsePath(const UChar* begin, const UChar* end, String& path); + bool parseNonceSource(const UChar* begin, const UChar* end); + bool isProtocolAllowedByStar(const URL&) const; + bool parseHashSource(const UChar* begin, const UChar* end); + const ContentSecurityPolicy& m_policy; Vector m_list; + HashSet m_nonces; + HashSet m_hashes; + OptionSet m_hashAlgorithmsUsed; String m_directiveName; bool m_allowSelf { false }; bool m_allowStar { false }; diff --git a/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.cpp b/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.cpp index 89133d4eb..4c4f11c42 100644 --- a/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.cpp +++ b/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.cpp @@ -47,4 +47,14 @@ bool ContentSecurityPolicySourceListDirective::allows(const URL& url) return m_sourceList.matches(url); } +bool ContentSecurityPolicySourceListDirective::allows(const String& nonce) const +{ + return m_sourceList.matches(nonce); +} + +bool ContentSecurityPolicySourceListDirective::allows(const ContentSecurityPolicyHash& hash) const +{ + return m_sourceList.matches(hash); +} + } // namespace WebCore diff --git a/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.h b/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.h index 9d4e2114c..ac650052d 100644 --- a/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.h +++ b/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.h @@ -39,9 +39,13 @@ public: ContentSecurityPolicySourceListDirective(const String& name, const String& value, const ContentSecurityPolicy&); bool allows(const URL&); + bool allows(const ContentSecurityPolicyHash&) const; + bool allows(const String& nonce) const; bool allowInline() const { return m_sourceList.allowInline(); } bool allowEval() const { return m_sourceList.allowEval(); } + OptionSet hashAlgorithmsUsed() const { return m_sourceList.hashAlgorithmsUsed(); } + private: ContentSecurityPolicySourceList m_sourceList; }; diff --git a/Source/WebCore/platform/crypto/CryptoDigest.h b/Source/WebCore/platform/crypto/CryptoDigest.h new file mode 100644 index 000000000..31f075aa3 --- /dev/null +++ b/Source/WebCore/platform/crypto/CryptoDigest.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CryptoDigest_h +#define CryptoDigest_h + +#include +#include + +namespace WebCore { + +struct CryptoDigestContext; + +class CryptoDigest { + WTF_MAKE_NONCOPYABLE(CryptoDigest); +public: + enum class Algorithm { + SHA_1, + SHA_224, + SHA_256, + SHA_384, + SHA_512, + }; + static std::unique_ptr create(Algorithm); + ~CryptoDigest(); + + void addBytes(const void* input, size_t length); + Vector computeHash(); + +private: + CryptoDigest(); + + std::unique_ptr m_context; +}; + +} // namespace WebCore + +#endif // CryptoDigest_h diff --git a/Source/WebCore/platform/crypto/qt/CryptoDigestQt.cpp b/Source/WebCore/platform/crypto/qt/CryptoDigestQt.cpp new file mode 100644 index 000000000..1dcb8bf72 --- /dev/null +++ b/Source/WebCore/platform/crypto/qt/CryptoDigestQt.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 Konstantin Tokavev + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CryptoDigest.h" + +#include +#include + +namespace WebCore { + +static QCryptographicHash::Algorithm toQtAlgorithm(CryptoDigest::Algorithm algorithm) +{ + switch (algorithm) { + case CryptoDigest::Algorithm::SHA_1: + return QCryptographicHash::Sha1; + + case CryptoDigest::Algorithm::SHA_224: + return QCryptographicHash::Sha224; + + case CryptoDigest::Algorithm::SHA_256: + return QCryptographicHash::Sha256; + + case CryptoDigest::Algorithm::SHA_384: + return QCryptographicHash::Sha384; + + case CryptoDigest::Algorithm::SHA_512: + return QCryptographicHash::Sha512; + } + + ASSERT_NOT_REACHED(); + return QCryptographicHash::Algorithm(); +} + +struct CryptoDigestContext { + CryptoDigestContext(QCryptographicHash::Algorithm algorithm) + : hash(algorithm) + { + } + QCryptographicHash hash; +}; + +CryptoDigest::CryptoDigest() +{ +} + +CryptoDigest::~CryptoDigest() +{ +} + +std::unique_ptr CryptoDigest::create(CryptoDigest::Algorithm algorithm) +{ + std::unique_ptr digest(new CryptoDigest); + digest->m_context.reset(new CryptoDigestContext(toQtAlgorithm(algorithm))); + return digest; +} + +void CryptoDigest::addBytes(const void* input, size_t length) +{ + m_context->hash.addData(static_cast(input), length); +} + +Vector CryptoDigest::computeHash() +{ + QByteArray digest = m_context->hash.result(); + Vector result(digest.size()); + memcpy(result.data(), digest.constData(), digest.size()); + return result; +} + +} // namespace WebCore diff --git a/Source/WebKit/PlatformQt.cmake b/Source/WebKit/PlatformQt.cmake index 909efc00d..3792def6f 100644 --- a/Source/WebKit/PlatformQt.cmake +++ b/Source/WebKit/PlatformQt.cmake @@ -418,7 +418,7 @@ install( COMPONENT Data ) -file(GLOB WebKit_PRIVATE_HEADERS qt/Api/*_p.h ../WebKit2/UIProcess/API/qt/*_p.h) +file(GLOB WebKit_PRIVATE_HEADERS qt/Api/*_p.h) install( FILES ${WebKit_PRIVATE_HEADERS} diff --git a/Source/WebKit2/PlatformQt.cmake b/Source/WebKit2/PlatformQt.cmake index 6bd0627f0..5883096eb 100644 --- a/Source/WebKit2/PlatformQt.cmake +++ b/Source/WebKit2/PlatformQt.cmake @@ -1,7 +1,7 @@ set(WebKit2_WebProcess_OUTPUT_NAME QtWebProcess) set(WebKit2_NetworkProcess_OUTPUT_NAME QtWebNetworkProcess) set(WebKit2_PluginProcess_OUTPUT_NAME QtWebPluginProcess) -set(WebKit2_DatabaseProcess_OUTPUT_NAME QtWebDatabaseProcess) +set(WebKit2_DatabaseProcess_OUTPUT_NAME QtWebStorageProcess) file(MAKE_DIRECTORY ${DERIVED_SOURCES_WEBKIT2_DIR}) @@ -259,6 +259,7 @@ if (ENABLE_NETSCAPE_PLUGIN_API) endif () list(APPEND WebKit2_SYSTEM_INCLUDE_DIRECTORIES + ${GLIB_INCLUDE_DIRS} ${GSTREAMER_INCLUDE_DIRS} ${Qt5Quick_INCLUDE_DIRS} ${Qt5Quick_PRIVATE_INCLUDE_DIRS} @@ -328,3 +329,12 @@ WEBKIT_CREATE_FORWARDING_HEADERS(QtWebKit/private DIRECTORIES UIProcess/API/qt) if (ENABLE_API_TESTS) add_subdirectory(UIProcess/API/qt/tests) endif () + +file(GLOB WebKit2_PRIVATE_HEADERS UIProcess/API/qt/*_p.h) +install( + FILES + ${WebKit2_PRIVATE_HEADERS} + DESTINATION + ${KDE_INSTALL_INCLUDEDIR}/QtWebKit/${PROJECT_VERSION}/QtWebKit/private + COMPONENT Data +) diff --git a/Source/WebKit2/Shared/qt/ProcessExecutablePathQt.cpp b/Source/WebKit2/Shared/qt/ProcessExecutablePathQt.cpp index c5f809d34..ae14ef13e 100644 --- a/Source/WebKit2/Shared/qt/ProcessExecutablePathQt.cpp +++ b/Source/WebKit2/Shared/qt/ProcessExecutablePathQt.cpp @@ -65,4 +65,11 @@ String executablePathOfNetworkProcess() return executablePath(QStringLiteral("QtWebNetworkProcess")); } +#if ENABLE(DATABASE_PROCESS) +String executablePathOfDatabaseProcess() +{ + return executablePath(QStringLiteral("QtWebStorageProcess")); +} +#endif + } // namespace WebKit diff --git a/Source/WebKit2/UIProcess/API/qt/qquickwebview.cpp b/Source/WebKit2/UIProcess/API/qt/qquickwebview.cpp index 3b84c6dba..b894d7338 100644 --- a/Source/WebKit2/UIProcess/API/qt/qquickwebview.cpp +++ b/Source/WebKit2/UIProcess/API/qt/qquickwebview.cpp @@ -407,6 +407,7 @@ void QQuickWebViewPrivate::initialize(WKPageConfigurationRef configurationRef) preferences.setMediaSourceEnabled(false); preferences.setWebGLEnabled(true); preferences.setForceCompositingMode(true); + preferences.setAllowFileAccessFromFileURLs(true); webPageProxy->setURLSchemeHandlerForScheme(QrcSchemeHandler::create(), ASCIILiteral("qrc")); diff --git a/Source/WebKit2/UIProcess/Launcher/qt/ProcessLauncherQt.cpp b/Source/WebKit2/UIProcess/Launcher/qt/ProcessLauncherQt.cpp index 53913fa56..51a3ffbba 100644 --- a/Source/WebKit2/UIProcess/Launcher/qt/ProcessLauncherQt.cpp +++ b/Source/WebKit2/UIProcess/Launcher/qt/ProcessLauncherQt.cpp @@ -122,6 +122,12 @@ void ProcessLauncher::launchProcess() commandLine = QLatin1String("%1 \"%2\" %3 %4"); QByteArray pluginProcessPrefix = qgetenv("QT_WEBKIT2_PP_CMD_PREFIX"); commandLine = commandLine.arg(QLatin1String(pluginProcessPrefix.constData())).arg(QString(executablePathOfPluginProcess())); +#endif +#if ENABLE(DATABASE_PROCESS) + } else if (m_launchOptions.processType == ProcessType::Database) { + commandLine = QLatin1String("%1 \"%2\" %3 %4"); + QByteArray processPrefix = qgetenv("QT_WEBKIT2_DP_CMD_PREFIX"); + commandLine = commandLine.arg(QLatin1String(processPrefix.constData())).arg(QString(executablePathOfDatabaseProcess())); #endif } else { qDebug() << "Unsupported process type" << (int)m_launchOptions.processType; diff --git a/Source/cmake/OptionsQt.cmake b/Source/cmake/OptionsQt.cmake index 9ca197f72..559b2ab8b 100644 --- a/Source/cmake/OptionsQt.cmake +++ b/Source/cmake/OptionsQt.cmake @@ -480,10 +480,11 @@ else () endif () endif () -find_package(Fontconfig) - -if (FONTCONFIG_FOUND) - SET_AND_EXPOSE_TO_BUILD(HAVE_FONTCONFIG 1) +if (ENABLE_TEST_SUPPORT) + find_package(Fontconfig) + if (FONTCONFIG_FOUND) + SET_AND_EXPOSE_TO_BUILD(HAVE_FONTCONFIG 1) + endif () endif () find_package(WebP) @@ -521,6 +522,33 @@ if (ENABLE_DEVICE_ORIENTATION) SET_AND_EXPOSE_TO_BUILD(HAVE_QTSENSORS 1) endif () +if (ENABLE_OPENGL) + # Note: Gui module is already found + # Warning: quotes are sinificant here! + if (NOT DEFINED Qt5Gui_OPENGL_IMPLEMENTATION OR "${Qt5Gui_OPENGL_IMPLEMENTATION}" STREQUAL "") + message(FATAL_ERROR "Qt with OpenGL support is required for ENABLE_OPENGL") + endif () + + SET_AND_EXPOSE_TO_BUILD(USE_TEXTURE_MAPPER_GL TRUE) + SET_AND_EXPOSE_TO_BUILD(ENABLE_GRAPHICS_CONTEXT_3D TRUE) + + if (WIN32) + include(CheckCXXSymbolExists) + set(CMAKE_REQUIRED_INCLUDES ${Qt5Gui_INCLUDE_DIRS}) + set(CMAKE_REQUIRED_FLAGS ${Qt5Gui_EXECUTABLE_COMPILE_FLAGS}) + check_cxx_symbol_exists(QT_OPENGL_DYNAMIC qopenglcontext.h HAVE_QT_OPENGL_DYNAMIC) + if (HAVE_QT_OPENGL_DYNAMIC) + set(Qt5Gui_OPENGL_IMPLEMENTATION DynamicGL) + endif () + unset(CMAKE_REQUIRED_INCLUDES) + unset(CMAKE_REQUIRED_FLAGS) + endif () + + message(STATUS "Qt OpenGL implementation: ${Qt5Gui_OPENGL_IMPLEMENTATION}") + message(STATUS "Qt OpenGL libraries: ${Qt5Gui_OPENGL_LIBRARIES}") + message(STATUS "Qt EGL libraries: ${Qt5Gui_EGL_LIBRARIES}") +endif () + if (ENABLE_PRINT_SUPPORT) list(APPEND QT_REQUIRED_COMPONENTS PrintSupport) SET_AND_EXPOSE_TO_BUILD(HAVE_QTPRINTSUPPORT 1) @@ -665,25 +693,6 @@ if (ENABLE_X11_TARGET) endif () endif () -if (ENABLE_OPENGL) - SET_AND_EXPOSE_TO_BUILD(USE_TEXTURE_MAPPER_GL TRUE) - SET_AND_EXPOSE_TO_BUILD(ENABLE_GRAPHICS_CONTEXT_3D TRUE) - - if (WIN32) - include(CheckCXXSymbolExists) - set(CMAKE_REQUIRED_INCLUDES ${Qt5Gui_INCLUDE_DIRS}) - set(CMAKE_REQUIRED_FLAGS ${Qt5Gui_EXECUTABLE_COMPILE_FLAGS}) - check_cxx_symbol_exists(QT_OPENGL_DYNAMIC qopenglcontext.h HAVE_QT_OPENGL_DYNAMIC) - if (HAVE_QT_OPENGL_DYNAMIC) - set(Qt5Gui_OPENGL_IMPLEMENTATION DynamicGL) - endif () - unset(CMAKE_REQUIRED_INCLUDES) - unset(CMAKE_REQUIRED_FLAGS) - endif () - - message("Qt OpenGL implementation: ${Qt5Gui_OPENGL_IMPLEMENTATION}") -endif () - if (NOT ENABLE_VIDEO) set(USE_MEDIA_FOUNDATION OFF) set(USE_QT_MULTIMEDIA OFF) diff --git a/Source/cmake/WebKitMacros.cmake b/Source/cmake/WebKitMacros.cmake index a34e85463..07e31432a 100644 --- a/Source/cmake/WebKitMacros.cmake +++ b/Source/cmake/WebKitMacros.cmake @@ -265,6 +265,8 @@ macro(WEBKIT_FRAMEWORK _target) ${${_target}_HEADERS} ${${_target}_SOURCES} ${${_target}_DERIVED_SOURCES} + ${${_target}_PRIVATE_HEADERS} + ${${_target}_PUBLIC_HEADERS} ) target_link_libraries(${_target} ${${_target}_LIBRARIES}) set_target_properties(${_target} PROPERTIES COMPILE_DEFINITIONS "BUILDING_${_target}") diff --git a/Tools/qmake/projects/run_cmake.pro b/Tools/qmake/projects/run_cmake.pro index fadfda059..f63fcd658 100644 --- a/Tools/qmake/projects/run_cmake.pro +++ b/Tools/qmake/projects/run_cmake.pro @@ -25,11 +25,10 @@ build_pass|!debug_and_release { USE_LIBHYPHEN=OFF !isEmpty(_QMAKE_SUPER_CACHE_) { - CMAKE_PREFIX_PATH=\"$$ROOT_QT_BUILD_DIR/qtbase;$$ROOT_QT_BUILD_DIR/qtlocation;$$ROOT_QT_BUILD_DIR/qtsensors;$$ROOT_QT_BUILD_DIR/qtdeclarative;$$ROOT_QT_BUILD_DIR/qtwebchannel\" + CMAKE_CONFIG += CMAKE_PREFIX_PATH=\"$$ROOT_QT_BUILD_DIR/qtbase;$$ROOT_QT_BUILD_DIR/qtlocation;$$ROOT_QT_BUILD_DIR/qtsensors;$$ROOT_QT_BUILD_DIR/qtdeclarative;$$ROOT_QT_BUILD_DIR/qtwebchannel\" } else { - CMAKE_PREFIX_PATH=\"$$[QT_INSTALL_PREFIX]\" + CMAKE_CONFIG += Qt5_DIR=\"$$[QT_INSTALL_LIBS]/cmake/Qt5\" } - CMAKE_CONFIG += CMAKE_PREFIX_PATH=$$CMAKE_PREFIX_PATH static: CMAKE_CONFIG += USE_THIN_ARCHIVES=OFF -- cgit v1.2.3