blob: 842884628aea409c223cea32402cf09c5834ab05 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_SAFE_BROWSING_CLOUD_CONTENT_SCANNING_BINARY_UPLOAD_SERVICE_H_
#define CHROME_BROWSER_SAFE_BROWSING_CLOUD_CONTENT_SCANNING_BINARY_UPLOAD_SERVICE_H_
#include <list>
#include <memory>
#include <string>
#include <unordered_map>
#include "base/callback.h"
#include "base/callback_forward.h"
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/timer/timer.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/binary_fcm_service.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/multipart_uploader.h"
#include "components/safe_browsing/core/proto/webprotect.pb.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
class Profile;
namespace safe_browsing {
// This class encapsulates the process of uploading a file for deep scanning,
// and asynchronously retrieving a verdict.
class BinaryUploadService {
public:
// The maximum size of data that can be uploaded via this service.
constexpr static size_t kMaxUploadSizeBytes = 50 * 1024 * 1024; // 50 MB
BinaryUploadService(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
Profile* profile);
// This constructor is useful in tests, if you want to keep a reference to the
// service's |binary_fcm_service_|.
BinaryUploadService(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
std::unique_ptr<BinaryFCMService> binary_fcm_service);
virtual ~BinaryUploadService();
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class Result {
// Unknown result.
UNKNOWN = 0,
// The request succeeded.
SUCCESS = 1,
// The upload failed, for an unspecified reason.
UPLOAD_FAILURE = 2,
// The upload succeeded, but a response was not received before timing out.
TIMEOUT = 3,
// The file was too large to upload.
FILE_TOO_LARGE = 4,
// The BinaryUploadService failed to get an InstanceID token.
FAILED_TO_GET_TOKEN = 5,
// The user is unauthorized to make the request.
UNAUTHORIZED = 6,
kMaxValue = UNAUTHORIZED,
};
// Callbacks used to pass along the results of scanning. The response protos
// will only be populated if the result is SUCCESS.
using Callback = base::OnceCallback<void(Result, DeepScanningClientResponse)>;
// A class to encapsulate the a request for upload. This class will provide
// all the functionality needed to generate a DeepScanningRequest, and
// subclasses will provide different sources of data to upload (e.g. file or
// string).
class Request {
public:
// |callback| will run on the UI thread.
explicit Request(Callback callback);
virtual ~Request();
Request(const Request&) = delete;
Request& operator=(const Request&) = delete;
Request(Request&&) = delete;
Request& operator=(Request&&) = delete;
// Structure of data returned in the callback to GetRequestData().
struct Data {
Data();
std::string contents;
};
// Asynchronously returns the file contents to upload.
// TODO(drubery): This could allocate up to kMaxUploadSizeBytes of memory
// for a large file upload. We should see how often that causes errors,
// and possibly implement some sort of streaming interface so we don't use
// so much memory.
//
// |result| is set to SUCCESS if getting the request data succeeded or
// some value describing the error.
using DataCallback = base::OnceCallback<void(Result, const Data&)>;
virtual void GetRequestData(DataCallback callback) = 0;
// Returns the metadata to upload, as a DeepScanningClientRequest.
const DeepScanningClientRequest& deep_scanning_request() const {
return deep_scanning_request_;
}
// Methods for modifying the DeepScanningClientRequest.
void set_request_dlp_scan(DlpDeepScanningClientRequest dlp_request);
void set_request_malware_scan(
MalwareDeepScanningClientRequest malware_request);
void set_fcm_token(const std::string& token);
void set_dm_token(const std::string& token);
void set_request_token(const std::string& token);
// Finish the request, with the given |result| and |response| from the
// server.
void FinishRequest(Result result, DeepScanningClientResponse response);
private:
DeepScanningClientRequest deep_scanning_request_;
Callback callback_;
};
// Upload the given file contents for deep scanning if the browser is
// authorized to upload data, otherwise queue the request.
virtual void MaybeUploadForDeepScanning(std::unique_ptr<Request> request);
// Indicates whether the browser is allowed to upload data.
using AuthorizationCallback = base::OnceCallback<void(bool)>;
void IsAuthorized(AuthorizationCallback callback);
// Run every callback in |authorization_callbacks_| and empty it.
void RunAuthorizationCallbacks();
// Resets |can_upload_data_|. Called every 24 hour by |timer_|.
void ResetAuthorizationData();
// Sets |can_upload_data_| for tests.
void SetAuthForTesting(bool authorized);
// Returns whether a download should be blocked based on file size alone. It
// checks the enterprise policy BlockLargeFileTransfer to decide this.
static bool ShouldBlockFileSize(size_t file_size);
// Returns the URL that requests are uploaded to.
static GURL GetUploadUrl();
private:
friend class BinaryUploadServiceTest;
// Upload the given file contents for deep scanning. The results will be
// returned asynchronously by calling |request|'s |callback|. This must be
// called on the UI thread.
void UploadForDeepScanning(std::unique_ptr<Request> request);
void OnGetInstanceID(Request* request, const std::string& token);
void OnGetRequestData(Request* request,
Result result,
const Request::Data& data);
void OnUploadComplete(Request* request,
bool success,
const std::string& response_data);
void OnGetResponse(Request* request, DeepScanningClientResponse response);
void MaybeFinishRequest(Request* request);
void OnTimeout(Request* request);
void FinishRequest(Request* request,
Result result,
DeepScanningClientResponse response);
bool IsActive(Request* request);
void MaybeUploadForDeepScanningCallback(std::unique_ptr<Request> request,
bool authorized);
// Callback once the response from the backend is received.
void ValidateDataUploadRequestCallback(BinaryUploadService::Result result,
DeepScanningClientResponse response);
void RecordRequestMetrics(Request* request,
Result result,
const DeepScanningClientResponse& response);
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
std::unique_ptr<BinaryFCMService> binary_fcm_service_;
Profile* const profile_;
// Resources associated with an in-progress request.
base::flat_map<Request*, std::unique_ptr<Request>> active_requests_;
base::flat_map<Request*, base::TimeTicks> start_times_;
base::flat_map<Request*, std::unique_ptr<base::OneShotTimer>> active_timers_;
base::flat_map<Request*, std::unique_ptr<MultipartUploadRequest>>
active_uploads_;
base::flat_map<Request*, std::string> active_tokens_;
base::flat_map<Request*, std::unique_ptr<MalwareDeepScanningVerdict>>
received_malware_verdicts_;
base::flat_map<Request*, std::unique_ptr<DlpDeepScanningVerdict>>
received_dlp_verdicts_;
// Indicates whether this browser can upload data.
// base::nullopt means the response from the backend has not been received
// yet.
// true means the response indicates data can be uploaded.
// false means the response indicates data cannot be uploaded.
base::Optional<bool> can_upload_data_ = base::nullopt;
// Callbacks waiting on IsAuthorized request.
std::list<base::OnceCallback<void(bool)>> authorization_callbacks_;
// Indicates if this service is waiting on the backend to validate event
// reporting. Used to avoid spamming the backend.
bool pending_validate_data_upload_request_ = false;
// Ensures we validate the browser is registered with the backend every 24
// hours.
base::RepeatingTimer timer_;
base::WeakPtrFactory<BinaryUploadService> weakptr_factory_;
};
} // namespace safe_browsing
#endif // CHROME_BROWSER_SAFE_BROWSING_CLOUD_CONTENT_SCANNING_BINARY_UPLOAD_SERVICE_H_