blob: 4ec615cceaff45a0175fd43d44fe3aadc4da275a [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/direct_sockets/chrome_direct_sockets_delegate.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/url_constants.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/socket_permission_request.h"
#include "extensions/browser/process_map.h"
#include "extensions/common/api/sockets/sockets_manifest_data.h"
namespace {
using ProtocolType = content::DirectSocketsDelegate::ProtocolType;
using RequestDetails = content::DirectSocketsDelegate::RequestDetails;
bool ValidateAddressAndPortForChromeApp(const extensions::Extension* extension,
const RequestDetails& request) {
switch (request.protocol) {
case ProtocolType::kTcp:
return extensions::SocketsManifestData::CheckRequest(
extension,
/*request=*/{content::SocketPermissionRequest::TCP_CONNECT,
request.address, request.port});
case ProtocolType::kConnectedUdp:
return extensions::SocketsManifestData::CheckRequest(
extension,
/*request=*/{content::SocketPermissionRequest::UDP_SEND_TO,
request.address, request.port});
case ProtocolType::kBoundUdp:
// For kBoundUdp we check both UDP_BIND for the given |address| and
// |port| as well as ensure that UDP_SEND_TO allows routing packets
// anywhere. '*' is the wildcard address, 0 is the wildcard port.
return extensions::SocketsManifestData::CheckRequest(
extension,
/*request=*/{content::SocketPermissionRequest::UDP_BIND,
request.address, request.port}) &&
extensions::SocketsManifestData::CheckRequest(
extension,
/*request=*/{content::SocketPermissionRequest::UDP_SEND_TO,
/*host=*/"*", /*port=*/0});
case ProtocolType::kTcpServer:
return extensions::SocketsManifestData::CheckRequest(
extension, /*request=*/{content::SocketPermissionRequest::TCP_LISTEN,
request.address, request.port});
}
}
bool ValidateAddressAndPortForIwa(const RequestDetails& request) {
switch (request.protocol) {
case ProtocolType::kTcp:
case ProtocolType::kConnectedUdp:
return true;
case ProtocolType::kBoundUdp:
// Port 0 indicates automatic port allocation.
// Ports below 1024 are usually system ports and should not be exposed.
return request.port == 0 || request.port >= 1024;
case ProtocolType::kTcpServer:
// Port 0 indicates automatic port allocation.
// Ports below 1024 are usually system ports and should not be exposed.
// Port numbers between 1024 and 32767 are usually specific to selected
// apps (which predominantly communicate over TCP).
return request.port == 0 || request.port >= 32768;
}
}
bool IsContentSettingAllowedForUrl(content::BrowserContext* browser_context,
const GURL& url,
ContentSettingsType content_setting) {
return HostContentSettingsMapFactory::GetForProfile(browser_context)
->GetContentSetting(url, url, content_setting) ==
CONTENT_SETTING_ALLOW;
}
} // namespace
bool ChromeDirectSocketsDelegate::ValidateRequest(
content::RenderFrameHost& rfh,
const RequestDetails& request) {
// If we're running an extension, follow the chrome.sockets.* permission
// model.
if (const extensions::Extension* extension =
extensions::ProcessMap::Get(rfh.GetBrowserContext())
->GetEnabledExtensionByProcessID(
rfh.GetProcess()->GetDeprecatedID())) {
return ValidateAddressAndPortForChromeApp(extension, request);
}
const GURL& url = rfh.GetMainFrame()->GetLastCommittedURL();
if (!IsContentSettingAllowedForUrl(rfh.GetBrowserContext(), url,
ContentSettingsType::DIRECT_SOCKETS)) {
return false;
}
if (url.SchemeIs(chrome::kIsolatedAppScheme)) {
return ValidateAddressAndPortForIwa(request);
}
return false;
}
bool ChromeDirectSocketsDelegate::ValidateRequestForSharedWorker(
content::BrowserContext* browser_context,
const GURL& shared_worker_url,
const RequestDetails& request) {
return IsContentSettingAllowedForUrl(browser_context, shared_worker_url,
ContentSettingsType::DIRECT_SOCKETS) &&
ValidateAddressAndPortForIwa(request);
}
bool ChromeDirectSocketsDelegate::ValidateRequestForServiceWorker(
content::BrowserContext* browser_context,
const url::Origin& origin,
const RequestDetails& request) {
return IsContentSettingAllowedForUrl(browser_context, origin.GetURL(),
ContentSettingsType::DIRECT_SOCKETS) &&
ValidateAddressAndPortForIwa(request);
}
void ChromeDirectSocketsDelegate::RequestPrivateNetworkAccess(
content::RenderFrameHost& rfh,
base::OnceCallback<void(bool)> callback) {
// No additional rules for Chrome Apps.
if (extensions::ProcessMap::Get(rfh.GetBrowserContext())
->Contains(rfh.GetProcess()->GetDeprecatedID())) {
std::move(callback).Run(/*allow_access=*/true);
return;
}
// TODO(crbug.com/368266657): Show a permission prompt for DS-PNA &
// ponder whether this requires transient activation.
std::move(callback).Run(IsContentSettingAllowedForUrl(
rfh.GetBrowserContext(), rfh.GetMainFrame()->GetLastCommittedURL(),
ContentSettingsType::DIRECT_SOCKETS_PRIVATE_NETWORK_ACCESS));
}
bool ChromeDirectSocketsDelegate::IsPrivateNetworkAccessAllowedForSharedWorker(
content::BrowserContext* browser_context,
const GURL& shared_worker_url) {
return IsContentSettingAllowedForUrl(
browser_context, shared_worker_url,
ContentSettingsType::DIRECT_SOCKETS_PRIVATE_NETWORK_ACCESS);
}
bool ChromeDirectSocketsDelegate::IsPrivateNetworkAccessAllowedForServiceWorker(
content::BrowserContext* browser_context,
const url::Origin& origin) {
const GURL& url = origin.GetURL();
return IsContentSettingAllowedForUrl(
browser_context, url,
ContentSettingsType::DIRECT_SOCKETS_PRIVATE_NETWORK_ACCESS);
}