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
)
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())
89 callbacks_
.erase(handle
);
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
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();
110 it
->second
.Run(result
.error
, result
.certificate
, result
.private_key
);
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
{
128 WebRTCIdentityRequestHandle(
129 WebRTCIdentityStore
* store
,
130 const WebRTCIdentityStore::CompletionCallback
& callback
)
131 : store_(store
), request_(NULL
), callback_(callback
) {}
134 friend class WebRTCIdentityStore
;
136 // Cancel the request. Does nothing if the request finished or was already
139 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
144 WebRTCIdentityRequest
* request
= request_
;
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
);
157 void OnRequestComplete(int error
,
158 const std::string
& certificate
,
159 const std::string
& private_key
) {
160 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
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(
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.
193 request
= new WebRTCIdentityRequest(origin
, identity_name
, common_name
);
194 // |request| will delete itself after the result is posted.
195 if (!backend_
->FindIdentity(
200 &WebRTCIdentityStore::BackendFindCallback
, this, request
))) {
201 // Bail out if the backend failed to start the task.
203 return base::Closure();
205 in_flight_requests_
.push_back(request
);
208 WebRTCIdentityRequestHandle
* handle
=
209 new WebRTCIdentityRequestHandle(this, callback
);
211 request
->AddCallback(
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
,
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
);
251 // Generate a new identity if not found in the DB.
252 WebRTCIdentityRequestResult
* result
=
253 new WebRTCIdentityRequestResult(0, "", "");
254 if (!task_runner_
->PostTaskAndReply(
256 base::Bind(&GenerateIdentityWorker
,
257 request
->common_name_
,
260 base::Bind(&WebRTCIdentityStore::GenerateIdentityCallback
,
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_
,
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
);
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(
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
];
315 } // namespace content