Bug 1638358 - Cookie Schemeful Same-Site - part 5 - schemeful comparison, r=mayhemer

Differential Revision: https://phabricator.services.mozilla.com/D75629
This commit is contained in:
Andrea Marchesini 2020-06-01 16:49:03 +00:00
Родитель 8170437e2e
Коммит 97f0db059a
14 изменённых файлов: 267 добавлений и 12 удалений

Просмотреть файл

@ -13,7 +13,13 @@ add_task(async function setup() {
// make sure userContext is enabled.
await SpecialPowers.pushPrefEnv({
set: [["privacy.userContext.enabled", true]],
set: [
["privacy.userContext.enabled", true],
// This test does a redirect from https to http and it checks the
// cookies. This is incompatible with the cookie sameSite schemeful
// feature and we need to disable it.
["network.cookie.sameSite.schemeful", false],
],
});
});

Просмотреть файл

@ -855,6 +855,19 @@ NS_IMETHODIMP BasePrincipal::GetIsIpAddress(bool* aIsIpAddress) {
return NS_OK;
}
NS_IMETHODIMP
BasePrincipal::GetScheme(nsACString& aScheme) {
aScheme.Truncate();
nsCOMPtr<nsIURI> prinURI;
nsresult rv = GetURI(getter_AddRefs(prinURI));
if (NS_FAILED(rv) || !prinURI) {
return NS_OK;
}
return prinURI->GetScheme(aScheme);
}
NS_IMETHODIMP
BasePrincipal::SchemeIs(const char* aScheme, bool* aResult) {
*aResult = false;

Просмотреть файл

@ -122,6 +122,7 @@ class BasePrincipal : public nsJSPrincipals {
NS_IMETHOD GetIsContentPrincipal(bool* aResult) override;
NS_IMETHOD GetIsExpandedPrincipal(bool* aResult) override;
NS_IMETHOD GetIsSystemPrincipal(bool* aResult) override;
NS_IMETHOD GetScheme(nsACString& aScheme) override;
NS_IMETHOD SchemeIs(const char* aScheme, bool* aResult) override;
NS_IMETHOD IsURIInPrefList(const char* aPref, bool* aResult) override;
NS_IMETHOD IsL10nAllowed(nsIURI* aURI, bool* aResult) override;

Просмотреть файл

@ -287,7 +287,12 @@ interface nsIPrincipal : nsISerializable
/* Returns the Spec of the Principals URI with
* user/pass/ref/query stripped for privacy and spoof prevention
*/
readonly attribute ACString exposableSpec;
readonly attribute ACString exposableSpec;
/**
* Return the scheme of the principals URI
*/
readonly attribute ACString scheme;
/**
* Checks if the Principal's URI Scheme matches with the parameter

Просмотреть файл

@ -7505,6 +7505,11 @@
value: @IS_NIGHTLY_BUILD@
mirror: always
- name: network.cookie.sameSite.schemeful
type: bool
value: @IS_NIGHTLY_BUILD@
mirror: always
- name: network.cookie.thirdparty.sessionOnly
type: bool
value: false

Просмотреть файл

@ -456,5 +456,65 @@ bool CookieCommons::ShouldIncludeCrossSiteCookieForDocument(Cookie* aCookie) {
return sameSiteAttr == nsICookie::SAMESITE_NONE;
}
// static
bool CookieCommons::MaybeCompareScheme(Cookie* aCookie,
nsICookie::schemeType aSchemeType) {
if (!StaticPrefs::network_cookie_sameSite_schemeful()) {
return true;
}
// This is an old cookie without a scheme yet. Let's consider it valid.
if (aCookie->SchemeMap() == nsICookie::SCHEME_UNSET) {
return true;
}
return !!(aCookie->SchemeMap() & aSchemeType);
}
// static
nsICookie::schemeType CookieCommons::URIToSchemeType(nsIURI* aURI) {
MOZ_ASSERT(aURI);
nsAutoCString scheme;
nsresult rv = aURI->GetScheme(scheme);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nsICookie::SCHEME_UNSET;
}
return SchemeToSchemeType(scheme);
}
// static
nsICookie::schemeType CookieCommons::PrincipalToSchemeType(
nsIPrincipal* aPrincipal) {
MOZ_ASSERT(aPrincipal);
nsAutoCString scheme;
nsresult rv = aPrincipal->GetScheme(scheme);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nsICookie::SCHEME_UNSET;
}
return SchemeToSchemeType(scheme);
}
// static
nsICookie::schemeType CookieCommons::SchemeToSchemeType(
const nsACString& aScheme) {
if (aScheme.Equals("https")) {
return nsICookie::SCHEME_HTTPS;
}
if (aScheme.Equals("http")) {
return nsICookie::SCHEME_HTTP;
}
if (aScheme.Equals("file")) {
return nsICookie::SCHEME_FILE;
}
MOZ_CRASH("Unsupported scheme type");
}
} // namespace net
} // namespace mozilla

Просмотреть файл

@ -108,6 +108,15 @@ class CookieCommons final {
nsIChannel* aChannel);
static bool ShouldIncludeCrossSiteCookieForDocument(Cookie* aCookie);
static bool MaybeCompareScheme(Cookie* aCookie,
nsICookie::schemeType aSchemeType);
static nsICookie::schemeType URIToSchemeType(nsIURI* aURI);
static nsICookie::schemeType PrincipalToSchemeType(nsIPrincipal* aPrincipal);
static nsICookie::schemeType SchemeToSchemeType(const nsACString& aScheme);
};
} // namespace net

Просмотреть файл

@ -284,6 +284,9 @@ CookieService::GetCookieStringFromDocument(Document* aDocument,
nsCOMPtr<nsIPrincipal> principal = aDocument->EffectiveStoragePrincipal();
nsICookie::schemeType schemeType =
CookieCommons::PrincipalToSchemeType(principal);
CookieStorage* storage = PickStorage(principal->OriginAttributesRef());
nsAutoCString baseDomain;
@ -352,6 +355,10 @@ CookieService::GetCookieStringFromDocument(Document* aDocument,
continue;
}
if (!CookieCommons::MaybeCompareScheme(cookie, schemeType)) {
continue;
}
// if the nsIURI path doesn't match the cookie path, don't send it back
if (!CookieCommons::PathMatches(cookie, pathFromURI)) {
continue;
@ -849,6 +856,8 @@ void CookieService::GetCookiesForURI(
baseDomainFromURI);
NS_ENSURE_SUCCESS_VOID(rv);
nsICookie::schemeType schemeType = CookieCommons::URIToSchemeType(aHostURI);
// check default prefs
uint32_t rejectedReason = aRejectedReason;
uint32_t priorCookieCount = storage->CountCookiesFromHost(
@ -913,6 +922,11 @@ void CookieService::GetCookiesForURI(
continue;
}
// The scheme doesn't match.
if (!CookieCommons::MaybeCompareScheme(cookie, schemeType)) {
continue;
}
if (aHttpBound && aIsSameSiteForeign &&
!ProcessSameSiteCookieForForeignRequest(
aChannel, cookie, aIsSafeTopLevelNav, laxByDefault)) {
@ -977,15 +991,7 @@ bool CookieService::CanSetCookie(
// init expiryTime such that session cookies won't prematurely expire
aCookieData.expiry() = INT64_MAX;
if (aHostURI->SchemeIs("https")) {
aCookieData.schemeMap() = nsICookie::SCHEME_HTTPS;
} else if (aHostURI->SchemeIs("http")) {
aCookieData.schemeMap() = nsICookie::SCHEME_HTTP;
} else if (aHostURI->SchemeIs("file")) {
aCookieData.schemeMap() = nsICookie::SCHEME_FILE;
} else {
MOZ_CRASH("Unsupported scheme type");
}
aCookieData.schemeMap() = CookieCommons::URIToSchemeType(aHostURI);
// aCookieHeader is an in/out param to point to the next cookie, if
// there is one. Save the present value for logging purposes

Просмотреть файл

@ -331,6 +331,9 @@ CookieServiceChild::GetCookieStringFromDocument(Document* aDocument,
nsCOMPtr<nsIPrincipal> principal = aDocument->EffectiveStoragePrincipal();
nsICookie::schemeType schemeType =
CookieCommons::PrincipalToSchemeType(principal);
nsAutoCString baseDomain;
nsresult rv = CookieCommons::GetBaseDomain(principal, baseDomain);
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -388,6 +391,10 @@ CookieServiceChild::GetCookieStringFromDocument(Document* aDocument,
continue;
}
if (!CookieCommons::MaybeCompareScheme(cookie, schemeType)) {
continue;
}
// if the nsIURI path doesn't match the cookie path, don't send it back
if (!CookieCommons::PathMatches(cookie, pathFromURI)) {
continue;

Просмотреть файл

@ -1,6 +1,17 @@
const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyServiceGetter(
Services,
"cookiemgr",
"@mozilla.org/cookiemanager;1",
"nsICookieManager"
);
function inChildProcess() {
return Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
}
@ -61,4 +72,122 @@ add_task(async _ => {
Ci.nsICookie.SCHEME_HTTP | Ci.nsICookie.SCHEME_HTTPS,
"HTTP + HTTPS Schemes"
);
Services.cookies.removeAll();
});
[true, false].forEach(schemefulComparison => {
add_task(async () => {
do_get_profile();
// Allow all cookies if the pref service is available in this process.
if (!inChildProcess()) {
Services.prefs.setBoolPref(
"network.cookie.sameSite.schemeful",
schemefulComparison
);
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
Services.prefs.setBoolPref(
"network.cookieJarSettings.unblocked_for_testing",
true
);
}
let cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService);
info("Let's set a cookie from HTTP example.org");
let uri = NetUtil.newURI("http://example.org/");
let principal = Services.scriptSecurityManager.createContentPrincipal(
uri,
{}
);
let channel = NetUtil.newChannel({
uri,
loadingPrincipal: principal,
securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
});
cs.setCookieStringFromHttp(uri, "a=b; sameSite=lax", channel);
let cookies = Services.cookies.getCookieStringFromHttp(uri, channel);
Assert.equal(cookies, "a=b", "Cookies match");
uri = NetUtil.newURI("https://example.org/");
principal = Services.scriptSecurityManager.createContentPrincipal(uri, {});
channel = NetUtil.newChannel({
uri,
loadingPrincipal: principal,
securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
});
cookies = Services.cookies.getCookieStringFromHttp(uri, channel);
if (schemefulComparison) {
Assert.equal(cookies, "", "No cookie for different scheme!");
} else {
Assert.equal(cookies, "a=b", "Cookie even for different scheme!");
}
cookies = Services.cookies.getCookieStringForPrincipal(principal);
if (schemefulComparison) {
Assert.equal(cookies, "", "No cookie for different scheme!");
} else {
Assert.equal(cookies, "a=b", "Cookie even for different scheme!");
}
Services.cookies.removeAll();
});
});
add_task(async _ => {
do_get_profile();
// Allow all cookies if the pref service is available in this process.
if (!inChildProcess()) {
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
Services.prefs.setBoolPref(
"network.cookieJarSettings.unblocked_for_testing",
true
);
}
info("Let's set a cookie without scheme");
Services.cookiemgr.add(
"example.org",
"/",
"a",
"b",
false,
false,
false,
Math.floor(Date.now() / 1000 + 1000),
{},
Ci.nsICookie.SAMESITE_LAX,
Ci.nsICookie.SCHEME_UNSET
);
let cookies = Services.cookies.getCookiesFromHost("example.org", {});
Assert.equal(cookies.length, 1, "We expect 1 cookie only");
Assert.equal(cookies[0].schemeMap, Ci.nsICookie.SCHEME_UNSET, "Unset scheme");
["https", "http"].forEach(scheme => {
let uri = NetUtil.newURI(scheme + "://example.org/");
let principal = Services.scriptSecurityManager.createContentPrincipal(
uri,
{}
);
let channel = NetUtil.newChannel({
uri,
loadingPrincipal: principal,
securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
});
cookies = Services.cookies.getCookieStringFromHttp(uri, channel);
Assert.equal(cookies, "a=b", "Cookie for unset scheme");
});
Services.cookies.removeAll();
});

Просмотреть файл

@ -199,6 +199,7 @@ void InitPrefs(nsIPrefBranch* aPrefBranch) {
Preferences::SetBool("network.cookie.sameSite.laxByDefault", false);
Preferences::SetBool("network.cookieJarSettings.unblocked_for_testing", true);
Preferences::SetBool("dom.securecontext.whitelist_onions", false);
Preferences::SetBool("network.cookie.sameSite.schemeful", false);
}
TEST(TestCookie, TestCookieMain)
@ -725,6 +726,7 @@ TEST(TestCookie, TestCookieMain)
GetACookieNoHttp(cookieService, "https://www.security.test/", cookie);
EXPECT_TRUE(CheckResult(cookie.get(), MUST_EQUAL,
"test_modify_cookie=non-security-cookie"));
// Test the non-security cookie can set when domain or path not same to secure
// cookie of same name.
SetACookie(cookieService, "https://www.security.test/", "test=security3");
@ -735,6 +737,11 @@ TEST(TestCookie, TestCookieMain)
GetACookieNoHttp(cookieService, "http://www.security.test/", cookie);
EXPECT_TRUE(CheckResult(cookie.get(), MUST_CONTAIN, "test=non-security2"));
Preferences::SetBool("network.cookie.sameSite.schemeful", true);
GetACookieNoHttp(cookieService, "http://www.security.test/", cookie);
EXPECT_FALSE(CheckResult(cookie.get(), MUST_CONTAIN, "test=security3"));
Preferences::SetBool("network.cookie.sameSite.schemeful", false);
// *** nsICookieManager interface tests
nsCOMPtr<nsICookieManager> cookieMgr =
do_GetService(NS_COOKIEMANAGER_CONTRACTID, &rv0);

Просмотреть файл

@ -36,7 +36,12 @@ function setupTest(uri, domain, cookies, loads, headers) {
var prefSet = new Promise(resolve => {
SpecialPowers.pushPrefEnv(
{ set: [["network.cookie.cookieBehavior", 1]] },
{
set: [
["network.cookie.cookieBehavior", 1],
["network.cookie.sameSite.schemeful", false],
],
},
resolve
);
});

Просмотреть файл

@ -0,0 +1 @@
prefs: [network.cookie.sameSite.schemeful:false]

Просмотреть файл

@ -0,0 +1 @@
prefs: [network.cookie.sameSite.schemeful:false]