| 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 |