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"
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"
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
) {}
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(
48 now
+ validity_period
,
50 &result
->certificate
);
53 DLOG(ERROR
) << "Unable to create x509 cert for client";
54 result
->error
= net::ERR_SELF_SIGNED_CERT_GENERATION_FAILED
;
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
;
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
{
78 WebRTCIdentityRequest(const GURL
& origin
,
79 const std::string
& identity_name
,
80 const std::string
& common_name
,
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())
91 callbacks_
.erase(handle
);
94 bool enable_cache() const { return enable_cache_
; }
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
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();
114 it
->second
.Run(result
.error
, result
.certificate
, result
.private_key
);
119 std::string identity_name_
;
120 std::string common_name_
;
121 typedef std::map
<WebRTCIdentityRequestHandle
*,
122 WebRTCIdentityStore::CompletionCallback
> CallbackMap
;
123 CallbackMap callbacks_
;
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
{
133 WebRTCIdentityRequestHandle(
134 WebRTCIdentityStore
* store
,
135 const WebRTCIdentityStore::CompletionCallback
& callback
)
136 : store_(store
), request_(NULL
), callback_(callback
) {}
139 friend class WebRTCIdentityStore
;
141 // Cancel the request. Does nothing if the request finished or was already
144 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
149 WebRTCIdentityRequest
* request
= request_
;
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
);
162 void OnRequestComplete(int error
,
163 const std::string
& certificate
,
164 const std::string
& private_key
) {
165 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
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(
189 const std::string
& identity_name
,
190 const std::string
& common_name
,
191 const CompletionCallback
& callback
,
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.
199 request
= new WebRTCIdentityRequest(origin
, identity_name
, common_name
,
201 // In either case, |request| will delete itself after the result is posted.
203 if (!backend_
->FindIdentity(
204 origin
, identity_name
, common_name
,
205 base::Bind(&WebRTCIdentityStore::BackendFindCallback
, this,
207 // Bail out if the backend failed to start the task.
209 return base::Closure();
212 GenerateNewIdentity(request
);
214 in_flight_requests_
.push_back(request
);
217 WebRTCIdentityRequestHandle
* handle
=
218 new WebRTCIdentityRequestHandle(this, callback
);
220 request
->AddCallback(
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
,
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
);
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_
,
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
);
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(
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
];
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