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