• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 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 "sync/engine/apply_control_data_updates.h"
6 
7 #include "base/metrics/histogram.h"
8 #include "sync/engine/conflict_resolver.h"
9 #include "sync/engine/conflict_util.h"
10 #include "sync/engine/syncer_util.h"
11 #include "sync/syncable/directory.h"
12 #include "sync/syncable/mutable_entry.h"
13 #include "sync/syncable/nigori_handler.h"
14 #include "sync/syncable/nigori_util.h"
15 #include "sync/syncable/syncable_write_transaction.h"
16 #include "sync/util/cryptographer.h"
17 
18 namespace syncer {
19 
20 using syncable::GET_TYPE_ROOT;
21 using syncable::IS_UNAPPLIED_UPDATE;
22 using syncable::IS_UNSYNCED;
23 using syncable::SERVER_SPECIFICS;
24 using syncable::SPECIFICS;
25 using syncable::SYNCER;
26 
ApplyControlDataUpdates(syncable::Directory * dir)27 void ApplyControlDataUpdates(syncable::Directory* dir) {
28   syncable::WriteTransaction trans(FROM_HERE, SYNCER, dir);
29 
30   std::vector<int64> handles;
31   dir->GetUnappliedUpdateMetaHandles(
32       &trans, ToFullModelTypeSet(ControlTypes()), &handles);
33 
34   // First, go through and manually apply any new top level datatype nodes (so
35   // that we don't have to worry about hitting a CONFLICT_HIERARCHY with an
36   // entry because we haven't applied its parent yet).
37   // TODO(sync): if at some point we support control datatypes with actual
38   // hierarchies we'll need to revisit this logic.
39   ModelTypeSet control_types = ControlTypes();
40   for (ModelTypeSet::Iterator iter = control_types.First(); iter.Good();
41        iter.Inc()) {
42     syncable::MutableEntry entry(&trans, syncable::GET_TYPE_ROOT, iter.Get());
43     if (!entry.good())
44       continue;
45     if (!entry.GetIsUnappliedUpdate())
46       continue;
47 
48     ModelType type = entry.GetServerModelType();
49     if (type == NIGORI) {
50       // Nigori node applications never fail.
51       ApplyNigoriUpdate(&trans,
52                         &entry,
53                         dir->GetCryptographer(&trans));
54     } else {
55       ApplyControlUpdate(&trans,
56                          &entry,
57                          dir->GetCryptographer(&trans));
58     }
59   }
60 
61   // Go through the rest of the unapplied control updates, skipping over any
62   // top level folders.
63   for (std::vector<int64>::const_iterator iter = handles.begin();
64        iter != handles.end(); ++iter) {
65     syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, *iter);
66     CHECK(entry.good());
67     ModelType type = entry.GetServerModelType();
68     CHECK(ControlTypes().Has(type));
69     if (!entry.GetUniqueServerTag().empty()) {
70       // We should have already applied all top level control nodes.
71       DCHECK(!entry.GetIsUnappliedUpdate());
72       continue;
73     }
74 
75     ApplyControlUpdate(&trans,
76                        &entry,
77                        dir->GetCryptographer(&trans));
78   }
79 }
80 
81 // Update the nigori handler with the server's nigori node.
82 //
83 // If we have a locally modified nigori node, we merge them manually. This
84 // handles the case where two clients both set a different passphrase. The
85 // second client to attempt to commit will go into a state of having pending
86 // keys, unioned the set of encrypted types, and eventually re-encrypt
87 // everything with the passphrase of the first client and commit the set of
88 // merged encryption keys. Until the second client provides the pending
89 // passphrase, the cryptographer will preserve the encryption keys based on the
90 // local passphrase, while the nigori node will preserve the server encryption
91 // keys.
ApplyNigoriUpdate(syncable::WriteTransaction * const trans,syncable::MutableEntry * const entry,Cryptographer * cryptographer)92 void ApplyNigoriUpdate(syncable::WriteTransaction* const trans,
93                        syncable::MutableEntry* const entry,
94                        Cryptographer* cryptographer) {
95   DCHECK(entry->GetIsUnappliedUpdate());
96 
97   // We apply the nigori update regardless of whether there's a conflict or
98   // not in order to preserve any new encrypted types or encryption keys.
99   // TODO(zea): consider having this return a bool reflecting whether it was a
100   // valid update or not, and in the case of invalid updates not overwrite the
101   // local data.
102   const sync_pb::NigoriSpecifics& nigori =
103       entry->GetServerSpecifics().nigori();
104   trans->directory()->GetNigoriHandler()->ApplyNigoriUpdate(nigori, trans);
105 
106   // Make sure any unsynced changes are properly encrypted as necessary.
107   // We only perform this if the cryptographer is ready. If not, these are
108   // re-encrypted at SetDecryptionPassphrase time (via ReEncryptEverything).
109   // This logic covers the case where the nigori update marked new datatypes
110   // for encryption, but didn't change the passphrase.
111   if (cryptographer->is_ready()) {
112     // Note that we don't bother to encrypt any data for which IS_UNSYNCED
113     // == false here. The machine that turned on encryption should know about
114     // and re-encrypt all synced data. It's possible it could get interrupted
115     // during this process, but we currently reencrypt everything at startup
116     // as well, so as soon as a client is restarted with this datatype marked
117     // for encryption, all the data should be updated as necessary.
118 
119     // If this fails, something is wrong with the cryptographer, but there's
120     // nothing we can do about it here.
121     DVLOG(1) << "Received new nigori, encrypting unsynced changes.";
122     syncable::ProcessUnsyncedChangesForEncryption(trans);
123   }
124 
125   if (!entry->GetIsUnsynced()) {  // Update only.
126     UpdateLocalDataFromServerData(trans, entry);
127   } else {  // Conflict.
128     const sync_pb::EntitySpecifics& server_specifics =
129         entry->GetServerSpecifics();
130     const sync_pb::NigoriSpecifics& server_nigori = server_specifics.nigori();
131     const sync_pb::EntitySpecifics& local_specifics =
132         entry->GetSpecifics();
133     const sync_pb::NigoriSpecifics& local_nigori = local_specifics.nigori();
134 
135     // We initialize the new nigori with the server state, and will override
136     // it as necessary below.
137     sync_pb::EntitySpecifics new_specifics = entry->GetServerSpecifics();
138     sync_pb::NigoriSpecifics* new_nigori = new_specifics.mutable_nigori();
139 
140     // If the cryptographer is not ready, another client set a new encryption
141     // passphrase. If we had migrated locally, we will re-migrate when the
142     // pending keys are provided. If we had set a new custom passphrase locally
143     // the user will have another chance to set a custom passphrase later
144     // (assuming they hadn't set a custom passphrase on the other client).
145     // Therefore, we only attempt to merge the nigori nodes if the cryptographer
146     // is ready.
147     // Note: we only update the encryption keybag if we're sure that we aren't
148     // invalidating the keystore_decryptor_token (i.e. we're either
149     // not migrated or we copying over all local state).
150     if (cryptographer->is_ready()) {
151       if (local_nigori.has_passphrase_type() &&
152           server_nigori.has_passphrase_type()) {
153         // They're both migrated, preserve the local nigori if the passphrase
154         // type is more conservative.
155         if (server_nigori.passphrase_type() ==
156                 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE &&
157             local_nigori.passphrase_type() !=
158                 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE) {
159           DCHECK(local_nigori.passphrase_type() ==
160                      sync_pb::NigoriSpecifics::FROZEN_IMPLICIT_PASSPHRASE ||
161                  local_nigori.passphrase_type() ==
162                      sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE);
163           new_nigori->CopyFrom(local_nigori);
164           cryptographer->GetKeys(new_nigori->mutable_encryption_keybag());
165         }
166       } else if (!local_nigori.has_passphrase_type() &&
167                  !server_nigori.has_passphrase_type()) {
168         // Set the explicit passphrase based on the local state. If the server
169         // had set an explict passphrase, we should have pending keys, so
170         // should not reach this code.
171         // Because neither side is migrated, we don't have to worry about the
172         // keystore decryptor token.
173         new_nigori->set_keybag_is_frozen(local_nigori.keybag_is_frozen());
174         cryptographer->GetKeys(new_nigori->mutable_encryption_keybag());
175       } else if (local_nigori.has_passphrase_type()) {
176         // Local is migrated but server is not. Copy over the local migrated
177         // data.
178         new_nigori->CopyFrom(local_nigori);
179         cryptographer->GetKeys(new_nigori->mutable_encryption_keybag());
180       }  // else leave the new nigori with the server state.
181     }
182 
183     // Always update to the safest set of encrypted types.
184     trans->directory()->GetNigoriHandler()->UpdateNigoriFromEncryptedTypes(
185         new_nigori,
186         trans);
187 
188     entry->PutSpecifics(new_specifics);
189     DVLOG(1) << "Resolving simple conflict, merging nigori nodes: "
190              << entry;
191 
192     conflict_util::OverwriteServerChanges(entry);
193 
194     UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
195                               ConflictResolver::NIGORI_MERGE,
196                               ConflictResolver::CONFLICT_RESOLUTION_SIZE);
197   }
198 }
199 
ApplyControlUpdate(syncable::WriteTransaction * const trans,syncable::MutableEntry * const entry,Cryptographer * cryptographer)200 void ApplyControlUpdate(syncable::WriteTransaction* const trans,
201                         syncable::MutableEntry* const entry,
202                         Cryptographer* cryptographer) {
203   DCHECK_NE(entry->GetServerModelType(), NIGORI);
204   DCHECK(entry->GetIsUnappliedUpdate());
205   if (entry->GetIsUnsynced()) {
206       // We just let the server win all conflicts with control types.
207     DVLOG(1) << "Ignoring local changes for control update.";
208     conflict_util::IgnoreLocalChanges(entry);
209     UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
210                               ConflictResolver::OVERWRITE_LOCAL,
211                               ConflictResolver::CONFLICT_RESOLUTION_SIZE);
212   }
213 
214   UpdateAttemptResponse response = AttemptToUpdateEntry(
215       trans, entry, cryptographer);
216   DCHECK_EQ(SUCCESS, response);
217 }
218 
219 }  // namespace syncer
220