| Index: chrome/browser/sync/engine/syncapi.cc
|
| diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc
|
| index c242d47d1cf92cee8524b6def29d42694fb3e421..e970c81c35156f02e92735ec868ac1400c1a70ca 100644
|
| --- a/chrome/browser/sync/engine/syncapi.cc
|
| +++ b/chrome/browser/sync/engine/syncapi.cc
|
| @@ -40,6 +40,7 @@
|
| #include "chrome/browser/sync/protocol/password_specifics.pb.h"
|
| #include "chrome/browser/sync/protocol/preference_specifics.pb.h"
|
| #include "chrome/browser/sync/protocol/service_constants.h"
|
| +#include "chrome/browser/sync/protocol/sync.pb.h"
|
| #include "chrome/browser/sync/protocol/theme_specifics.pb.h"
|
| #include "chrome/browser/sync/protocol/typed_url_specifics.pb.h"
|
| #include "chrome/browser/sync/sessions/sync_session_context.h"
|
| @@ -64,6 +65,8 @@ using browser_sync::AllStatus;
|
| using browser_sync::AllStatusEvent;
|
| using browser_sync::AuthWatcher;
|
| using browser_sync::AuthWatcherEvent;
|
| +using browser_sync::Cryptographer;
|
| +using browser_sync::KeyParams;
|
| using browser_sync::ModelSafeRoutingInfo;
|
| using browser_sync::ModelSafeWorker;
|
| using browser_sync::ModelSafeWorkerRegistrar;
|
| @@ -71,6 +74,7 @@ using browser_sync::Syncer;
|
| using browser_sync::SyncerEvent;
|
| using browser_sync::SyncerThread;
|
| using browser_sync::UserSettings;
|
| +using browser_sync::kNigoriTag;
|
| using browser_sync::sessions::SyncSessionContext;
|
| using notifier::TalkMediator;
|
| using notifier::TalkMediatorImpl;
|
| @@ -80,6 +84,7 @@ using std::string;
|
| using std::vector;
|
| using syncable::Directory;
|
| using syncable::DirectoryManager;
|
| +using syncable::Entry;
|
| using syncable::SPECIFICS;
|
|
|
| typedef GoogleServiceAuthError AuthError;
|
| @@ -174,6 +179,23 @@ std::string BaseNode::GenerateSyncableHash(
|
| return encode_output;
|
| }
|
|
|
| +bool BaseNode::DecryptIfNecessary(Entry* entry) {
|
| + if (GetIsFolder()) return true; // Ignore the top-level password folder.
|
| + const sync_pb::EntitySpecifics& specifics =
|
| + entry->Get(syncable::SPECIFICS);
|
| + if (specifics.HasExtension(sync_pb::password)) {
|
| + const sync_pb::EncryptedData& encrypted =
|
| + specifics.GetExtension(sync_pb::password).encrypted();
|
| + scoped_ptr<sync_pb::PasswordSpecificsData> data(
|
| + new sync_pb::PasswordSpecificsData);
|
| + if (!GetTransaction()->GetCryptographer()->Decrypt(encrypted,
|
| + data.get()))
|
| + return false;
|
| + password_data_.swap(data);
|
| + }
|
| + return true;
|
| +}
|
| +
|
| int64 BaseNode::GetParentId() const {
|
| return IdToMetahandle(GetTransaction()->GetWrappedTrans(),
|
| GetEntry()->Get(syncable::PARENT_ID));
|
| @@ -249,13 +271,10 @@ const sync_pb::NigoriSpecifics& BaseNode::GetNigoriSpecifics() const {
|
| return GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::nigori);
|
| }
|
|
|
| -bool BaseNode::GetPasswordSpecifics(sync_pb::PasswordSpecificsData* data)
|
| - const {
|
| +const sync_pb::PasswordSpecificsData& BaseNode::GetPasswordSpecifics() const {
|
| DCHECK(GetModelType() == syncable::PASSWORDS);
|
| - DCHECK(data);
|
| - const sync_pb::PasswordSpecifics& specifics =
|
| - GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::password);
|
| - return data->ParseFromString(specifics.blob());
|
| + DCHECK(password_data_.get());
|
| + return *password_data_;
|
| }
|
|
|
| const sync_pb::PreferenceSpecifics& BaseNode::GetPreferenceSpecifics() const {
|
| @@ -356,7 +375,11 @@ void WriteNode::SetPasswordSpecifics(
|
| std::string serialized_data;
|
| data.SerializeToString(&serialized_data);
|
| sync_pb::PasswordSpecifics new_value;
|
| - new_value.set_blob(serialized_data);
|
| + if (!GetTransaction()->GetCryptographer()->Encrypt(
|
| + data,
|
| + new_value.mutable_encrypted()))
|
| + NOTREACHED();
|
| +
|
| PutPasswordSpecificsAndMarkForSyncing(new_value);
|
| }
|
|
|
| @@ -451,7 +474,8 @@ bool WriteNode::InitByIdLookup(int64 id) {
|
| DCHECK_NE(id, kInvalidId);
|
| entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
|
| syncable::GET_BY_HANDLE, id);
|
| - return (entry_->good() && !entry_->Get(syncable::IS_DEL));
|
| + return (entry_->good() && !entry_->Get(syncable::IS_DEL) &&
|
| + DecryptIfNecessary(entry_));
|
| }
|
|
|
| // Find a node by client tag, and bind this WriteNode to it.
|
| @@ -467,7 +491,8 @@ bool WriteNode::InitByClientTagLookup(syncable::ModelType model_type,
|
|
|
| entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
|
| syncable::GET_BY_CLIENT_TAG, hash);
|
| - return (entry_->good() && !entry_->Get(syncable::IS_DEL));
|
| + return (entry_->good() && !entry_->Get(syncable::IS_DEL) &&
|
| + DecryptIfNecessary(entry_));
|
| }
|
|
|
| bool WriteNode::InitByTagLookup(const std::string& tag) {
|
| @@ -703,7 +728,7 @@ bool ReadNode::InitByIdLookup(int64 id) {
|
| LOG_IF(WARNING, model_type == syncable::UNSPECIFIED ||
|
| model_type == syncable::TOP_LEVEL_FOLDER)
|
| << "SyncAPI InitByIdLookup referencing unusual object.";
|
| - return true;
|
| + return DecryptIfNecessary(entry_);
|
| }
|
|
|
| bool ReadNode::InitByClientTagLookup(syncable::ModelType model_type,
|
| @@ -716,7 +741,8 @@ bool ReadNode::InitByClientTagLookup(syncable::ModelType model_type,
|
|
|
| entry_ = new syncable::Entry(transaction_->GetWrappedTrans(),
|
| syncable::GET_BY_CLIENT_TAG, hash);
|
| - return (entry_->good() && !entry_->Get(syncable::IS_DEL));
|
| + return (entry_->good() && !entry_->Get(syncable::IS_DEL) &&
|
| + DecryptIfNecessary(entry_));
|
| }
|
|
|
| const syncable::Entry* ReadNode::GetEntry() const {
|
| @@ -741,7 +767,7 @@ bool ReadNode::InitByTagLookup(const std::string& tag) {
|
| LOG_IF(WARNING, model_type == syncable::UNSPECIFIED ||
|
| model_type == syncable::TOP_LEVEL_FOLDER)
|
| << "SyncAPI InitByTagLookup referencing unusually typed object.";
|
| - return true;
|
| + return DecryptIfNecessary(entry_);
|
| }
|
|
|
| //////////////////////////////////////////////////////////////////////////
|
| @@ -891,6 +917,8 @@ class SyncManager::SyncInternal
|
| // Tell the sync engine to start the syncing process.
|
| void StartSyncing();
|
|
|
| + void SetPassphrase(const std::string& passphrase);
|
| +
|
| // Call periodically from a database-safe thread to persist recent changes
|
| // to the syncapi model.
|
| void SaveChanges();
|
| @@ -1215,6 +1243,10 @@ void SyncManager::StartSyncing() {
|
| data_->StartSyncing();
|
| }
|
|
|
| +void SyncManager::SetPassphrase(const std::string& passphrase) {
|
| + data_->SetPassphrase(passphrase);
|
| +}
|
| +
|
| bool SyncManager::RequestPause() {
|
| return data_->syncer_thread()->RequestPause();
|
| }
|
| @@ -1482,6 +1514,25 @@ void SyncManager::SyncInternal::RaiseAuthNeededEvent() {
|
| observer_->OnAuthError(AuthError(auth_problem_));
|
| }
|
|
|
| +void SyncManager::SyncInternal::SetPassphrase(
|
| + const std::string& passphrase) {
|
| + Cryptographer* cryptographer = dir_manager()->cryptographer();
|
| + KeyParams params = {"localhost", "dummy", passphrase};
|
| + if (cryptographer->has_pending_keys()) {
|
| + if (!cryptographer->DecryptPendingKeys(params)) {
|
| + observer_->OnPassphraseRequired();
|
| + return;
|
| + }
|
| + // Nudge the syncer so that passwords updates that were waiting for this
|
| + // passphrase get applied as soon as possible.
|
| + sync_manager_->RequestNudge();
|
| + } else {
|
| + cryptographer->AddKey(params);
|
| + // TODO(albertb): Update the Nigori node on the server with the new keys.
|
| + }
|
| + observer_->OnPassphraseAccepted();
|
| +}
|
| +
|
| SyncManager::~SyncManager() {
|
| delete data_;
|
| }
|
| @@ -1767,7 +1818,6 @@ void SyncManager::SyncInternal::HandleChannelEvent(const SyncerEvent& event) {
|
| // download; if so, we should signal that initialization is complete.
|
| if (event.snapshot->is_share_usable)
|
| MarkAndNotifyInitializationComplete();
|
| - return;
|
| }
|
|
|
| if (!observer_)
|
| @@ -1781,6 +1831,38 @@ void SyncManager::SyncInternal::HandleChannelEvent(const SyncerEvent& event) {
|
| // Notifications are sent at the end of every sync cycle, regardless of
|
| // whether we should sync again.
|
| if (event.what_happened == SyncerEvent::SYNC_CYCLE_ENDED) {
|
| +
|
| + ModelSafeRoutingInfo enabled_types;
|
| + registrar_->GetModelSafeRoutingInfo(&enabled_types);
|
| + if (enabled_types.count(syncable::PASSWORDS) > 0) {
|
| + Cryptographer* cryptographer =
|
| + GetUserShare()->dir_manager->cryptographer();
|
| + if (!cryptographer->is_ready() && !cryptographer->has_pending_keys()) {
|
| + sync_api::ReadTransaction trans(GetUserShare());
|
| + sync_api::ReadNode node(&trans);
|
| + if (!node.InitByTagLookup(kNigoriTag)) {
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| + const sync_pb::NigoriSpecifics& nigori = node.GetNigoriSpecifics();
|
| + if (!nigori.encrypted().blob().empty()) {
|
| + if (cryptographer->CanDecrypt(nigori.encrypted())) {
|
| + cryptographer->SetKeys(nigori.encrypted());
|
| + } else {
|
| + cryptographer->SetPendingKeys(nigori.encrypted());
|
| + }
|
| + }
|
| + }
|
| + // If we've completed a sync cycle and the cryptographer isn't ready yet,
|
| + // prompt the user for a passphrase.
|
| + if (!cryptographer->is_ready()) {
|
| + observer_->OnPassphraseRequired();
|
| + }
|
| + }
|
| +
|
| + if (!initialized())
|
| + return;
|
| +
|
| if (!event.snapshot->has_more_to_sync) {
|
| observer_->OnSyncCycleCompleted(event.snapshot);
|
| }
|
| @@ -2026,7 +2108,8 @@ BaseTransaction::BaseTransaction(UserShare* share)
|
| : lookup_(NULL) {
|
| DCHECK(share && share->dir_manager.get());
|
| lookup_ = new syncable::ScopedDirLookup(share->dir_manager.get(),
|
| - share->authenticated_name);
|
| + share->authenticated_name);
|
| + cryptographer_ = share->dir_manager->cryptographer();
|
| if (!(lookup_->good()))
|
| DCHECK(false) << "ScopedDirLookup failed on valid DirManager.";
|
| }
|
|
|