Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(488)

Side by Side Diff: content/browser/media/webrtc/webrtc_identity_store_backend.cc

Issue 2033353002: Remove PeerConnectionIdentityStore and related messaging/storage code. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Delayed deletion by 120s Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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_backend.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <tuple>
11
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/macros.h"
15 #include "base/memory/scoped_vector.h"
16 #include "base/strings/string_util.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "net/base/net_errors.h"
19 #include "sql/statement.h"
20 #include "sql/transaction.h"
21 #include "storage/browser/quota/special_storage_policy.h"
22 #include "url/gurl.h"
23
24 namespace content {
25
26 static const char kWebRTCIdentityStoreDBName[] = "webrtc_identity_store";
27
28 static const base::FilePath::CharType kWebRTCIdentityStoreDirectory[] =
29 FILE_PATH_LITERAL("WebRTCIdentityStore");
30
31 // Initializes the identity table, returning true on success.
32 static bool InitDB(sql::Connection* db) {
33 if (db->DoesTableExist(kWebRTCIdentityStoreDBName)) {
34 if (db->DoesColumnExist(kWebRTCIdentityStoreDBName, "origin") &&
35 db->DoesColumnExist(kWebRTCIdentityStoreDBName, "identity_name") &&
36 db->DoesColumnExist(kWebRTCIdentityStoreDBName, "common_name") &&
37 db->DoesColumnExist(kWebRTCIdentityStoreDBName, "certificate") &&
38 db->DoesColumnExist(kWebRTCIdentityStoreDBName, "private_key") &&
39 db->DoesColumnExist(kWebRTCIdentityStoreDBName, "creation_time"))
40 return true;
41
42 if (!db->Execute("DROP TABLE webrtc_identity_store"))
43 return false;
44 }
45
46 return db->Execute(
47 "CREATE TABLE webrtc_identity_store"
48 " ("
49 "origin TEXT NOT NULL,"
50 "identity_name TEXT NOT NULL,"
51 "common_name TEXT NOT NULL,"
52 "certificate BLOB NOT NULL,"
53 "private_key BLOB NOT NULL,"
54 "creation_time INTEGER)");
55 }
56
57 struct WebRTCIdentityStoreBackend::IdentityKey {
58 IdentityKey(const GURL& origin, const std::string& identity_name)
59 : origin(origin), identity_name(identity_name) {}
60
61 bool operator<(const IdentityKey& other) const {
62 return std::tie(origin, identity_name) <
63 std::tie(other.origin, other.identity_name);
64 }
65
66 GURL origin;
67 std::string identity_name;
68 };
69
70 struct WebRTCIdentityStoreBackend::Identity {
71 Identity(const std::string& common_name,
72 const std::string& certificate,
73 const std::string& private_key)
74 : common_name(common_name),
75 certificate(certificate),
76 private_key(private_key),
77 creation_time(base::Time::Now().ToInternalValue()) {}
78
79 Identity(const std::string& common_name,
80 const std::string& certificate,
81 const std::string& private_key,
82 int64_t creation_time)
83 : common_name(common_name),
84 certificate(certificate),
85 private_key(private_key),
86 creation_time(creation_time) {}
87
88 std::string common_name;
89 std::string certificate;
90 std::string private_key;
91 int64_t creation_time;
92 };
93
94 struct WebRTCIdentityStoreBackend::PendingFindRequest {
95 PendingFindRequest(const GURL& origin,
96 const std::string& identity_name,
97 const std::string& common_name,
98 const FindIdentityCallback& callback)
99 : origin(origin),
100 identity_name(identity_name),
101 common_name(common_name),
102 callback(callback) {}
103
104 ~PendingFindRequest() {}
105
106 GURL origin;
107 std::string identity_name;
108 std::string common_name;
109 FindIdentityCallback callback;
110 };
111
112 // The class encapsulates the database operations. All members except ctor and
113 // dtor should be accessed on the DB thread.
114 // It can be created/destroyed on any thread.
115 class WebRTCIdentityStoreBackend::SqlLiteStorage
116 : public base::RefCountedThreadSafe<SqlLiteStorage> {
117 public:
118 SqlLiteStorage(base::TimeDelta validity_period,
119 const base::FilePath& path,
120 storage::SpecialStoragePolicy* policy)
121 : validity_period_(validity_period), special_storage_policy_(policy) {
122 if (!path.empty())
123 path_ = path.Append(kWebRTCIdentityStoreDirectory);
124 }
125
126 void Load(IdentityMap* out_map);
127 void Close();
128 void AddIdentity(const GURL& origin,
129 const std::string& identity_name,
130 const Identity& identity);
131 void DeleteIdentity(const GURL& origin,
132 const std::string& identity_name,
133 const Identity& identity);
134 void DeleteBetween(base::Time delete_begin, base::Time delete_end);
135
136 void SetValidityPeriodForTesting(base::TimeDelta validity_period) {
137 DCHECK_CURRENTLY_ON(BrowserThread::DB);
138 DCHECK(!db_.get());
139 validity_period_ = validity_period;
140 }
141
142 private:
143 friend class base::RefCountedThreadSafe<SqlLiteStorage>;
144
145 enum OperationType {
146 ADD_IDENTITY,
147 DELETE_IDENTITY
148 };
149 struct PendingOperation {
150 PendingOperation(OperationType type,
151 const GURL& origin,
152 const std::string& identity_name,
153 const Identity& identity)
154 : type(type),
155 origin(origin),
156 identity_name(identity_name),
157 identity(identity) {}
158
159 OperationType type;
160 GURL origin;
161 std::string identity_name;
162 Identity identity;
163 };
164 typedef ScopedVector<PendingOperation> PendingOperationList;
165
166 virtual ~SqlLiteStorage() {}
167 void OnDatabaseError(int error, sql::Statement* stmt);
168 void BatchOperation(OperationType type,
169 const GURL& origin,
170 const std::string& identity_name,
171 const Identity& identity);
172 void Commit();
173
174 base::TimeDelta validity_period_;
175 // The file path of the DB. Empty if temporary.
176 base::FilePath path_;
177 scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy_;
178 std::unique_ptr<sql::Connection> db_;
179 // Batched DB operations pending to commit.
180 PendingOperationList pending_operations_;
181
182 DISALLOW_COPY_AND_ASSIGN(SqlLiteStorage);
183 };
184
185 WebRTCIdentityStoreBackend::WebRTCIdentityStoreBackend(
186 const base::FilePath& path,
187 storage::SpecialStoragePolicy* policy,
188 base::TimeDelta validity_period)
189 : validity_period_(validity_period),
190 state_(NOT_STARTED),
191 sql_lite_storage_(new SqlLiteStorage(validity_period, path, policy)) {
192 }
193
194 bool WebRTCIdentityStoreBackend::FindIdentity(
195 const GURL& origin,
196 const std::string& identity_name,
197 const std::string& common_name,
198 const FindIdentityCallback& callback) {
199 DCHECK_CURRENTLY_ON(BrowserThread::IO);
200 if (state_ == CLOSED)
201 return false;
202
203 if (state_ != LOADED) {
204 // Queues the request to wait for the DB to load.
205 pending_find_requests_.push_back(
206 new PendingFindRequest(origin, identity_name, common_name, callback));
207 if (state_ == LOADING)
208 return true;
209
210 DCHECK_EQ(state_, NOT_STARTED);
211
212 // Kick off loading the DB.
213 std::unique_ptr<IdentityMap> out_map(new IdentityMap());
214 base::Closure task(
215 base::Bind(&SqlLiteStorage::Load, sql_lite_storage_, out_map.get()));
216 // |out_map| will be NULL after this call.
217 if (BrowserThread::PostTaskAndReply(
218 BrowserThread::DB,
219 FROM_HERE,
220 task,
221 base::Bind(&WebRTCIdentityStoreBackend::OnLoaded,
222 this,
223 base::Passed(&out_map)))) {
224 state_ = LOADING;
225 return true;
226 }
227 // If it fails to post task, falls back to ERR_FILE_NOT_FOUND.
228 }
229
230 IdentityKey key(origin, identity_name);
231 IdentityMap::iterator iter = identities_.find(key);
232 if (iter != identities_.end() && iter->second.common_name == common_name) {
233 base::TimeDelta age = base::Time::Now() - base::Time::FromInternalValue(
234 iter->second.creation_time);
235 if (age < validity_period_) {
236 // Identity found.
237 return BrowserThread::PostTask(BrowserThread::IO,
238 FROM_HERE,
239 base::Bind(callback,
240 net::OK,
241 iter->second.certificate,
242 iter->second.private_key));
243 }
244 // Removes the expired identity from the in-memory cache. The copy in the
245 // database will be removed on the next load.
246 identities_.erase(iter);
247 }
248
249 return BrowserThread::PostTask(
250 BrowserThread::IO,
251 FROM_HERE,
252 base::Bind(callback, net::ERR_FILE_NOT_FOUND, "", ""));
253 }
254
255 void WebRTCIdentityStoreBackend::AddIdentity(const GURL& origin,
256 const std::string& identity_name,
257 const std::string& common_name,
258 const std::string& certificate,
259 const std::string& private_key) {
260 DCHECK_CURRENTLY_ON(BrowserThread::IO);
261 if (state_ == CLOSED)
262 return;
263
264 // If there is an existing identity for the same origin and identity_name,
265 // delete it.
266 IdentityKey key(origin, identity_name);
267 Identity identity(common_name, certificate, private_key);
268
269 if (identities_.find(key) != identities_.end()) {
270 if (!BrowserThread::PostTask(BrowserThread::DB,
271 FROM_HERE,
272 base::Bind(&SqlLiteStorage::DeleteIdentity,
273 sql_lite_storage_,
274 origin,
275 identity_name,
276 identities_.find(key)->second)))
277 return;
278 }
279 identities_.insert(std::pair<IdentityKey, Identity>(key, identity));
280
281 BrowserThread::PostTask(BrowserThread::DB,
282 FROM_HERE,
283 base::Bind(&SqlLiteStorage::AddIdentity,
284 sql_lite_storage_,
285 origin,
286 identity_name,
287 identity));
288 }
289
290 void WebRTCIdentityStoreBackend::Close() {
291 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
292 BrowserThread::PostTask(
293 BrowserThread::IO,
294 FROM_HERE,
295 base::Bind(&WebRTCIdentityStoreBackend::Close, this));
296 return;
297 }
298
299 if (state_ == CLOSED)
300 return;
301
302 state_ = CLOSED;
303 BrowserThread::PostTask(
304 BrowserThread::DB,
305 FROM_HERE,
306 base::Bind(&SqlLiteStorage::Close, sql_lite_storage_));
307 }
308
309 void WebRTCIdentityStoreBackend::DeleteBetween(base::Time delete_begin,
310 base::Time delete_end,
311 const base::Closure& callback) {
312 DCHECK_CURRENTLY_ON(BrowserThread::IO);
313 if (state_ == CLOSED)
314 return;
315
316 // Delete the in-memory cache.
317 IdentityMap::iterator it = identities_.begin();
318 while (it != identities_.end()) {
319 if (it->second.creation_time >= delete_begin.ToInternalValue() &&
320 it->second.creation_time <= delete_end.ToInternalValue()) {
321 identities_.erase(it++);
322 } else {
323 ++it;
324 }
325 }
326 BrowserThread::PostTaskAndReply(BrowserThread::DB,
327 FROM_HERE,
328 base::Bind(&SqlLiteStorage::DeleteBetween,
329 sql_lite_storage_,
330 delete_begin,
331 delete_end),
332 callback);
333 }
334
335 void WebRTCIdentityStoreBackend::SetValidityPeriodForTesting(
336 base::TimeDelta validity_period) {
337 DCHECK_CURRENTLY_ON(BrowserThread::IO);
338 validity_period_ = validity_period;
339 BrowserThread::PostTask(
340 BrowserThread::DB,
341 FROM_HERE,
342 base::Bind(&SqlLiteStorage::SetValidityPeriodForTesting,
343 sql_lite_storage_,
344 validity_period));
345 }
346
347 WebRTCIdentityStoreBackend::~WebRTCIdentityStoreBackend() {}
348
349 void WebRTCIdentityStoreBackend::OnLoaded(
350 std::unique_ptr<IdentityMap> out_map) {
351 DCHECK_CURRENTLY_ON(BrowserThread::IO);
352
353 if (state_ != LOADING)
354 return;
355
356 DVLOG(3) << "WebRTC identity store has loaded.";
357
358 state_ = LOADED;
359 identities_.swap(*out_map);
360
361 for (size_t i = 0; i < pending_find_requests_.size(); ++i) {
362 FindIdentity(pending_find_requests_[i]->origin,
363 pending_find_requests_[i]->identity_name,
364 pending_find_requests_[i]->common_name,
365 pending_find_requests_[i]->callback);
366 delete pending_find_requests_[i];
367 }
368 pending_find_requests_.clear();
369 }
370
371 //
372 // Implementation of SqlLiteStorage.
373 //
374
375 void WebRTCIdentityStoreBackend::SqlLiteStorage::Load(IdentityMap* out_map) {
376 DCHECK_CURRENTLY_ON(BrowserThread::DB);
377 DCHECK(!db_.get());
378
379 // Ensure the parent directory for storing certs is created before reading
380 // from it.
381 const base::FilePath dir = path_.DirName();
382 if (!base::PathExists(dir) && !base::CreateDirectory(dir)) {
383 DVLOG(2) << "Unable to open DB file path.";
384 return;
385 }
386
387 db_.reset(new sql::Connection());
388
389 db_->set_error_callback(base::Bind(&SqlLiteStorage::OnDatabaseError, this));
390
391 if (!db_->Open(path_)) {
392 DVLOG(2) << "Unable to open DB.";
393 db_.reset();
394 return;
395 }
396
397 if (!InitDB(db_.get())) {
398 DVLOG(2) << "Unable to init DB.";
399 db_.reset();
400 return;
401 }
402
403 db_->Preload();
404
405 // Delete expired identities.
406 DeleteBetween(base::Time(), base::Time::Now() - validity_period_);
407
408 // Slurp all the identities into the out_map.
409 sql::Statement stmt(db_->GetUniqueStatement(
410 "SELECT origin, identity_name, common_name, "
411 "certificate, private_key, creation_time "
412 "FROM webrtc_identity_store"));
413 CHECK(stmt.is_valid());
414
415 while (stmt.Step()) {
416 IdentityKey key(GURL(stmt.ColumnString(0)), stmt.ColumnString(1));
417 std::string common_name(stmt.ColumnString(2));
418 std::string cert, private_key;
419 stmt.ColumnBlobAsString(3, &cert);
420 stmt.ColumnBlobAsString(4, &private_key);
421 int64_t creation_time = stmt.ColumnInt64(5);
422 std::pair<IdentityMap::iterator, bool> result =
423 out_map->insert(std::pair<IdentityKey, Identity>(
424 key, Identity(common_name, cert, private_key, creation_time)));
425 DCHECK(result.second);
426 }
427 }
428
429 void WebRTCIdentityStoreBackend::SqlLiteStorage::Close() {
430 DCHECK_CURRENTLY_ON(BrowserThread::DB);
431 Commit();
432 db_.reset();
433 }
434
435 void WebRTCIdentityStoreBackend::SqlLiteStorage::AddIdentity(
436 const GURL& origin,
437 const std::string& identity_name,
438 const Identity& identity) {
439 DCHECK_CURRENTLY_ON(BrowserThread::DB);
440 if (!db_.get())
441 return;
442
443 // Do not add for session only origins.
444 if (special_storage_policy_.get() &&
445 !special_storage_policy_->IsStorageProtected(origin) &&
446 special_storage_policy_->IsStorageSessionOnly(origin)) {
447 return;
448 }
449 BatchOperation(ADD_IDENTITY, origin, identity_name, identity);
450 }
451
452 void WebRTCIdentityStoreBackend::SqlLiteStorage::DeleteIdentity(
453 const GURL& origin,
454 const std::string& identity_name,
455 const Identity& identity) {
456 DCHECK_CURRENTLY_ON(BrowserThread::DB);
457 if (!db_.get())
458 return;
459 BatchOperation(DELETE_IDENTITY, origin, identity_name, identity);
460 }
461
462 void WebRTCIdentityStoreBackend::SqlLiteStorage::DeleteBetween(
463 base::Time delete_begin,
464 base::Time delete_end) {
465 DCHECK_CURRENTLY_ON(BrowserThread::DB);
466 if (!db_.get())
467 return;
468
469 // Commit pending operations first.
470 Commit();
471
472 sql::Statement del_stmt(db_->GetCachedStatement(
473 SQL_FROM_HERE,
474 "DELETE FROM webrtc_identity_store"
475 " WHERE creation_time >= ? AND creation_time <= ?"));
476 CHECK(del_stmt.is_valid());
477
478 del_stmt.BindInt64(0, delete_begin.ToInternalValue());
479 del_stmt.BindInt64(1, delete_end.ToInternalValue());
480
481 sql::Transaction transaction(db_.get());
482 if (!transaction.Begin()) {
483 DVLOG(2) << "Failed to begin the transaction.";
484 return;
485 }
486
487 if (!del_stmt.Run()) {
488 DVLOG(2) << "Failed to run the delete statement.";
489 return;
490 }
491
492 if (!transaction.Commit())
493 DVLOG(2) << "Failed to commit the transaction.";
494 }
495
496 void WebRTCIdentityStoreBackend::SqlLiteStorage::OnDatabaseError(
497 int error,
498 sql::Statement* stmt) {
499 DCHECK_CURRENTLY_ON(BrowserThread::DB);
500
501 db_->RazeAndClose();
502 // It's not safe to reset |db_| here.
503 }
504
505 void WebRTCIdentityStoreBackend::SqlLiteStorage::BatchOperation(
506 OperationType type,
507 const GURL& origin,
508 const std::string& identity_name,
509 const Identity& identity) {
510 DCHECK_CURRENTLY_ON(BrowserThread::DB);
511 // Commit every 30 seconds.
512 static const base::TimeDelta kCommitInterval(
513 base::TimeDelta::FromSeconds(30));
514 // Commit right away if we have more than 512 outstanding operations.
515 static const size_t kCommitAfterBatchSize = 512;
516
517 // We do a full copy of the cert here, and hopefully just here.
518 std::unique_ptr<PendingOperation> operation(
519 new PendingOperation(type, origin, identity_name, identity));
520
521 pending_operations_.push_back(operation.release());
522
523 if (pending_operations_.size() == 1) {
524 // We've gotten our first entry for this batch, fire off the timer.
525 BrowserThread::PostDelayedTask(BrowserThread::DB,
526 FROM_HERE,
527 base::Bind(&SqlLiteStorage::Commit, this),
528 kCommitInterval);
529 } else if (pending_operations_.size() >= kCommitAfterBatchSize) {
530 // We've reached a big enough batch, fire off a commit now.
531 BrowserThread::PostTask(BrowserThread::DB,
532 FROM_HERE,
533 base::Bind(&SqlLiteStorage::Commit, this));
534 }
535 }
536
537 void WebRTCIdentityStoreBackend::SqlLiteStorage::Commit() {
538 DCHECK_CURRENTLY_ON(BrowserThread::DB);
539 // Maybe an old timer fired or we are already Close()'ed.
540 if (!db_.get() || pending_operations_.empty())
541 return;
542
543 sql::Statement add_stmt(db_->GetCachedStatement(
544 SQL_FROM_HERE,
545 "INSERT INTO webrtc_identity_store "
546 "(origin, identity_name, common_name, certificate,"
547 " private_key, creation_time) VALUES"
548 " (?,?,?,?,?,?)"));
549
550 CHECK(add_stmt.is_valid());
551
552 sql::Statement del_stmt(db_->GetCachedStatement(
553 SQL_FROM_HERE,
554 "DELETE FROM webrtc_identity_store WHERE origin=? AND identity_name=?"));
555
556 CHECK(del_stmt.is_valid());
557
558 sql::Transaction transaction(db_.get());
559 if (!transaction.Begin()) {
560 DVLOG(2) << "Failed to begin the transaction.";
561 return;
562 }
563
564 // Swaps |pending_operations_| into a temporary list to make sure
565 // |pending_operations_| is always cleared in case of DB errors.
566 PendingOperationList pending_operations_copy;
567 pending_operations_.swap(pending_operations_copy);
568
569 for (PendingOperationList::const_iterator it =
570 pending_operations_copy.begin();
571 it != pending_operations_copy.end();
572 ++it) {
573 switch ((*it)->type) {
574 case ADD_IDENTITY: {
575 add_stmt.Reset(true);
576 add_stmt.BindString(0, (*it)->origin.spec());
577 add_stmt.BindString(1, (*it)->identity_name);
578 add_stmt.BindString(2, (*it)->identity.common_name);
579 const std::string& cert = (*it)->identity.certificate;
580 add_stmt.BindBlob(3, cert.data(), cert.size());
581 const std::string& private_key = (*it)->identity.private_key;
582 add_stmt.BindBlob(4, private_key.data(), private_key.size());
583 add_stmt.BindInt64(5, (*it)->identity.creation_time);
584 if (!add_stmt.Run()) {
585 DVLOG(2) << "Failed to add the identity to DB.";
586 return;
587 }
588 break;
589 }
590 case DELETE_IDENTITY:
591 del_stmt.Reset(true);
592 del_stmt.BindString(0, (*it)->origin.spec());
593 del_stmt.BindString(1, (*it)->identity_name);
594 if (!del_stmt.Run()) {
595 DVLOG(2) << "Failed to delete the identity from DB.";
596 return;
597 }
598 break;
599
600 default:
601 NOTREACHED();
602 break;
603 }
604 }
605
606 if (!transaction.Commit())
607 DVLOG(2) << "Failed to commit the transaction.";
608 }
609
610 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698