summaryrefslogtreecommitdiffstats
path: root/content/browser/media/webrtc/webrtc_identity_store.cc
diff options
context:
space:
mode:
Diffstat (limited to 'content/browser/media/webrtc/webrtc_identity_store.cc')
-rw-r--r--content/browser/media/webrtc/webrtc_identity_store.cc325
1 files changed, 325 insertions, 0 deletions
diff --git a/content/browser/media/webrtc/webrtc_identity_store.cc b/content/browser/media/webrtc/webrtc_identity_store.cc
new file mode 100644
index 0000000..a5b91ad
--- /dev/null
+++ b/content/browser/media/webrtc/webrtc_identity_store.cc
@@ -0,0 +1,325 @@
+// Copyright 2013 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.
+
+#include "content/browser/media/webrtc/webrtc_identity_store.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/rand_util.h"
+#include "base/threading/worker_pool.h"
+#include "content/browser/media/webrtc/webrtc_identity_store_backend.h"
+#include "content/public/browser/browser_thread.h"
+#include "crypto/rsa_private_key.h"
+#include "net/base/net_errors.h"
+#include "net/cert/x509_util.h"
+#include "url/gurl.h"
+
+namespace content {
+
+struct WebRTCIdentityRequestResult {
+ WebRTCIdentityRequestResult(int error,
+ const std::string& certificate,
+ const std::string& private_key)
+ : error(error), certificate(certificate), private_key(private_key) {}
+
+ int error;
+ std::string certificate;
+ std::string private_key;
+};
+
+// Generates a new identity using |common_name| which expires after
+// |validity_period| and returns the result in |result|.
+static void GenerateIdentityWorker(const std::string& common_name,
+ base::TimeDelta validity_period,
+ WebRTCIdentityRequestResult* result) {
+ result->error = net::OK;
+ int serial_number = base::RandInt(0, std::numeric_limits<int>::max());
+
+ scoped_ptr<crypto::RSAPrivateKey> key;
+ base::Time now = base::Time::Now();
+ bool success = net::x509_util::CreateKeyAndSelfSignedCert(
+ "CN=" + common_name,
+ serial_number,
+ now,
+ now + validity_period,
+ &key,
+ &result->certificate);
+
+ if (!success) {
+ DLOG(ERROR) << "Unable to create x509 cert for client";
+ result->error = net::ERR_SELF_SIGNED_CERT_GENERATION_FAILED;
+ return;
+ }
+
+ std::vector<uint8_t> private_key_info;
+ if (!key->ExportPrivateKey(&private_key_info)) {
+ DLOG(ERROR) << "Unable to export private key";
+ result->error = net::ERR_PRIVATE_KEY_EXPORT_FAILED;
+ return;
+ }
+
+ result->private_key =
+ std::string(private_key_info.begin(), private_key_info.end());
+}
+
+class WebRTCIdentityRequestHandle;
+
+// The class represents an identity request internal to WebRTCIdentityStore.
+// It has a one-to-many mapping to the external version of the request,
+// WebRTCIdentityRequestHandle, i.e. multiple identical external requests are
+// combined into one internal request.
+// It's deleted automatically when the request is completed.
+class WebRTCIdentityRequest {
+ public:
+ WebRTCIdentityRequest(const GURL& origin,
+ const std::string& identity_name,
+ const std::string& common_name,
+ bool enable_cache)
+ : origin_(origin),
+ identity_name_(identity_name),
+ common_name_(common_name),
+ enable_cache_(enable_cache) {}
+
+ void Cancel(WebRTCIdentityRequestHandle* handle) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (callbacks_.find(handle) == callbacks_.end())
+ return;
+ callbacks_.erase(handle);
+ }
+
+ bool enable_cache() const { return enable_cache_; }
+
+ private:
+ friend class WebRTCIdentityStore;
+
+ void AddCallback(WebRTCIdentityRequestHandle* handle,
+ const WebRTCIdentityStore::CompletionCallback& callback) {
+ DCHECK(callbacks_.find(handle) == callbacks_.end());
+ callbacks_[handle] = callback;
+ }
+
+ // This method deletes "this" and no one should access it after the request
+ // completes.
+ // We do not use base::Owned to tie its lifetime to the callback for
+ // WebRTCIdentityStoreBackend::FindIdentity, because it needs to live longer
+ // than that if the identity does not exist in DB.
+ void Post(const WebRTCIdentityRequestResult& result) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end();
+ ++it)
+ it->second.Run(result.error, result.certificate, result.private_key);
+ delete this;
+ }
+
+ GURL origin_;
+ std::string identity_name_;
+ std::string common_name_;
+ typedef std::map<WebRTCIdentityRequestHandle*,
+ WebRTCIdentityStore::CompletionCallback> CallbackMap;
+ CallbackMap callbacks_;
+ bool enable_cache_;
+};
+
+// The class represents an identity request which calls back to the external
+// client when the request completes.
+// Its lifetime is tied with the Callback held by the corresponding
+// WebRTCIdentityRequest.
+class WebRTCIdentityRequestHandle {
+ public:
+ WebRTCIdentityRequestHandle(
+ WebRTCIdentityStore* store,
+ const WebRTCIdentityStore::CompletionCallback& callback)
+ : request_(NULL), callback_(callback) {}
+
+ private:
+ friend class WebRTCIdentityStore;
+
+ // Cancel the request. Does nothing if the request finished or was already
+ // cancelled.
+ void Cancel() {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (!request_)
+ return;
+
+ callback_.Reset();
+ WebRTCIdentityRequest* request = request_;
+ request_ = NULL;
+ // "this" will be deleted after the following call, because "this" is
+ // owned by the Callback held by |request|.
+ request->Cancel(this);
+ }
+
+ void OnRequestStarted(WebRTCIdentityRequest* request) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ DCHECK(request);
+ request_ = request;
+ }
+
+ void OnRequestComplete(int error,
+ const std::string& certificate,
+ const std::string& private_key) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ DCHECK(request_);
+ request_ = NULL;
+ base::ResetAndReturn(&callback_).Run(error, certificate, private_key);
+ }
+
+ WebRTCIdentityRequest* request_;
+ WebRTCIdentityStore::CompletionCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebRTCIdentityRequestHandle);
+};
+
+WebRTCIdentityStore::WebRTCIdentityStore(const base::FilePath& path,
+ storage::SpecialStoragePolicy* policy)
+ : validity_period_(base::TimeDelta::FromDays(30)),
+ task_runner_(base::WorkerPool::GetTaskRunner(true)),
+ backend_(new WebRTCIdentityStoreBackend(path, policy, validity_period_)) {
+ }
+
+WebRTCIdentityStore::~WebRTCIdentityStore() { backend_->Close(); }
+
+base::Closure WebRTCIdentityStore::RequestIdentity(
+ const GURL& origin,
+ const std::string& identity_name,
+ const std::string& common_name,
+ const CompletionCallback& callback,
+ bool enable_cache) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ WebRTCIdentityRequest* request =
+ FindRequest(origin, identity_name, common_name);
+ // If there is no identical request in flight, create a new one, queue it,
+ // and make the backend request.
+ if (!request) {
+ request = new WebRTCIdentityRequest(origin, identity_name, common_name,
+ enable_cache);
+ // In either case, |request| will delete itself after the result is posted.
+ if (enable_cache) {
+ if (!backend_->FindIdentity(
+ origin, identity_name, common_name,
+ base::Bind(&WebRTCIdentityStore::BackendFindCallback, this,
+ request))) {
+ // Bail out if the backend failed to start the task.
+ delete request;
+ return base::Closure();
+ }
+ } else {
+ GenerateNewIdentity(request);
+ }
+ in_flight_requests_.push_back(request);
+ }
+
+ WebRTCIdentityRequestHandle* handle =
+ new WebRTCIdentityRequestHandle(this, callback);
+
+ request->AddCallback(
+ handle,
+ base::Bind(&WebRTCIdentityRequestHandle::OnRequestComplete,
+ base::Owned(handle)));
+ handle->OnRequestStarted(request);
+ return base::Bind(&WebRTCIdentityRequestHandle::Cancel,
+ base::Unretained(handle));
+}
+
+void WebRTCIdentityStore::DeleteBetween(base::Time delete_begin,
+ base::Time delete_end,
+ const base::Closure& callback) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ backend_->DeleteBetween(delete_begin, delete_end, callback);
+}
+
+void WebRTCIdentityStore::SetValidityPeriodForTesting(
+ base::TimeDelta validity_period) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ validity_period_ = validity_period;
+ backend_->SetValidityPeriodForTesting(validity_period);
+}
+
+void WebRTCIdentityStore::SetTaskRunnerForTesting(
+ const scoped_refptr<base::TaskRunner>& task_runner) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ task_runner_ = task_runner;
+}
+
+void WebRTCIdentityStore::BackendFindCallback(WebRTCIdentityRequest* request,
+ int error,
+ const std::string& certificate,
+ const std::string& private_key) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (error == net::OK) {
+ DVLOG(2) << "Identity found in DB.";
+ WebRTCIdentityRequestResult result(error, certificate, private_key);
+ PostRequestResult(request, result);
+ return;
+ }
+ GenerateNewIdentity(request);
+}
+
+void WebRTCIdentityStore::GenerateIdentityCallback(
+ WebRTCIdentityRequest* request,
+ WebRTCIdentityRequestResult* result) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (result->error == net::OK && request->enable_cache()) {
+ DVLOG(2) << "New identity generated and added to the backend.";
+ backend_->AddIdentity(request->origin_,
+ request->identity_name_,
+ request->common_name_,
+ result->certificate,
+ result->private_key);
+ }
+ PostRequestResult(request, *result);
+}
+
+void WebRTCIdentityStore::PostRequestResult(
+ WebRTCIdentityRequest* request,
+ const WebRTCIdentityRequestResult& result) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ // Removes the in flight request from the queue.
+ for (size_t i = 0; i < in_flight_requests_.size(); ++i) {
+ if (in_flight_requests_[i] == request) {
+ in_flight_requests_.erase(in_flight_requests_.begin() + i);
+ break;
+ }
+ }
+ // |request| will be deleted after this call.
+ request->Post(result);
+}
+
+// Find an identical request from the in flight requests.
+WebRTCIdentityRequest* WebRTCIdentityStore::FindRequest(
+ const GURL& origin,
+ const std::string& identity_name,
+ const std::string& common_name) {
+ for (size_t i = 0; i < in_flight_requests_.size(); ++i) {
+ if (in_flight_requests_[i]->origin_ == origin &&
+ in_flight_requests_[i]->identity_name_ == identity_name &&
+ in_flight_requests_[i]->common_name_ == common_name) {
+ return in_flight_requests_[i];
+ }
+ }
+ return NULL;
+}
+
+void WebRTCIdentityStore::GenerateNewIdentity(WebRTCIdentityRequest* request) {
+ WebRTCIdentityRequestResult* result =
+ new WebRTCIdentityRequestResult(0, "", "");
+ if (!task_runner_->PostTaskAndReply(
+ FROM_HERE, base::Bind(&GenerateIdentityWorker, request->common_name_,
+ validity_period_, result),
+ base::Bind(&WebRTCIdentityStore::GenerateIdentityCallback, this,
+ request, base::Owned(result)))) {
+ // Completes the request with error if failed to post the task.
+ WebRTCIdentityRequestResult result(net::ERR_UNEXPECTED, "", "");
+ PostRequestResult(request, result);
+ }
+}
+
+} // namespace content