- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / media / webrtc_identity_store.cc
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_identity_store.h"
6
7 #include <map>
8
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"
20
21 namespace content {
22
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) {}
28
29   int error;
30   std::string certificate;
31   std::string private_key;
32 };
33
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());
41
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);
51
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;
56   }
57
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;
63   }
64
65   result->private_key =
66       std::string(private_key_info.begin(), private_key_info.end());
67 }
68
69 class WebRTCIdentityRequestHandle;
70
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) {}
84
85   void Cancel(WebRTCIdentityRequestHandle* handle) {
86     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
87     if (callbacks_.find(handle) == callbacks_.end())
88       return;
89     callbacks_.erase(handle);
90   }
91
92  private:
93   friend class WebRTCIdentityStore;
94
95   void AddCallback(WebRTCIdentityRequestHandle* handle,
96                    const WebRTCIdentityStore::CompletionCallback& callback) {
97     DCHECK(callbacks_.find(handle) == callbacks_.end());
98     callbacks_[handle] = callback;
99   }
100
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(BrowserThread::CurrentlyOn(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;
112   }
113
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_;
120 };
121
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) {}
132
133  private:
134   friend class WebRTCIdentityStore;
135
136   // Cancel the request.  Does nothing if the request finished or was already
137   // cancelled.
138   void Cancel() {
139     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
140     if (!request_)
141       return;
142
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);
149   }
150
151   void OnRequestStarted(WebRTCIdentityRequest* request) {
152     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
153     DCHECK(request);
154     request_ = request;
155   }
156
157   void OnRequestComplete(int error,
158                          const std::string& certificate,
159                          const std::string& private_key) {
160     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
161     DCHECK(request_);
162     request_ = NULL;
163     base::ResetAndReturn(&callback_).Run(error, certificate, private_key);
164   }
165
166   WebRTCIdentityStore* store_;
167   WebRTCIdentityRequest* request_;
168   WebRTCIdentityStore::CompletionCallback callback_;
169
170   DISALLOW_COPY_AND_ASSIGN(WebRTCIdentityRequestHandle);
171 };
172
173 WebRTCIdentityStore::WebRTCIdentityStore(const base::FilePath& path,
174                                          quota::SpecialStoragePolicy* policy)
175     : validity_period_(base::TimeDelta::FromDays(30)),
176       task_runner_(base::WorkerPool::GetTaskRunner(true)),
177       backend_(new WebRTCIdentityStoreBackend(path, policy, validity_period_)) {
178   }
179
180 WebRTCIdentityStore::~WebRTCIdentityStore() { backend_->Close(); }
181
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(BrowserThread::CurrentlyOn(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();
204     }
205     in_flight_requests_.push_back(request);
206   }
207
208   WebRTCIdentityRequestHandle* handle =
209     new WebRTCIdentityRequestHandle(this, callback);
210
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));
218 }
219
220 void WebRTCIdentityStore::DeleteBetween(base::Time delete_begin,
221                                         base::Time delete_end,
222                                         const base::Closure& callback) {
223   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
224   backend_->DeleteBetween(delete_begin, delete_end, callback);
225 }
226
227 void WebRTCIdentityStore::SetValidityPeriodForTesting(
228     base::TimeDelta validity_period) {
229   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
230   validity_period_ = validity_period;
231   backend_->SetValidityPeriodForTesting(validity_period);
232 }
233
234 void WebRTCIdentityStore::SetTaskRunnerForTesting(
235     const scoped_refptr<base::TaskRunner>& task_runner) {
236   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
237   task_runner_ = task_runner;
238 }
239
240 void WebRTCIdentityStore::BackendFindCallback(WebRTCIdentityRequest* request,
241                                               int error,
242                                               const std::string& certificate,
243                                               const std::string& private_key) {
244   DCHECK(BrowserThread::CurrentlyOn(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;
250   }
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);
267   }
268 }
269
270 void WebRTCIdentityStore::GenerateIdentityCallback(
271     WebRTCIdentityRequest* request,
272     WebRTCIdentityRequestResult* result) {
273   DCHECK(BrowserThread::CurrentlyOn(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);
281   }
282   PostRequestResult(request, *result);
283 }
284
285 void WebRTCIdentityStore::PostRequestResult(
286     WebRTCIdentityRequest* request,
287     const WebRTCIdentityRequestResult& result) {
288   DCHECK(BrowserThread::CurrentlyOn(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;
294     }
295   }
296   // |request| will be deleted after this call.
297   request->Post(result);
298 }
299
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];
310     }
311   }
312   return NULL;
313 }
314
315 }  // namespace content