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