Bug 1229639 - Part 1: Match CSP host source with percent-decoded URI. r=ckerschb
authorHenry Chang <hchang@mozilla.com>
Tue, 06 Sep 2016 18:29:26 +0800
changeset 354278 e8f963c5afd2c0b140a94532b0a2cdbe5fde679d
parent 354277 e6cb70f31562075a607506632440222aee0a7db3
child 354279 f0df0335a0497383aa3ac8886855f73e870c2db8
push id6570
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:26:13 +0000
treeherdermozilla-beta@f455459b2ae5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersckerschb
bugs1229639
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1229639 - Part 1: Match CSP host source with percent-decoded URI. r=ckerschb MozReview-Commit-ID: CSGeoSR2qw8
dom/security/nsCSPParser.cpp
dom/security/nsCSPParser.h
dom/security/nsCSPUtils.cpp
dom/security/nsCSPUtils.h
--- a/dom/security/nsCSPParser.cpp
+++ b/dom/security/nsCSPParser.cpp
@@ -186,73 +186,16 @@ nsCSPParser::resetCurChar(const nsAStrin
 // number sign ("#") character, or by the end of the URI.
 // http://tools.ietf.org/html/rfc3986#section-3.3
 bool
 nsCSPParser::atEndOfPath()
 {
   return (atEnd() || peek(QUESTIONMARK) || peek(NUMBER_SIGN));
 }
 
-void
-nsCSPParser::percentDecodeStr(const nsAString& aEncStr, nsAString& outDecStr)
-{
-  outDecStr.Truncate();
-
-  // helper function that should not be visible outside this methods scope
-  struct local {
-    static inline char16_t convertHexDig(char16_t aHexDig) {
-      if (isNumberToken(aHexDig)) {
-        return aHexDig - '0';
-      }
-      if (aHexDig >= 'A' && aHexDig <= 'F') {
-        return aHexDig - 'A' + 10;
-      }
-      // must be a lower case character
-      // (aHexDig >= 'a' && aHexDig <= 'f')
-      return aHexDig - 'a' + 10;
-    }
-  };
-
-  const char16_t *cur, *end, *hexDig1, *hexDig2;
-  cur = aEncStr.BeginReading();
-  end = aEncStr.EndReading();
-
-  while (cur != end) {
-    // if it's not a percent sign then there is
-    // nothing to do for that character
-    if (*cur != PERCENT_SIGN) {
-      outDecStr.Append(*cur);
-      cur++;
-      continue;
-    }
-
-    // get the two hexDigs following the '%'-sign
-    hexDig1 = cur + 1;
-    hexDig2 = cur + 2;
-
-    // if there are no hexdigs after the '%' then
-    // there is nothing to do for us.
-    if (hexDig1 == end || hexDig2 == end ||
-        !isValidHexDig(*hexDig1) ||
-        !isValidHexDig(*hexDig2)) {
-      outDecStr.Append(PERCENT_SIGN);
-      cur++;
-      continue;
-    }
-
-    // decode "% hexDig1 hexDig2" into a character.
-    char16_t decChar = (local::convertHexDig(*hexDig1) << 4) +
-                       local::convertHexDig(*hexDig2);
-    outDecStr.Append(decChar);
-
-    // increment 'cur' to after the second hexDig
-    cur = ++hexDig2;
-  }
-}
-
 // unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
 bool
 nsCSPParser::atValidUnreservedChar()
 {
   return (peek(isCharacterToken) || peek(isNumberToken) ||
           peek(DASH) || peek(DOT) ||
           peek(UNDERLINE) || peek(TILDE));
 }
@@ -393,17 +336,17 @@ nsCSPParser::subPath(nsCSPHostSrc* aCspH
   uint32_t charCounter = 0;
   nsString pctDecodedSubPath;
 
   while (!atEndOfPath()) {
     if (peek(SLASH)) {
       // before appendig any additional portion of a subpath we have to pct-decode
       // that portion of the subpath. atValidPathChar() already verified a correct
       // pct-encoding, now we can safely decode and append the decoded-sub path.
-      percentDecodeStr(mCurValue, pctDecodedSubPath);
+      CSP_PercentDecodeStr(mCurValue, pctDecodedSubPath);
       aCspHost->appendPath(pctDecodedSubPath);
       // Resetting current value since we are appending parts of the path
       // to aCspHost, e.g; "http://www.example.com/path1/path2" then the
       // first part is "/path1", second part "/path2"
       resetCurValue();
     }
     else if (!atValidPathChar()) {
       const char16_t* params[] = { mCurToken.get() };
@@ -422,17 +365,17 @@ nsCSPParser::subPath(nsCSPHostSrc* aCspH
     advance();
     if (++charCounter > kSubHostPathCharacterCutoff) {
       return false;
     }
   }
   // before appendig any additional portion of a subpath we have to pct-decode
   // that portion of the subpath. atValidPathChar() already verified a correct
   // pct-encoding, now we can safely decode and append the decoded-sub path.
-  percentDecodeStr(mCurValue, pctDecodedSubPath);
+  CSP_PercentDecodeStr(mCurValue, pctDecodedSubPath);
   aCspHost->appendPath(pctDecodedSubPath);
   resetCurValue();
   return true;
 }
 
 bool
 nsCSPParser::path(nsCSPHostSrc* aCspHost)
 {
--- a/dom/security/nsCSPParser.h
+++ b/dom/security/nsCSPParser.h
@@ -139,18 +139,16 @@ class nsCSPParser {
     bool                port();
     bool                path(nsCSPHostSrc* aCspHost);
 
     bool subHost();                                         // helper function to parse subDomains
     bool atValidUnreservedChar();                           // helper function to parse unreserved
     bool atValidSubDelimChar();                             // helper function to parse sub-delims
     bool atValidPctEncodedChar();                           // helper function to parse pct-encoded
     bool subPath(nsCSPHostSrc* aCspHost);                   // helper function to parse paths
-    void percentDecodeStr(const nsAString& aEncStr,         // helper function to percent-decode
-                          nsAString& outDecStr);
 
     inline bool atEnd()
     {
       return mCurChar >= mEndChar;
     }
 
     inline bool accept(char16_t aSymbol)
     {
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -26,16 +26,73 @@ GetCspUtilsLog()
   static mozilla::LazyLogModule gCspUtilsPRLog("CSPUtils");
   return gCspUtilsPRLog;
 }
 
 #define CSPUTILSLOG(args) MOZ_LOG(GetCspUtilsLog(), mozilla::LogLevel::Debug, args)
 #define CSPUTILSLOGENABLED() MOZ_LOG_TEST(GetCspUtilsLog(), mozilla::LogLevel::Debug)
 
 void
+CSP_PercentDecodeStr(const nsAString& aEncStr, nsAString& outDecStr)
+{
+  outDecStr.Truncate();
+
+  // helper function that should not be visible outside this methods scope
+  struct local {
+    static inline char16_t convertHexDig(char16_t aHexDig) {
+      if (isNumberToken(aHexDig)) {
+        return aHexDig - '0';
+      }
+      if (aHexDig >= 'A' && aHexDig <= 'F') {
+        return aHexDig - 'A' + 10;
+      }
+      // must be a lower case character
+      // (aHexDig >= 'a' && aHexDig <= 'f')
+      return aHexDig - 'a' + 10;
+    }
+  };
+
+  const char16_t *cur, *end, *hexDig1, *hexDig2;
+  cur = aEncStr.BeginReading();
+  end = aEncStr.EndReading();
+
+  while (cur != end) {
+    // if it's not a percent sign then there is
+    // nothing to do for that character
+    if (*cur != PERCENT_SIGN) {
+      outDecStr.Append(*cur);
+      cur++;
+      continue;
+    }
+
+    // get the two hexDigs following the '%'-sign
+    hexDig1 = cur + 1;
+    hexDig2 = cur + 2;
+
+    // if there are no hexdigs after the '%' then
+    // there is nothing to do for us.
+    if (hexDig1 == end || hexDig2 == end ||
+        !isValidHexDig(*hexDig1) ||
+        !isValidHexDig(*hexDig2)) {
+      outDecStr.Append(PERCENT_SIGN);
+      cur++;
+      continue;
+    }
+
+    // decode "% hexDig1 hexDig2" into a character.
+    char16_t decChar = (local::convertHexDig(*hexDig1) << 4) +
+                       local::convertHexDig(*hexDig2);
+    outDecStr.Append(decChar);
+
+    // increment 'cur' to after the second hexDig
+    cur = ++hexDig2;
+  }
+}
+
+void
 CSP_GetLocalizedStr(const char16_t* aName,
                     const char16_t** aParams,
                     uint32_t aLength,
                     char16_t** outResult)
 {
   nsCOMPtr<nsIStringBundle> keyStringBundle;
   nsCOMPtr<nsIStringBundleService> stringBundleService =
     mozilla::services::GetStringBundleService();
@@ -562,30 +619,33 @@ nsCSPHostSrc::permits(nsIURI* aUri, cons
   }
 
   // Before we can check if the host matches, we have to
   // extract the host part from aUri.
   nsAutoCString uriHost;
   nsresult rv = aUri->GetHost(uriHost);
   NS_ENSURE_SUCCESS(rv, false);
 
+  nsString decodedUriHost;
+  CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriHost), decodedUriHost);
+
   // 4.5) host matching: Check if the allowed host starts with a wilcard.
   if (mHost.First() == '*') {
     NS_ASSERTION(mHost[1] == '.', "Second character needs to be '.' whenever host starts with '*'");
 
     // Eliminate leading "*", but keeping the FULL STOP (.) thereafter before checking
     // if the remaining characters match
     nsString wildCardHost = mHost;
     wildCardHost = Substring(wildCardHost, 1, wildCardHost.Length() - 1);
-    if (!StringEndsWith(NS_ConvertUTF8toUTF16(uriHost), wildCardHost)) {
+    if (!StringEndsWith(decodedUriHost, wildCardHost)) {
       return false;
     }
   }
   // 4.6) host matching: Check if hosts match.
-  else if (!mHost.Equals(NS_ConvertUTF8toUTF16(uriHost))) {
+  else if (!mHost.Equals(decodedUriHost)) {
     return false;
   }
 
   // Port matching: Check if the ports match.
   if (!permitsPort(mScheme, mPort, aUri)) {
     return false;
   }
 
@@ -599,28 +659,32 @@ nsCSPHostSrc::permits(nsIURI* aUri, cons
     nsCOMPtr<nsIURL> url = do_QueryInterface(aUri);
     if (!url) {
       NS_ASSERTION(false, "can't QI into nsIURI");
       return false;
     }
     nsAutoCString uriPath;
     rv = url->GetFilePath(uriPath);
     NS_ENSURE_SUCCESS(rv, false);
+
+    nsString decodedUriPath;
+    CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriPath), decodedUriPath);
+
     // check if the last character of mPath is '/'; if so
     // we just have to check loading resource is within
     // the allowed path.
     if (mPath.Last() == '/') {
-      if (!StringBeginsWith(NS_ConvertUTF8toUTF16(uriPath), mPath)) {
+      if (!StringBeginsWith(decodedUriPath, mPath)) {
         return false;
       }
     }
     // otherwise mPath whitelists a specific file, and we have to
     // check if the loading resource matches that whitelisted file.
     else {
-      if (!mPath.Equals(NS_ConvertUTF8toUTF16(uriPath))) {
+      if (!mPath.Equals(decodedUriPath)) {
         return false;
       }
     }
   }
 
   // At the end: scheme, host, port and path match -> allow the load.
   return true;
 }
--- a/dom/security/nsCSPUtils.h
+++ b/dom/security/nsCSPUtils.h
@@ -188,16 +188,18 @@ nsCSPHostSrc* CSP_CreateHostSrcFromURI(n
 bool CSP_IsValidDirective(const nsAString& aDir);
 bool CSP_IsDirective(const nsAString& aValue, CSPDirective aDir);
 bool CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey);
 bool CSP_IsQuotelessKeyword(const nsAString& aKey);
 CSPDirective CSP_ContentTypeToDirective(nsContentPolicyType aType);
 
 class nsCSPSrcVisitor;
 
+void CSP_PercentDecodeStr(const nsAString& aEncStr, nsAString& outDecStr);
+
 /* =============== nsCSPSrc ================== */
 
 class nsCSPBaseSrc {
   public:
     nsCSPBaseSrc();
     virtual ~nsCSPBaseSrc();
 
     virtual bool permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,