| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "sync/engine/apply_control_data_updates.h" | 5 #include "sync/engine/apply_control_data_updates.h" |
| 6 | 6 |
| 7 #include "base/metrics/histogram.h" | 7 #include "base/metrics/histogram.h" |
| 8 #include "sync/engine/conflict_resolver.h" | 8 #include "sync/engine/conflict_resolver.h" |
| 9 #include "sync/engine/conflict_util.h" | 9 #include "sync/engine/conflict_util.h" |
| 10 #include "sync/engine/syncer_util.h" | 10 #include "sync/engine/syncer_util.h" |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 50 | 50 |
| 51 // Mainly for unit tests. We should have a Nigori node by this point. | 51 // Mainly for unit tests. We should have a Nigori node by this point. |
| 52 if (!nigori_node.good()) { | 52 if (!nigori_node.good()) { |
| 53 return false; | 53 return false; |
| 54 } | 54 } |
| 55 | 55 |
| 56 if (!nigori_node.Get(IS_UNAPPLIED_UPDATE)) { | 56 if (!nigori_node.Get(IS_UNAPPLIED_UPDATE)) { |
| 57 return true; | 57 return true; |
| 58 } | 58 } |
| 59 | 59 |
| 60 // We apply the nigori update regardless of whether there's a conflict or |
| 61 // not in order to preserve any new encrypted types or encryption keys. |
| 60 const sync_pb::NigoriSpecifics& nigori = | 62 const sync_pb::NigoriSpecifics& nigori = |
| 61 nigori_node.Get(SERVER_SPECIFICS).nigori(); | 63 nigori_node.Get(SERVER_SPECIFICS).nigori(); |
| 62 trans->directory()->GetNigoriHandler()->ApplyNigoriUpdate(nigori, trans); | 64 trans->directory()->GetNigoriHandler()->ApplyNigoriUpdate(nigori, trans); |
| 63 | 65 |
| 64 // Make sure any unsynced changes are properly encrypted as necessary. | 66 // Make sure any unsynced changes are properly encrypted as necessary. |
| 65 // We only perform this if the cryptographer is ready. If not, these are | 67 // We only perform this if the cryptographer is ready. If not, these are |
| 66 // re-encrypted at SetDecryptionPassphrase time (via ReEncryptEverything). | 68 // re-encrypted at SetDecryptionPassphrase time (via ReEncryptEverything). |
| 67 // This logic covers the case where the nigori update marked new datatypes | 69 // This logic covers the case where the nigori update marked new datatypes |
| 68 // for encryption, but didn't change the passphrase. | 70 // for encryption, but didn't change the passphrase. |
| 69 if (cryptographer->is_ready()) { | 71 if (cryptographer->is_ready()) { |
| 70 // Note that we don't bother to encrypt any data for which IS_UNSYNCED | 72 // Note that we don't bother to encrypt any data for which IS_UNSYNCED |
| 71 // == false here. The machine that turned on encryption should know about | 73 // == false here. The machine that turned on encryption should know about |
| 72 // and re-encrypt all synced data. It's possible it could get interrupted | 74 // and re-encrypt all synced data. It's possible it could get interrupted |
| 73 // during this process, but we currently reencrypt everything at startup | 75 // during this process, but we currently reencrypt everything at startup |
| 74 // as well, so as soon as a client is restarted with this datatype marked | 76 // as well, so as soon as a client is restarted with this datatype marked |
| 75 // for encryption, all the data should be updated as necessary. | 77 // for encryption, all the data should be updated as necessary. |
| 76 | 78 |
| 77 // If this fails, something is wrong with the cryptographer, but there's | 79 // If this fails, something is wrong with the cryptographer, but there's |
| 78 // nothing we can do about it here. | 80 // nothing we can do about it here. |
| 79 DVLOG(1) << "Received new nigori, encrypting unsynced changes."; | 81 DVLOG(1) << "Received new nigori, encrypting unsynced changes."; |
| 80 syncable::ProcessUnsyncedChangesForEncryption(trans); | 82 syncable::ProcessUnsyncedChangesForEncryption(trans); |
| 81 } | 83 } |
| 82 | 84 |
| 83 if (!nigori_node.Get(IS_UNSYNCED)) { // Update only. | 85 if (!nigori_node.Get(IS_UNSYNCED)) { // Update only. |
| 84 UpdateLocalDataFromServerData(trans, &nigori_node); | 86 UpdateLocalDataFromServerData(trans, &nigori_node); |
| 85 } else { // Conflict. | 87 } else { // Conflict. |
| 86 // Create a new set of specifics based on the server specifics (which | 88 const sync_pb::EntitySpecifics& server_specifics = |
| 87 // preserves their encryption keys). | 89 nigori_node.Get(SERVER_SPECIFICS); |
| 88 sync_pb::EntitySpecifics specifics = nigori_node.Get(SERVER_SPECIFICS); | 90 const sync_pb::NigoriSpecifics& server_nigori = server_specifics.nigori(); |
| 89 sync_pb::NigoriSpecifics* server_nigori = specifics.mutable_nigori(); | 91 const sync_pb::EntitySpecifics& local_specifics = |
| 90 // Store the merged set of encrypted types. | 92 nigori_node.Get(SPECIFICS); |
| 91 // (NigoriHandler::ApplyNigoriUpdate(..) will have merged the local types | 93 const sync_pb::NigoriSpecifics& local_nigori = local_specifics.nigori(); |
| 92 // already). | 94 |
| 95 // We initialize the new nigori with the server state, and will override |
| 96 // it as necessary below. |
| 97 sync_pb::EntitySpecifics new_specifics = nigori_node.Get(SERVER_SPECIFICS); |
| 98 sync_pb::NigoriSpecifics* new_nigori = new_specifics.mutable_nigori(); |
| 99 |
| 100 // If the cryptographer is not ready, another client set a new encryption |
| 101 // passphrase. If we had migrated locally, we will re-migrate when the |
| 102 // pending keys are provided. If we had set a new custom passphrase locally |
| 103 // the user will have another chance to set a custom passphrase later |
| 104 // (assuming they hadn't set a custom passphrase on the other client). |
| 105 // Therefore, we only attempt to merge the nigori nodes if the cryptographer |
| 106 // is ready. |
| 107 // Note: we only update the encryption keybag if we're sure that we aren't |
| 108 // invalidating the keystore_decryptor_token (i.e. we're either |
| 109 // not migrated or we copying over all local state). |
| 110 if (cryptographer->is_ready()) { |
| 111 if (local_nigori.has_passphrase_type() && |
| 112 server_nigori.has_passphrase_type()) { |
| 113 // They're both migrated, preserve the local nigori if the passphrase |
| 114 // type is more conservative. |
| 115 if (server_nigori.passphrase_type() == |
| 116 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE && |
| 117 local_nigori.passphrase_type() != |
| 118 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE) { |
| 119 DCHECK(local_nigori.passphrase_type() == |
| 120 sync_pb::NigoriSpecifics::FROZEN_IMPLICIT_PASSPHRASE || |
| 121 local_nigori.passphrase_type() == |
| 122 sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE); |
| 123 new_nigori->CopyFrom(local_nigori); |
| 124 cryptographer->GetKeys(new_nigori->mutable_encryption_keybag()); |
| 125 } |
| 126 } else if (!local_nigori.has_passphrase_type() && |
| 127 !server_nigori.has_passphrase_type()) { |
| 128 // Set the explicit passphrase based on the local state. If the server |
| 129 // had set an explict passphrase, we should have pending keys, so |
| 130 // should not reach this code. |
| 131 // Because neither side is migrated, we don't have to worry about the |
| 132 // keystore decryptor token. |
| 133 new_nigori->set_keybag_is_frozen(local_nigori.keybag_is_frozen()); |
| 134 cryptographer->GetKeys(new_nigori->mutable_encryption_keybag()); |
| 135 } else if (local_nigori.has_passphrase_type()) { |
| 136 // Local is migrated but server is not. Copy over the local migrated |
| 137 // data. |
| 138 new_nigori->CopyFrom(local_nigori); |
| 139 cryptographer->GetKeys(new_nigori->mutable_encryption_keybag()); |
| 140 } // else leave the new nigori with the server state. |
| 141 } |
| 142 |
| 143 // Always update to the safest set of encrypted types. |
| 93 trans->directory()->GetNigoriHandler()->UpdateNigoriFromEncryptedTypes( | 144 trans->directory()->GetNigoriHandler()->UpdateNigoriFromEncryptedTypes( |
| 94 server_nigori, | 145 new_nigori, |
| 95 trans); | 146 trans); |
| 96 // The cryptographer has the both the local and remote encryption keys | 147 |
| 97 // (added at NigoriHandler::ApplyNigoriUpdate(..) time). | 148 nigori_node.Put(SPECIFICS, new_specifics); |
| 98 // If the cryptographer is ready, then it already merged both sets of keys | |
| 99 // and we can store them back in. In that case, the remote key was already | |
| 100 // part of the local keybag, so we preserve the local key as the default | |
| 101 // (including whether it's an explicit key). | |
| 102 // If the cryptographer is not ready, then the user will have to provide | |
| 103 // the passphrase to decrypt the pending keys. When they do so, the | |
| 104 // SetDecryptionPassphrase code will act based on whether the server | |
| 105 // update has an explicit passphrase or not. | |
| 106 // - If the server had an explicit passphrase, that explicit passphrase | |
| 107 // will be preserved as the default encryption key. | |
| 108 // - If the server did not have an explicit passphrase, we assume the | |
| 109 // local passphrase is the most up to date and preserve the local | |
| 110 // default encryption key marked as an implicit passphrase. | |
| 111 // This works fine except for the case where we had locally set an | |
| 112 // explicit passphrase. In that case the nigori node will have the default | |
| 113 // key based on the local explicit passphassphrase, but will not have it | |
| 114 // marked as explicit. To fix this we'd have to track whether we have a | |
| 115 // explicit passphrase or not separate from the nigori, which would | |
| 116 // introduce even more complexity, so we leave it up to the user to reset | |
| 117 // that passphrase as an explicit one via settings. The goal here is to | |
| 118 // ensure both sets of encryption keys are preserved. | |
| 119 if (cryptographer->is_ready()) { | |
| 120 cryptographer->GetKeys(server_nigori->mutable_encryption_keybag()); | |
| 121 server_nigori->set_keybag_is_frozen( | |
| 122 nigori_node.Get(SPECIFICS).nigori().keybag_is_frozen()); | |
| 123 } | |
| 124 nigori_node.Put(SPECIFICS, specifics); | |
| 125 DVLOG(1) << "Resolving simple conflict, merging nigori nodes: " | 149 DVLOG(1) << "Resolving simple conflict, merging nigori nodes: " |
| 126 << nigori_node; | 150 << nigori_node; |
| 127 | 151 |
| 128 OverwriteServerChanges(&nigori_node); | 152 OverwriteServerChanges(&nigori_node); |
| 129 | 153 |
| 130 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 154 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 131 ConflictResolver::NIGORI_MERGE, | 155 ConflictResolver::NIGORI_MERGE, |
| 132 ConflictResolver::CONFLICT_RESOLUTION_SIZE); | 156 ConflictResolver::CONFLICT_RESOLUTION_SIZE); |
| 133 } | 157 } |
| 134 | 158 |
| 135 return true; | 159 return true; |
| 136 } | 160 } |
| 137 | 161 |
| 138 } // namespace syncer | 162 } // namespace syncer |
| OLD | NEW |