Use multiline attribute to check for IA2_STATE_MULTILINE.
[chromium-blink-merge.git] / content / browser / media / webrtc_identity_store.cc
blob2cf8fb13c8a065d72310c64e5fa3a2588a6d2eed
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 : origin_(origin),
82 identity_name_(identity_name),
83 common_name_(common_name) {}
85 void Cancel(WebRTCIdentityRequestHandle* handle) {
86 DCHECK_CURRENTLY_ON(BrowserThread::IO);
87 if (callbacks_.find(handle) == callbacks_.end())
88 return;
89 callbacks_.erase(handle);
92 private:
93 friend class WebRTCIdentityStore;
95 void AddCallback(WebRTCIdentityRequestHandle* handle,
96 const WebRTCIdentityStore::CompletionCallback& callback) {
97 DCHECK(callbacks_.find(handle) == callbacks_.end());
98 callbacks_[handle] = callback;
101 // This method deletes "this" and no one should access it after the request
102 // completes.
103 // We do not use base::Owned to tie its lifetime to the callback for
104 // WebRTCIdentityStoreBackend::FindIdentity, because it needs to live longer
105 // than that if the identity does not exist in DB.
106 void Post(const WebRTCIdentityRequestResult& result) {
107 DCHECK_CURRENTLY_ON(BrowserThread::IO);
108 for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end();
109 ++it)
110 it->second.Run(result.error, result.certificate, result.private_key);
111 delete this;
114 GURL origin_;
115 std::string identity_name_;
116 std::string common_name_;
117 typedef std::map<WebRTCIdentityRequestHandle*,
118 WebRTCIdentityStore::CompletionCallback> CallbackMap;
119 CallbackMap callbacks_;
122 // The class represents an identity request which calls back to the external
123 // client when the request completes.
124 // Its lifetime is tied with the Callback held by the corresponding
125 // WebRTCIdentityRequest.
126 class WebRTCIdentityRequestHandle {
127 public:
128 WebRTCIdentityRequestHandle(
129 WebRTCIdentityStore* store,
130 const WebRTCIdentityStore::CompletionCallback& callback)
131 : store_(store), request_(NULL), callback_(callback) {}
133 private:
134 friend class WebRTCIdentityStore;
136 // Cancel the request. Does nothing if the request finished or was already
137 // cancelled.
138 void Cancel() {
139 DCHECK_CURRENTLY_ON(BrowserThread::IO);
140 if (!request_)
141 return;
143 callback_.Reset();
144 WebRTCIdentityRequest* request = request_;
145 request_ = NULL;
146 // "this" will be deleted after the following call, because "this" is
147 // owned by the Callback held by |request|.
148 request->Cancel(this);
151 void OnRequestStarted(WebRTCIdentityRequest* request) {
152 DCHECK_CURRENTLY_ON(BrowserThread::IO);
153 DCHECK(request);
154 request_ = request;
157 void OnRequestComplete(int error,
158 const std::string& certificate,
159 const std::string& private_key) {
160 DCHECK_CURRENTLY_ON(BrowserThread::IO);
161 DCHECK(request_);
162 request_ = NULL;
163 base::ResetAndReturn(&callback_).Run(error, certificate, private_key);
166 WebRTCIdentityStore* store_;
167 WebRTCIdentityRequest* request_;
168 WebRTCIdentityStore::CompletionCallback callback_;
170 DISALLOW_COPY_AND_ASSIGN(WebRTCIdentityRequestHandle);
173 WebRTCIdentityStore::WebRTCIdentityStore(const base::FilePath& path,
174 storage::SpecialStoragePolicy* policy)
175 : validity_period_(base::TimeDelta::FromDays(30)),
176 task_runner_(base::WorkerPool::GetTaskRunner(true)),
177 backend_(new WebRTCIdentityStoreBackend(path, policy, validity_period_)) {
180 WebRTCIdentityStore::~WebRTCIdentityStore() { backend_->Close(); }
182 base::Closure WebRTCIdentityStore::RequestIdentity(
183 const GURL& origin,
184 const std::string& identity_name,
185 const std::string& common_name,
186 const CompletionCallback& callback) {
187 DCHECK_CURRENTLY_ON(BrowserThread::IO);
188 WebRTCIdentityRequest* request =
189 FindRequest(origin, identity_name, common_name);
190 // If there is no identical request in flight, create a new one, queue it,
191 // and make the backend request.
192 if (!request) {
193 request = new WebRTCIdentityRequest(origin, identity_name, common_name);
194 // |request| will delete itself after the result is posted.
195 if (!backend_->FindIdentity(
196 origin,
197 identity_name,
198 common_name,
199 base::Bind(
200 &WebRTCIdentityStore::BackendFindCallback, this, request))) {
201 // Bail out if the backend failed to start the task.
202 delete request;
203 return base::Closure();
205 in_flight_requests_.push_back(request);
208 WebRTCIdentityRequestHandle* handle =
209 new WebRTCIdentityRequestHandle(this, callback);
211 request->AddCallback(
212 handle,
213 base::Bind(&WebRTCIdentityRequestHandle::OnRequestComplete,
214 base::Owned(handle)));
215 handle->OnRequestStarted(request);
216 return base::Bind(&WebRTCIdentityRequestHandle::Cancel,
217 base::Unretained(handle));
220 void WebRTCIdentityStore::DeleteBetween(base::Time delete_begin,
221 base::Time delete_end,
222 const base::Closure& callback) {
223 DCHECK_CURRENTLY_ON(BrowserThread::IO);
224 backend_->DeleteBetween(delete_begin, delete_end, callback);
227 void WebRTCIdentityStore::SetValidityPeriodForTesting(
228 base::TimeDelta validity_period) {
229 DCHECK_CURRENTLY_ON(BrowserThread::IO);
230 validity_period_ = validity_period;
231 backend_->SetValidityPeriodForTesting(validity_period);
234 void WebRTCIdentityStore::SetTaskRunnerForTesting(
235 const scoped_refptr<base::TaskRunner>& task_runner) {
236 DCHECK_CURRENTLY_ON(BrowserThread::IO);
237 task_runner_ = task_runner;
240 void WebRTCIdentityStore::BackendFindCallback(WebRTCIdentityRequest* request,
241 int error,
242 const std::string& certificate,
243 const std::string& private_key) {
244 DCHECK_CURRENTLY_ON(BrowserThread::IO);
245 if (error == net::OK) {
246 DVLOG(2) << "Identity found in DB.";
247 WebRTCIdentityRequestResult result(error, certificate, private_key);
248 PostRequestResult(request, result);
249 return;
251 // Generate a new identity if not found in the DB.
252 WebRTCIdentityRequestResult* result =
253 new WebRTCIdentityRequestResult(0, "", "");
254 if (!task_runner_->PostTaskAndReply(
255 FROM_HERE,
256 base::Bind(&GenerateIdentityWorker,
257 request->common_name_,
258 validity_period_,
259 result),
260 base::Bind(&WebRTCIdentityStore::GenerateIdentityCallback,
261 this,
262 request,
263 base::Owned(result)))) {
264 // Completes the request with error if failed to post the task.
265 WebRTCIdentityRequestResult result(net::ERR_UNEXPECTED, "", "");
266 PostRequestResult(request, result);
270 void WebRTCIdentityStore::GenerateIdentityCallback(
271 WebRTCIdentityRequest* request,
272 WebRTCIdentityRequestResult* result) {
273 DCHECK_CURRENTLY_ON(BrowserThread::IO);
274 if (result->error == net::OK) {
275 DVLOG(2) << "New identity generated and added to the backend.";
276 backend_->AddIdentity(request->origin_,
277 request->identity_name_,
278 request->common_name_,
279 result->certificate,
280 result->private_key);
282 PostRequestResult(request, *result);
285 void WebRTCIdentityStore::PostRequestResult(
286 WebRTCIdentityRequest* request,
287 const WebRTCIdentityRequestResult& result) {
288 DCHECK_CURRENTLY_ON(BrowserThread::IO);
289 // Removes the in flight request from the queue.
290 for (size_t i = 0; i < in_flight_requests_.size(); ++i) {
291 if (in_flight_requests_[i] == request) {
292 in_flight_requests_.erase(in_flight_requests_.begin() + i);
293 break;
296 // |request| will be deleted after this call.
297 request->Post(result);
300 // Find an identical request from the in flight requests.
301 WebRTCIdentityRequest* WebRTCIdentityStore::FindRequest(
302 const GURL& origin,
303 const std::string& identity_name,
304 const std::string& common_name) {
305 for (size_t i = 0; i < in_flight_requests_.size(); ++i) {
306 if (in_flight_requests_[i]->origin_ == origin &&
307 in_flight_requests_[i]->identity_name_ == identity_name &&
308 in_flight_requests_[i]->common_name_ == common_name) {
309 return in_flight_requests_[i];
312 return NULL;
315 } // namespace content