Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / media / webrtc_identity_store.cc
blob9867a2f69c6b7c099e82c819fa334458b9454d83
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/media/webrtc_identity_store.h"
7 #include <map>
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/logging.h"
12 #include "base/rand_util.h"
13 #include "base/threading/worker_pool.h"
14 #include "content/browser/media/webrtc_identity_store_backend.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "crypto/rsa_private_key.h"
17 #include "net/base/net_errors.h"
18 #include "net/cert/x509_util.h"
19 #include "url/gurl.h"
21 namespace content {
23 struct WebRTCIdentityRequestResult {
24 WebRTCIdentityRequestResult(int error,
25 const std::string& certificate,
26 const std::string& private_key)
27 : error(error), certificate(certificate), private_key(private_key) {}
29 int error;
30 std::string certificate;
31 std::string private_key;
34 // Generates a new identity using |common_name| which expires after
35 // |validity_period| and returns the result in |result|.
36 static void GenerateIdentityWorker(const std::string& common_name,
37 base::TimeDelta validity_period,
38 WebRTCIdentityRequestResult* result) {
39 result->error = net::OK;
40 int serial_number = base::RandInt(0, std::numeric_limits<int>::max());
42 scoped_ptr<crypto::RSAPrivateKey> key;
43 base::Time now = base::Time::Now();
44 bool success = net::x509_util::CreateKeyAndSelfSignedCert(
45 "CN=" + common_name,
46 serial_number,
47 now,
48 now + validity_period,
49 &key,
50 &result->certificate);
52 if (!success) {
53 DLOG(ERROR) << "Unable to create x509 cert for client";
54 result->error = net::ERR_SELF_SIGNED_CERT_GENERATION_FAILED;
55 return;
58 std::vector<uint8> private_key_info;
59 if (!key->ExportPrivateKey(&private_key_info)) {
60 DLOG(ERROR) << "Unable to export private key";
61 result->error = net::ERR_PRIVATE_KEY_EXPORT_FAILED;
62 return;
65 result->private_key =
66 std::string(private_key_info.begin(), private_key_info.end());
69 class WebRTCIdentityRequestHandle;
71 // The class represents an identity request internal to WebRTCIdentityStore.
72 // It has a one-to-many mapping to the external version of the request,
73 // WebRTCIdentityRequestHandle, i.e. multiple identical external requests are
74 // combined into one internal request.
75 // It's deleted automatically when the request is completed.
76 class WebRTCIdentityRequest {
77 public:
78 WebRTCIdentityRequest(const GURL& origin,
79 const std::string& identity_name,
80 const std::string& common_name,
81 bool enable_cache)
82 : origin_(origin),
83 identity_name_(identity_name),
84 common_name_(common_name),
85 enable_cache_(enable_cache) {}
87 void Cancel(WebRTCIdentityRequestHandle* handle) {
88 DCHECK_CURRENTLY_ON(BrowserThread::IO);
89 if (callbacks_.find(handle) == callbacks_.end())
90 return;
91 callbacks_.erase(handle);
94 bool enable_cache() const { return enable_cache_; }
96 private:
97 friend class WebRTCIdentityStore;
99 void AddCallback(WebRTCIdentityRequestHandle* handle,
100 const WebRTCIdentityStore::CompletionCallback& callback) {
101 DCHECK(callbacks_.find(handle) == callbacks_.end());
102 callbacks_[handle] = callback;
105 // This method deletes "this" and no one should access it after the request
106 // completes.
107 // We do not use base::Owned to tie its lifetime to the callback for
108 // WebRTCIdentityStoreBackend::FindIdentity, because it needs to live longer
109 // than that if the identity does not exist in DB.
110 void Post(const WebRTCIdentityRequestResult& result) {
111 DCHECK_CURRENTLY_ON(BrowserThread::IO);
112 for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end();
113 ++it)
114 it->second.Run(result.error, result.certificate, result.private_key);
115 delete this;
118 GURL origin_;
119 std::string identity_name_;
120 std::string common_name_;
121 typedef std::map<WebRTCIdentityRequestHandle*,
122 WebRTCIdentityStore::CompletionCallback> CallbackMap;
123 CallbackMap callbacks_;
124 bool enable_cache_;
127 // The class represents an identity request which calls back to the external
128 // client when the request completes.
129 // Its lifetime is tied with the Callback held by the corresponding
130 // WebRTCIdentityRequest.
131 class WebRTCIdentityRequestHandle {
132 public:
133 WebRTCIdentityRequestHandle(
134 WebRTCIdentityStore* store,
135 const WebRTCIdentityStore::CompletionCallback& callback)
136 : store_(store), request_(NULL), callback_(callback) {}
138 private:
139 friend class WebRTCIdentityStore;
141 // Cancel the request. Does nothing if the request finished or was already
142 // cancelled.
143 void Cancel() {
144 DCHECK_CURRENTLY_ON(BrowserThread::IO);
145 if (!request_)
146 return;
148 callback_.Reset();
149 WebRTCIdentityRequest* request = request_;
150 request_ = NULL;
151 // "this" will be deleted after the following call, because "this" is
152 // owned by the Callback held by |request|.
153 request->Cancel(this);
156 void OnRequestStarted(WebRTCIdentityRequest* request) {
157 DCHECK_CURRENTLY_ON(BrowserThread::IO);
158 DCHECK(request);
159 request_ = request;
162 void OnRequestComplete(int error,
163 const std::string& certificate,
164 const std::string& private_key) {
165 DCHECK_CURRENTLY_ON(BrowserThread::IO);
166 DCHECK(request_);
167 request_ = NULL;
168 base::ResetAndReturn(&callback_).Run(error, certificate, private_key);
171 WebRTCIdentityStore* store_;
172 WebRTCIdentityRequest* request_;
173 WebRTCIdentityStore::CompletionCallback callback_;
175 DISALLOW_COPY_AND_ASSIGN(WebRTCIdentityRequestHandle);
178 WebRTCIdentityStore::WebRTCIdentityStore(const base::FilePath& path,
179 storage::SpecialStoragePolicy* policy)
180 : validity_period_(base::TimeDelta::FromDays(30)),
181 task_runner_(base::WorkerPool::GetTaskRunner(true)),
182 backend_(new WebRTCIdentityStoreBackend(path, policy, validity_period_)) {
185 WebRTCIdentityStore::~WebRTCIdentityStore() { backend_->Close(); }
187 base::Closure WebRTCIdentityStore::RequestIdentity(
188 const GURL& origin,
189 const std::string& identity_name,
190 const std::string& common_name,
191 const CompletionCallback& callback,
192 bool enable_cache) {
193 DCHECK_CURRENTLY_ON(BrowserThread::IO);
194 WebRTCIdentityRequest* request =
195 FindRequest(origin, identity_name, common_name);
196 // If there is no identical request in flight, create a new one, queue it,
197 // and make the backend request.
198 if (!request) {
199 request = new WebRTCIdentityRequest(origin, identity_name, common_name,
200 enable_cache);
201 // In either case, |request| will delete itself after the result is posted.
202 if (enable_cache) {
203 if (!backend_->FindIdentity(
204 origin, identity_name, common_name,
205 base::Bind(&WebRTCIdentityStore::BackendFindCallback, this,
206 request))) {
207 // Bail out if the backend failed to start the task.
208 delete request;
209 return base::Closure();
211 } else {
212 GenerateNewIdentity(request);
214 in_flight_requests_.push_back(request);
217 WebRTCIdentityRequestHandle* handle =
218 new WebRTCIdentityRequestHandle(this, callback);
220 request->AddCallback(
221 handle,
222 base::Bind(&WebRTCIdentityRequestHandle::OnRequestComplete,
223 base::Owned(handle)));
224 handle->OnRequestStarted(request);
225 return base::Bind(&WebRTCIdentityRequestHandle::Cancel,
226 base::Unretained(handle));
229 void WebRTCIdentityStore::DeleteBetween(base::Time delete_begin,
230 base::Time delete_end,
231 const base::Closure& callback) {
232 DCHECK_CURRENTLY_ON(BrowserThread::IO);
233 backend_->DeleteBetween(delete_begin, delete_end, callback);
236 void WebRTCIdentityStore::SetValidityPeriodForTesting(
237 base::TimeDelta validity_period) {
238 DCHECK_CURRENTLY_ON(BrowserThread::IO);
239 validity_period_ = validity_period;
240 backend_->SetValidityPeriodForTesting(validity_period);
243 void WebRTCIdentityStore::SetTaskRunnerForTesting(
244 const scoped_refptr<base::TaskRunner>& task_runner) {
245 DCHECK_CURRENTLY_ON(BrowserThread::IO);
246 task_runner_ = task_runner;
249 void WebRTCIdentityStore::BackendFindCallback(WebRTCIdentityRequest* request,
250 int error,
251 const std::string& certificate,
252 const std::string& private_key) {
253 DCHECK_CURRENTLY_ON(BrowserThread::IO);
254 if (error == net::OK) {
255 DVLOG(2) << "Identity found in DB.";
256 WebRTCIdentityRequestResult result(error, certificate, private_key);
257 PostRequestResult(request, result);
258 return;
260 GenerateNewIdentity(request);
263 void WebRTCIdentityStore::GenerateIdentityCallback(
264 WebRTCIdentityRequest* request,
265 WebRTCIdentityRequestResult* result) {
266 DCHECK_CURRENTLY_ON(BrowserThread::IO);
267 if (result->error == net::OK && request->enable_cache()) {
268 DVLOG(2) << "New identity generated and added to the backend.";
269 backend_->AddIdentity(request->origin_,
270 request->identity_name_,
271 request->common_name_,
272 result->certificate,
273 result->private_key);
275 PostRequestResult(request, *result);
278 void WebRTCIdentityStore::PostRequestResult(
279 WebRTCIdentityRequest* request,
280 const WebRTCIdentityRequestResult& result) {
281 DCHECK_CURRENTLY_ON(BrowserThread::IO);
282 // Removes the in flight request from the queue.
283 for (size_t i = 0; i < in_flight_requests_.size(); ++i) {
284 if (in_flight_requests_[i] == request) {
285 in_flight_requests_.erase(in_flight_requests_.begin() + i);
286 break;
289 // |request| will be deleted after this call.
290 request->Post(result);
293 // Find an identical request from the in flight requests.
294 WebRTCIdentityRequest* WebRTCIdentityStore::FindRequest(
295 const GURL& origin,
296 const std::string& identity_name,
297 const std::string& common_name) {
298 for (size_t i = 0; i < in_flight_requests_.size(); ++i) {
299 if (in_flight_requests_[i]->origin_ == origin &&
300 in_flight_requests_[i]->identity_name_ == identity_name &&
301 in_flight_requests_[i]->common_name_ == common_name) {
302 return in_flight_requests_[i];
305 return NULL;
308 void WebRTCIdentityStore::GenerateNewIdentity(WebRTCIdentityRequest* request) {
309 WebRTCIdentityRequestResult* result =
310 new WebRTCIdentityRequestResult(0, "", "");
311 if (!task_runner_->PostTaskAndReply(
312 FROM_HERE, base::Bind(&GenerateIdentityWorker, request->common_name_,
313 validity_period_, result),
314 base::Bind(&WebRTCIdentityStore::GenerateIdentityCallback, this,
315 request, base::Owned(result)))) {
316 // Completes the request with error if failed to post the task.
317 WebRTCIdentityRequestResult result(net::ERR_UNEXPECTED, "", "");
318 PostRequestResult(request, result);
322 } // namespace content