1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
|
// 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.
#ifndef CHROME_BROWSER_ENTERPRISE_CONNECTORS_ANALYSIS_LOCAL_BINARY_UPLOAD_SERVICE_H_
#define CHROME_BROWSER_ENTERPRISE_CONNECTORS_ANALYSIS_LOCAL_BINARY_UPLOAD_SERVICE_H_
#include <map>
#include <memory>
#include <set>
#include <vector>
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/enterprise/connectors/analysis/content_analysis_sdk_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h"
#include "components/device_signals/core/common/mojom/system_signals.mojom.h"
namespace enterprise_connectors {
// This class encapsulates the process of sending a file to local content
// analysis agents for deep scanning and asynchronously retrieving a verdict.
// This class runs on the UI thread.
class LocalBinaryUploadService : public safe_browsing::BinaryUploadService {
public:
// the maximum number of concurrently active requests to the local content
// analysis agent.
static constexpr size_t kMaxActiveCount = 5;
// The maximum number of reconnection retries chrome will attempt when an
// error occurs with the agent communication. Once this is reached chrome
// will no longer attempt to connect to the agent until it restarts.
static constexpr size_t kMaxRetryCount = 1;
// The maximum amount of time chrome will wait for a verdict from the local
// content analysis agent.
static constexpr base::TimeDelta kScanningTimeout = base::Minutes(5);
// Keeps track and owns requests sent to the local agent for deep scanning.
// When RequestInfo is created a timer is started to handle agent timeouts.
// If the timer expires before a response is returned for this requesdt, then
// LocalBinaryUploadService will respond to the request with an
// UPLOAD_FAILURE and will send an ack back to the agent that it took too
// long.
//
// The move ctor and dtor are declared noexcept so that when placed inside
// a vector<>, the move ctor is used instead of the copy ctor (which is
// deleted).
struct RequestInfo {
RequestInfo(std::unique_ptr<Request> request, base::OnceClosure closure);
RequestInfo(const RequestInfo& other) = delete;
RequestInfo(RequestInfo&& other) noexcept;
RequestInfo& operator=(const RequestInfo& other) = delete;
RequestInfo& operator=(RequestInfo&& other) noexcept;
~RequestInfo() noexcept;
base::TimeTicks started_at;
std::unique_ptr<Request> request;
std::unique_ptr<base::OneShotTimer> timer;
};
explicit LocalBinaryUploadService(Profile* profile);
~LocalBinaryUploadService() override;
// Send the given file contents to local partners for deep scanning.
void MaybeUploadForDeepScanning(std::unique_ptr<Request> request) override;
void MaybeAcknowledge(std::unique_ptr<Ack> ack) override;
void MaybeCancelRequests(std::unique_ptr<CancelRequests> cancel) override;
base::WeakPtr<BinaryUploadService> AsWeakPtr() override;
size_t GetActiveRequestCountForTesting() const {
return active_requests_.size();
}
size_t GetPendingRequestCountForTesting() const {
return pending_requests_.size();
}
const std::map<Request::Id, RequestInfo>& GetActiveRequestsForTesting()
const {
return active_requests_;
}
const std::vector<RequestInfo>& GetPendingRequestsForTesting() const {
return pending_requests_;
}
void OnTimeoutForTesting(Request::Id id) { OnTimeout(id); }
protected:
// Map to keep track of whether the agent's authenticity has been
// verified and if a check is in progress. If a given agent's `config` is
// not in the map, then the agent is not verified and no verification is
// in progress. If the `config` is in the map and the value is false,
// an agent verification is in progress. If the `config` is in the map
// and the value is true, the agent has been verified.
using AgentVerifiedMap =
std::map<content_analysis::sdk::Client::Config,
bool,
decltype(ContentAnalysisSdkManager::CompareConfig)>;
AgentVerifiedMap& GetAgentVerifiedMapForTesting() {
return is_agent_verified_;
}
// Gets the SystemSignalsService to extract the subject name from the
// agent. This method is virtual to allow overriding in tests.
virtual device_signals::mojom::SystemSignalsService*
GetSystemSignalsService();
// Starts verification of the agent specified by the given config. This
// method virtual so that tests can override the dependency on
// SystemsignalsServiceHost.
virtual void StartAgentVerification(
const content_analysis::sdk::Client::Config& config,
base::span<const char* const> subject_names);
// Called when the verification of the agent specified by the given config
// is complete.
void OnFileSystemSignals(
content_analysis::sdk::Client::Config config,
base::span<const char* const> subject_names,
const std::vector<device_signals::FileSystemItem>& items);
// Determines if the authenticity of the agent specified by the given config
// has been or is being verified. This method virtual so that tests can
// override the dependency on SystemsignalsServiceHost.
virtual bool IsAgentVerified(
const content_analysis::sdk::Client::Config& config);
virtual bool IsAgentBeingVerified(
const content_analysis::sdk::Client::Config& config);
private:
// If an error occurs with a client, reset its state.
void ResetClient(const content_analysis::sdk::Client::Config& config);
// Starts a local content analysis for the analysis request given by `id`.
void DoLocalContentAnalysis(Request::Id id,
Result result,
Request::Data data);
// Handles a response from the agent for a given request.
// `data` is not used directly by this function, but is needed to keep a
// scoped handle alive.
void HandleResponse(
scoped_refptr<ContentAnalysisSdkManager::WrappedClient> wrapped,
safe_browsing::BinaryUploadService::Request::Data data,
std::optional<content_analysis::sdk::ContentAnalysisResponse>
sdk_response);
// Starts a local content analysis ack request.
void DoSendAck(
scoped_refptr<ContentAnalysisSdkManager::WrappedClient> wrapped,
std::unique_ptr<safe_browsing::BinaryUploadService::Ack> ack);
// Starts a local content analysis cancel request.
void DoSendCancel(
scoped_refptr<ContentAnalysisSdkManager::WrappedClient> wrapped,
std::unique_ptr<safe_browsing::BinaryUploadService::CancelRequests>
cancel);
// Handles a response from the agent for a given ask or cancel.
void HandleAckResponse(
scoped_refptr<ContentAnalysisSdkManager::WrappedClient> wrapped,
int status);
void HandleCancelResponse(
scoped_refptr<ContentAnalysisSdkManager::WrappedClient> wrapped,
std::unique_ptr<safe_browsing::BinaryUploadService::CancelRequests>
cancel,
int status);
// In tests, this method can be overridden to know when a cancel request
// has been sent to the agent.
virtual void OnCancelRequestSent(std::unique_ptr<CancelRequests> cancel) {}
// Find the request that corresponds to the given response.
Request::Id FindRequestByToken(
const content_analysis::sdk::ContentAnalysisResponse& sdk_response);
// Move the next request from the pending list, if any, to the active
// list and process it.
void ProcessNextPendingRequest();
// Starts the request given by `id` that is already on the active
// list. If this function returns true the active request list is still
// valid. Otherwise the active request list has been cleared.
bool ProcessRequest(Request::Id id);
// Finish the request given by `id` and inform caller of the the resulting
// verdict.
void FinishRequest(Request::Id id,
Result result,
ContentAnalysisResponse response);
// Send a cancel request to the agent if there are no more active requests
// for the given action.
void SendCancelRequestsIfNeeded();
// Handles a timeout for the request given by `id`. The request could
// be in either the active or pending lists.
void OnTimeout(Request::Id id);
// If there haven't been too many retries, moves all requests from the active
// list to the pending list and queues up a task to reconnect to the agent.
// Once reconnected the requests will be retried in order.
//
// If there have been too many errors connecting to the agent, fail all
// active and pending requests. No more attempts will be made to reconnect
// to the agent and that all subsequent deep scan requests should fail
// automatically.
void RetryActiveRequestsSoonOrFailAllRequests(
const content_analysis::sdk::Client::Config& config);
// Attempts to reconnect to agent if a connection is not already in progress.
void StartConnectionRetry();
// Called when BinaryUploadService should attempt to reconnect and retry
// requests to the agent. This method is called by the timer set in
// RetryRequest().
void OnConnectionRetry();
// This method returns true when a connection retry timer is in progress.
bool ConnectionRetryInProgress();
void RecordRequestMetrics(
const RequestInfo& info,
Result result,
const enterprise_connectors::ContentAnalysisResponse& response);
raw_ptr<Profile> profile_;
Request::Id::Generator request_id_generator_;
// Keeps track of outstanding requests sent to the agent.
std::map<Request::Id, RequestInfo> active_requests_;
// Keeps track of pending requests not yet sent.
std::vector<RequestInfo> pending_requests_;
// Pending cancel requests. Once all requests associated with a given
// action are sent, a cancel request can be sent. This is to ensure that
// chrome does not send requests for analysis for a given action after a
// cancel request has been sent.
std::set<std::unique_ptr<CancelRequests>> pending_cancel_requests_;
// Timer used to retry connection to agent.
base::OneShotTimer connection_retry_timer_;
// Number of times LBUS has retried to connect to agent. This count is
// reset once a successful connection is established.
size_t retry_count_ = 0;
// Map to keep track of whether the agent's authenticity has been
// verified and if a check is in progress.
AgentVerifiedMap is_agent_verified_;
// As the last data member of class.
base::WeakPtrFactory<LocalBinaryUploadService> factory_{this};
};
} // namespace enterprise_connectors
#endif // CHROME_BROWSER_ENTERPRISE_CONNECTORS_ANALYSIS_LOCAL_BINARY_UPLOAD_SERVICE_H_
|