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

Side by Side Diff: chrome/browser/sync/engine/syncapi.cc

Issue 7108067: [Sync] Ensure cryptographer ready before encrypting. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Split bootstrap encryption and add ReloadNigori method Created 9 years, 6 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/sync/engine/syncapi.h" 5 #include "chrome/browser/sync/engine/syncapi.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <bitset> 8 #include <bitset>
9 #include <iomanip> 9 #include <iomanip>
10 #include <list> 10 #include <list>
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after
195 } 195 }
196 } 196 }
197 197
198 UserShare::UserShare() {} 198 UserShare::UserShare() {}
199 199
200 UserShare::~UserShare() {} 200 UserShare::~UserShare() {}
201 201
202 //////////////////////////////////// 202 ////////////////////////////////////
203 // BaseNode member definitions. 203 // BaseNode member definitions.
204 204
205 BaseNode::BaseNode() {} 205 BaseNode::BaseNode() : password_data_(new sync_pb::PasswordSpecificsData) {}
206 206
207 BaseNode::~BaseNode() {} 207 BaseNode::~BaseNode() {}
208 208
209 std::string BaseNode::GenerateSyncableHash( 209 std::string BaseNode::GenerateSyncableHash(
210 syncable::ModelType model_type, const std::string& client_tag) { 210 syncable::ModelType model_type, const std::string& client_tag) {
211 // blank PB with just the extension in it has termination symbol, 211 // blank PB with just the extension in it has termination symbol,
212 // handy for delimiter 212 // handy for delimiter
213 sync_pb::EntitySpecifics serialized_type; 213 sync_pb::EntitySpecifics serialized_type;
214 syncable::AddDefaultExtensionValue(model_type, &serialized_type); 214 syncable::AddDefaultExtensionValue(model_type, &serialized_type);
215 std::string hash_input; 215 std::string hash_input;
(...skipping 22 matching lines...) Expand all
238 } 238 }
239 239
240 bool BaseNode::DecryptIfNecessary() { 240 bool BaseNode::DecryptIfNecessary() {
241 if (GetIsFolder()) return true; // Ignore the top-level datatype folder. 241 if (GetIsFolder()) return true; // Ignore the top-level datatype folder.
242 const sync_pb::EntitySpecifics& specifics = 242 const sync_pb::EntitySpecifics& specifics =
243 GetEntry()->Get(syncable::SPECIFICS); 243 GetEntry()->Get(syncable::SPECIFICS);
244 if (specifics.HasExtension(sync_pb::password)) { 244 if (specifics.HasExtension(sync_pb::password)) {
245 // Passwords have their own legacy encryption structure. 245 // Passwords have their own legacy encryption structure.
246 scoped_ptr<sync_pb::PasswordSpecificsData> data(DecryptPasswordSpecifics( 246 scoped_ptr<sync_pb::PasswordSpecificsData> data(DecryptPasswordSpecifics(
247 specifics, GetTransaction()->GetCryptographer())); 247 specifics, GetTransaction()->GetCryptographer()));
248 if (!data.get()) 248 if (!data.get()) {
249 LOG(ERROR) << "Failed to decrypt password specifics.";
249 return false; 250 return false;
251 }
250 password_data_.swap(data); 252 password_data_.swap(data);
251 return true; 253 return true;
252 } 254 }
253 255
254 // We assume any node with the encrypted field set has encrypted data. 256 // We assume any node with the encrypted field set has encrypted data.
255 if (!specifics.has_encrypted()) 257 if (!specifics.has_encrypted())
256 return true; 258 return true;
257 259
258 const sync_pb::EncryptedData& encrypted = 260 const sync_pb::EncryptedData& encrypted =
259 specifics.encrypted(); 261 specifics.encrypted();
260 std::string plaintext_data = GetTransaction()->GetCryptographer()-> 262 std::string plaintext_data = GetTransaction()->GetCryptographer()->
261 DecryptToString(encrypted); 263 DecryptToString(encrypted);
262 if (plaintext_data.length() == 0) 264 if (plaintext_data.length() == 0 ||
263 return false; 265 !unencrypted_data_.ParseFromString(plaintext_data)) {
264 if (!unencrypted_data_.ParseFromString(plaintext_data)) {
265 LOG(ERROR) << "Failed to decrypt encrypted node of type " << 266 LOG(ERROR) << "Failed to decrypt encrypted node of type " <<
266 syncable::ModelTypeToString(GetModelType()) << "."; 267 syncable::ModelTypeToString(GetModelType()) << ".";
267 return false; 268 return false;
268 } 269 }
269 return true; 270 return true;
270 } 271 }
271 272
272 const sync_pb::EntitySpecifics& BaseNode::GetUnencryptedSpecifics( 273 const sync_pb::EntitySpecifics& BaseNode::GetUnencryptedSpecifics(
273 const syncable::Entry* entry) const { 274 const syncable::Entry* entry) const {
274 const sync_pb::EntitySpecifics& specifics = entry->Get(SPECIFICS); 275 const sync_pb::EntitySpecifics& specifics = entry->Get(SPECIFICS);
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
397 return GetEntitySpecifics().GetExtension(sync_pb::bookmark); 398 return GetEntitySpecifics().GetExtension(sync_pb::bookmark);
398 } 399 }
399 400
400 const sync_pb::NigoriSpecifics& BaseNode::GetNigoriSpecifics() const { 401 const sync_pb::NigoriSpecifics& BaseNode::GetNigoriSpecifics() const {
401 DCHECK_EQ(syncable::NIGORI, GetModelType()); 402 DCHECK_EQ(syncable::NIGORI, GetModelType());
402 return GetEntitySpecifics().GetExtension(sync_pb::nigori); 403 return GetEntitySpecifics().GetExtension(sync_pb::nigori);
403 } 404 }
404 405
405 const sync_pb::PasswordSpecificsData& BaseNode::GetPasswordSpecifics() const { 406 const sync_pb::PasswordSpecificsData& BaseNode::GetPasswordSpecifics() const {
406 DCHECK_EQ(syncable::PASSWORDS, GetModelType()); 407 DCHECK_EQ(syncable::PASSWORDS, GetModelType());
407 DCHECK(password_data_.get());
408 return *password_data_; 408 return *password_data_;
409 } 409 }
410 410
411 const sync_pb::ThemeSpecifics& BaseNode::GetThemeSpecifics() const { 411 const sync_pb::ThemeSpecifics& BaseNode::GetThemeSpecifics() const {
412 DCHECK_EQ(syncable::THEMES, GetModelType()); 412 DCHECK_EQ(syncable::THEMES, GetModelType());
413 return GetEntitySpecifics().GetExtension(sync_pb::theme); 413 return GetEntitySpecifics().GetExtension(sync_pb::theme);
414 } 414 }
415 415
416 const sync_pb::TypedUrlSpecifics& BaseNode::GetTypedUrlSpecifics() const { 416 const sync_pb::TypedUrlSpecifics& BaseNode::GetTypedUrlSpecifics() const {
417 DCHECK_EQ(syncable::TYPED_URLS, GetModelType()); 417 DCHECK_EQ(syncable::TYPED_URLS, GetModelType());
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
564 scoped_ptr<sync_pb::PasswordSpecificsData> old_plaintext( 564 scoped_ptr<sync_pb::PasswordSpecificsData> old_plaintext(
565 DecryptPasswordSpecifics(GetEntry()->Get(SPECIFICS), cryptographer)); 565 DecryptPasswordSpecifics(GetEntry()->Get(SPECIFICS), cryptographer));
566 if (old_plaintext.get() && 566 if (old_plaintext.get() &&
567 old_plaintext->SerializeAsString() == data.SerializeAsString() && 567 old_plaintext->SerializeAsString() == data.SerializeAsString() &&
568 cryptographer->CanDecryptUsingDefaultKey(old_ciphertext)) { 568 cryptographer->CanDecryptUsingDefaultKey(old_ciphertext)) {
569 return; 569 return;
570 } 570 }
571 571
572 sync_pb::PasswordSpecifics new_value; 572 sync_pb::PasswordSpecifics new_value;
573 if (!cryptographer->Encrypt(data, new_value.mutable_encrypted())) { 573 if (!cryptographer->Encrypt(data, new_value.mutable_encrypted())) {
574 NOTREACHED(); 574 NOTREACHED() << "Failed to encrypt password, possibly due to sync node "
575 << "corruption";
576 return;
575 } 577 }
576 578
577 sync_pb::EntitySpecifics entity_specifics; 579 sync_pb::EntitySpecifics entity_specifics;
578 entity_specifics.MutableExtension(sync_pb::password)->CopyFrom(new_value); 580 entity_specifics.MutableExtension(sync_pb::password)->CopyFrom(new_value);
579 SetEntitySpecifics(entity_specifics); 581 SetEntitySpecifics(entity_specifics);
580 } 582 }
581 583
582 void WriteNode::SetThemeSpecifics( 584 void WriteNode::SetThemeSpecifics(
583 const sync_pb::ThemeSpecifics& new_value) { 585 const sync_pb::ThemeSpecifics& new_value) {
584 sync_pb::EntitySpecifics entity_specifics; 586 sync_pb::EntitySpecifics entity_specifics;
(...skipping 611 matching lines...) Expand 10 before | Expand all | Expand 10 after
1196 1198
1197 // Called when the user disables or enables a sync type. 1199 // Called when the user disables or enables a sync type.
1198 void UpdateEnabledTypes(); 1200 void UpdateEnabledTypes();
1199 1201
1200 // Tell the sync engine to start the syncing process. 1202 // Tell the sync engine to start the syncing process.
1201 void StartSyncingNormally(); 1203 void StartSyncingNormally();
1202 1204
1203 // Whether or not the Nigori node is encrypted using an explicit passphrase. 1205 // Whether or not the Nigori node is encrypted using an explicit passphrase.
1204 bool IsUsingExplicitPassphrase(); 1206 bool IsUsingExplicitPassphrase();
1205 1207
1208 // Update the Cryptographer from the current nigori node.
1209 // Note: opens a transaction and can trigger an ON_PASSPHRASE_REQUIRED, so
1210 // should only be called after syncapi is fully initialized.
1211 // Returns true if cryptographer is ready, false otherwise.
1212 bool UpdateCryptographerFromNigori();
1213
1206 // Set the datatypes we want to encrypt and encrypt any nodes as necessary. 1214 // Set the datatypes we want to encrypt and encrypt any nodes as necessary.
1207 void EncryptDataTypes(const syncable::ModelTypeSet& encrypted_types); 1215 void EncryptDataTypes(const syncable::ModelTypeSet& encrypted_types);
1208 1216
1209 // Try to set the current passphrase to |passphrase|, and record whether 1217 // Try to set the current passphrase to |passphrase|, and record whether
1210 // it is an explicit passphrase or implicitly using gaia in the Nigori 1218 // it is an explicit passphrase or implicitly using gaia in the Nigori
1211 // node. 1219 // node.
1212 void SetPassphrase(const std::string& passphrase, bool is_explicit); 1220 void SetPassphrase(const std::string& passphrase, bool is_explicit);
1213 1221
1214 // Call periodically from a database-safe thread to persist recent changes 1222 // Call periodically from a database-safe thread to persist recent changes
1215 // to the syncapi model. 1223 // to the syncapi model.
(...skipping 572 matching lines...) Expand 10 before | Expand all | Expand 10 after
1788 } 1796 }
1789 1797
1790 // Do this once the directory is opened. 1798 // Do this once the directory is opened.
1791 BootstrapEncryption(restored_key_for_bootstrapping); 1799 BootstrapEncryption(restored_key_for_bootstrapping);
1792 MarkAndNotifyInitializationComplete(); 1800 MarkAndNotifyInitializationComplete();
1793 return signed_in; 1801 return signed_in;
1794 } 1802 }
1795 1803
1796 void SyncManager::SyncInternal::BootstrapEncryption( 1804 void SyncManager::SyncInternal::BootstrapEncryption(
1797 const std::string& restored_key_for_bootstrapping) { 1805 const std::string& restored_key_for_bootstrapping) {
1806 // Cryptographer should only be accessed while holding a transaction.
1807 ReadTransaction trans(GetUserShare());
1808 Cryptographer* cryptographer = trans.GetCryptographer();
1809
1810 // Set the bootstrap token before bailing out if nigori node is not there.
1811 // This could happen if server asked us to migrate nigri.
1812 cryptographer->Bootstrap(restored_key_for_bootstrapping);
1813 }
1814
1815 bool SyncManager::SyncInternal::UpdateCryptographerFromNigori() {
1798 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); 1816 syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
1799 if (!lookup.good()) { 1817 if (!lookup.good()) {
1800 VLOG(0) << "BootstrapEncryption: lookup not good so bailing out"; 1818 NOTREACHED() << "BootstrapEncryption: lookup not good so bailing out";
1819 return false;
1820 }
1821 if (!lookup->initial_sync_ended_for_type(syncable::NIGORI))
1822 return false; // Should only happen during first time sync.
1823
1824 ReadTransaction trans(GetUserShare());
1825 Cryptographer* cryptographer = trans.GetCryptographer();
1826
1827 ReadNode node(&trans);
1828 if (!node.InitByTagLookup(kNigoriTag)) {
1801 NOTREACHED(); 1829 NOTREACHED();
1802 return; 1830 return false;
1831 }
1832 Cryptographer::UpdateResult result =
1833 cryptographer->Update(node.GetNigoriSpecifics());
1834 if (result == Cryptographer::NEEDS_PASSPHRASE) {
1835 ObserverList<SyncManager::Observer> temp_obs_list;
1836 CopyObservers(&temp_obs_list);
1837 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1838 OnPassphraseRequired(sync_api::REASON_DECRYPTION));
1803 } 1839 }
1804 1840
1805 sync_pb::NigoriSpecifics nigori; 1841 return cryptographer->is_ready();
1806 syncable::ModelTypeSet encrypted_types;
1807 {
1808 // Cryptographer should only be accessed while holding a transaction.
1809 ReadTransaction trans(GetUserShare());
1810 Cryptographer* cryptographer = trans.GetCryptographer();
1811
1812 // Set the bootstrap token before bailing out if nigori node is not there.
1813 // This could happen if server asked us to migrate nigri.
1814 cryptographer->Bootstrap(restored_key_for_bootstrapping);
1815
1816 if (!lookup->initial_sync_ended_for_type(syncable::NIGORI))
1817 return;
1818
1819 ReadNode node(&trans);
1820 if (!node.InitByTagLookup(kNigoriTag)) {
1821 NOTREACHED();
1822 return;
1823 }
1824
1825 nigori.CopyFrom(node.GetNigoriSpecifics());
1826 Cryptographer::UpdateResult result = cryptographer->Update(nigori);
1827 if (result == Cryptographer::NEEDS_PASSPHRASE) {
1828 ObserverList<SyncManager::Observer> temp_obs_list;
1829 CopyObservers(&temp_obs_list);
1830 FOR_EACH_OBSERVER(SyncManager::Observer, temp_obs_list,
1831 OnPassphraseRequired(sync_api::REASON_DECRYPTION));
1832 }
1833
1834 // Refresh list of encrypted datatypes.
1835 encrypted_types = GetEncryptedTypes(&trans);
1836 }
1837
1838
1839
1840 // Ensure any datatypes that need encryption are encrypted.
1841 EncryptDataTypes(encrypted_types);
1842 } 1842 }
1843 1843
1844 void SyncManager::SyncInternal::StartSyncingNormally() { 1844 void SyncManager::SyncInternal::StartSyncingNormally() {
1845 // Start the syncer thread. This won't actually 1845 // Start the syncer thread. This won't actually
1846 // result in any syncing until at least the 1846 // result in any syncing until at least the
1847 // DirectoryManager broadcasts the OPENED event, 1847 // DirectoryManager broadcasts the OPENED event,
1848 // and a valid server connection is detected. 1848 // and a valid server connection is detected.
1849 if (syncer_thread()) // NULL during certain unittests. 1849 if (syncer_thread()) // NULL during certain unittests.
1850 syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); 1850 syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
1851 } 1851 }
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after
2078 } 2078 }
2079 2079
2080 void SyncManager::SyncInternal::EncryptDataTypes( 2080 void SyncManager::SyncInternal::EncryptDataTypes(
2081 const syncable::ModelTypeSet& encrypted_types) { 2081 const syncable::ModelTypeSet& encrypted_types) {
2082 VLOG(1) << "Attempting to encrypt datatypes " 2082 VLOG(1) << "Attempting to encrypt datatypes "
2083 << syncable::ModelTypeSetToString(encrypted_types); 2083 << syncable::ModelTypeSetToString(encrypted_types);
2084 2084
2085 WriteTransaction trans(GetUserShare()); 2085 WriteTransaction trans(GetUserShare());
2086 WriteNode node(&trans); 2086 WriteNode node(&trans);
2087 if (!node.InitByTagLookup(kNigoriTag)) { 2087 if (!node.InitByTagLookup(kNigoriTag)) {
2088 LOG(ERROR) << "Unable to set encrypted datatypes because Nigori node not " 2088 NOTREACHED() << "Unable to set encrypted datatypes because Nigori node not "
2089 << "found."; 2089 << "found.";
2090 NOTREACHED();
2091 return; 2090 return;
2092 } 2091 }
2093 2092
2094 Cryptographer* cryptographer = trans.GetCryptographer(); 2093 Cryptographer* cryptographer = trans.GetCryptographer();
2095 2094
2095 if (!cryptographer->is_ready()) {
2096 NOTREACHED() << "Attempting to encrypt datatypes when cryptographer not "
2097 << "ready.";
2098 return;
2099 }
2100
2096 // Update the Nigori node set of encrypted datatypes so other machines notice. 2101 // Update the Nigori node set of encrypted datatypes so other machines notice.
2097 // Note, we merge the current encrypted types with those requested. Once a 2102 // Note, we merge the current encrypted types with those requested. Once a
2098 // datatypes is marked as needing encryption, it is never unmarked. 2103 // datatypes is marked as needing encryption, it is never unmarked.
2099 sync_pb::NigoriSpecifics nigori; 2104 sync_pb::NigoriSpecifics nigori;
2100 nigori.CopyFrom(node.GetNigoriSpecifics()); 2105 nigori.CopyFrom(node.GetNigoriSpecifics());
2101 syncable::ModelTypeSet current_encrypted_types = GetEncryptedTypes(&trans); 2106 syncable::ModelTypeSet current_encrypted_types = GetEncryptedTypes(&trans);
2102 syncable::ModelTypeSet newly_encrypted_types; 2107 syncable::ModelTypeSet newly_encrypted_types;
2103 std::set_union(current_encrypted_types.begin(), current_encrypted_types.end(), 2108 std::set_union(current_encrypted_types.begin(), current_encrypted_types.end(),
2104 encrypted_types.begin(), encrypted_types.end(), 2109 encrypted_types.begin(), encrypted_types.end(),
2105 std::inserter(newly_encrypted_types, 2110 std::inserter(newly_encrypted_types,
(...skipping 877 matching lines...) Expand 10 before | Expand all | Expand 10 after
2983 } 2988 }
2984 BaseTransaction::~BaseTransaction() { 2989 BaseTransaction::~BaseTransaction() {
2985 delete lookup_; 2990 delete lookup_;
2986 } 2991 }
2987 2992
2988 UserShare* SyncManager::GetUserShare() const { 2993 UserShare* SyncManager::GetUserShare() const {
2989 DCHECK(data_->initialized()) << "GetUserShare requires initialization!"; 2994 DCHECK(data_->initialized()) << "GetUserShare requires initialization!";
2990 return data_->GetUserShare(); 2995 return data_->GetUserShare();
2991 } 2996 }
2992 2997
2998 void SyncManager::ReloadNigori() {
2999 DCHECK(data_->initialized());
3000 if (data_->UpdateCryptographerFromNigori())
tim (not reviewing) 2011/06/14 01:32:58 is it possible for is_ready() to change from true
Nicolas Zea 2011/06/14 16:52:38 You're right, once we release the transaction it's
3001 data_->EncryptDataTypes(syncable::ModelTypeSet());
3002 }
3003
2993 syncable::ModelTypeSet SyncManager::GetEncryptedDataTypes() const { 3004 syncable::ModelTypeSet SyncManager::GetEncryptedDataTypes() const {
2994 sync_api::ReadTransaction trans(GetUserShare()); 3005 sync_api::ReadTransaction trans(GetUserShare());
2995 return GetEncryptedTypes(&trans); 3006 return GetEncryptedTypes(&trans);
2996 } 3007 }
2997 3008
2998
2999 bool SyncManager::HasUnsyncedItems() const { 3009 bool SyncManager::HasUnsyncedItems() const {
3000 sync_api::ReadTransaction trans(GetUserShare()); 3010 sync_api::ReadTransaction trans(GetUserShare());
3001 return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0); 3011 return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0);
3002 } 3012 }
3003 3013
3004 void SyncManager::LogUnsyncedItems(int level) const { 3014 void SyncManager::LogUnsyncedItems(int level) const {
3005 std::vector<int64> unsynced_handles; 3015 std::vector<int64> unsynced_handles;
3006 sync_api::ReadTransaction trans(GetUserShare()); 3016 sync_api::ReadTransaction trans(GetUserShare());
3007 trans.GetWrappedTrans()->directory()->GetUnsyncedMetaHandles( 3017 trans.GetWrappedTrans()->directory()->GetUnsyncedMetaHandles(
3008 trans.GetWrappedTrans(), &unsynced_handles); 3018 trans.GetWrappedTrans(), &unsynced_handles);
(...skipping 18 matching lines...) Expand all
3027 void SyncManager::TriggerOnIncomingNotificationForTest( 3037 void SyncManager::TriggerOnIncomingNotificationForTest(
3028 const syncable::ModelTypeBitSet& model_types) { 3038 const syncable::ModelTypeBitSet& model_types) {
3029 syncable::ModelTypePayloadMap model_types_with_payloads = 3039 syncable::ModelTypePayloadMap model_types_with_payloads =
3030 syncable::ModelTypePayloadMapFromBitSet(model_types, 3040 syncable::ModelTypePayloadMapFromBitSet(model_types,
3031 std::string()); 3041 std::string());
3032 3042
3033 data_->OnIncomingNotification(model_types_with_payloads); 3043 data_->OnIncomingNotification(model_types_with_payloads);
3034 } 3044 }
3035 3045
3036 } // namespace sync_api 3046 } // namespace sync_api
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698