| 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/syncable/nigori_util.h" | 5 #include "sync/syncable/nigori_util.h" |
| 6 | 6 |
| 7 #include <queue> | 7 #include <queue> |
| 8 #include <string> | 8 #include <string> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| 11 #include "base/json/json_writer.h" | 11 #include "base/json/json_writer.h" |
| 12 #include "sync/syncable/directory.h" | 12 #include "sync/syncable/directory.h" |
| 13 #include "sync/syncable/entry.h" | 13 #include "sync/syncable/entry.h" |
| 14 #include "sync/syncable/mutable_entry.h" | 14 #include "sync/syncable/mutable_entry.h" |
| 15 #include "sync/syncable/syncable_util.h" | 15 #include "sync/syncable/syncable_util.h" |
| 16 #include "sync/syncable/write_transaction.h" | 16 #include "sync/syncable/write_transaction.h" |
| 17 #include "sync/util/cryptographer.h" | 17 #include "sync/util/cryptographer.h" |
| 18 | 18 |
| 19 namespace syncer { | 19 namespace syncer { |
| 20 namespace syncable { | 20 namespace syncable { |
| 21 | 21 |
| 22 bool ProcessUnsyncedChangesForEncryption( | 22 bool ProcessUnsyncedChangesForEncryption( |
| 23 WriteTransaction* const trans, | 23 WriteTransaction* const trans, |
| 24 syncer::Cryptographer* cryptographer) { | 24 Cryptographer* cryptographer) { |
| 25 DCHECK(cryptographer->is_ready()); | 25 DCHECK(cryptographer->is_ready()); |
| 26 // Get list of all datatypes with unsynced changes. It's possible that our | 26 // Get list of all datatypes with unsynced changes. It's possible that our |
| 27 // local changes need to be encrypted if encryption for that datatype was | 27 // local changes need to be encrypted if encryption for that datatype was |
| 28 // just turned on (and vice versa). | 28 // just turned on (and vice versa). |
| 29 // Note: we do not attempt to re-encrypt data with a new key here as key | 29 // Note: we do not attempt to re-encrypt data with a new key here as key |
| 30 // changes in this code path are likely due to consistency issues (we have | 30 // changes in this code path are likely due to consistency issues (we have |
| 31 // to be updated to a key we already have, e.g. an old key). | 31 // to be updated to a key we already have, e.g. an old key). |
| 32 std::vector<int64> handles; | 32 std::vector<int64> handles; |
| 33 GetUnsyncedEntries(trans, &handles); | 33 GetUnsyncedEntries(trans, &handles); |
| 34 for (size_t i = 0; i < handles.size(); ++i) { | 34 for (size_t i = 0; i < handles.size(); ++i) { |
| (...skipping 27 matching lines...) Expand all Loading... |
| 62 if (EntryNeedsEncryption(encrypted_types, entry)) | 62 if (EntryNeedsEncryption(encrypted_types, entry)) |
| 63 return false; | 63 return false; |
| 64 } | 64 } |
| 65 return true; | 65 return true; |
| 66 } | 66 } |
| 67 | 67 |
| 68 bool EntryNeedsEncryption(ModelTypeSet encrypted_types, | 68 bool EntryNeedsEncryption(ModelTypeSet encrypted_types, |
| 69 const Entry& entry) { | 69 const Entry& entry) { |
| 70 if (!entry.Get(UNIQUE_SERVER_TAG).empty()) | 70 if (!entry.Get(UNIQUE_SERVER_TAG).empty()) |
| 71 return false; // We don't encrypt unique server nodes. | 71 return false; // We don't encrypt unique server nodes. |
| 72 syncer::ModelType type = entry.GetModelType(); | 72 ModelType type = entry.GetModelType(); |
| 73 if (type == PASSWORDS || type == NIGORI) | 73 if (type == PASSWORDS || type == NIGORI) |
| 74 return false; | 74 return false; |
| 75 // Checking NON_UNIQUE_NAME is not necessary for the correctness of encrypting | 75 // Checking NON_UNIQUE_NAME is not necessary for the correctness of encrypting |
| 76 // the data, nor for determining if data is encrypted. We simply ensure it has | 76 // the data, nor for determining if data is encrypted. We simply ensure it has |
| 77 // been overwritten to avoid any possible leaks of sensitive data. | 77 // been overwritten to avoid any possible leaks of sensitive data. |
| 78 return SpecificsNeedsEncryption(encrypted_types, entry.Get(SPECIFICS)) || | 78 return SpecificsNeedsEncryption(encrypted_types, entry.Get(SPECIFICS)) || |
| 79 (encrypted_types.Has(type) && | 79 (encrypted_types.Has(type) && |
| 80 entry.Get(NON_UNIQUE_NAME) != kEncryptedString); | 80 entry.Get(NON_UNIQUE_NAME) != kEncryptedString); |
| 81 } | 81 } |
| 82 | 82 |
| 83 bool SpecificsNeedsEncryption(ModelTypeSet encrypted_types, | 83 bool SpecificsNeedsEncryption(ModelTypeSet encrypted_types, |
| 84 const sync_pb::EntitySpecifics& specifics) { | 84 const sync_pb::EntitySpecifics& specifics) { |
| 85 const ModelType type = GetModelTypeFromSpecifics(specifics); | 85 const ModelType type = GetModelTypeFromSpecifics(specifics); |
| 86 if (type == PASSWORDS || type == NIGORI) | 86 if (type == PASSWORDS || type == NIGORI) |
| 87 return false; // These types have their own encryption schemes. | 87 return false; // These types have their own encryption schemes. |
| 88 if (!encrypted_types.Has(type)) | 88 if (!encrypted_types.Has(type)) |
| 89 return false; // This type does not require encryption | 89 return false; // This type does not require encryption |
| 90 return !specifics.has_encrypted(); | 90 return !specifics.has_encrypted(); |
| 91 } | 91 } |
| 92 | 92 |
| 93 // Mainly for testing. | 93 // Mainly for testing. |
| 94 bool VerifyDataTypeEncryptionForTest( | 94 bool VerifyDataTypeEncryptionForTest( |
| 95 BaseTransaction* const trans, | 95 BaseTransaction* const trans, |
| 96 syncer::Cryptographer* cryptographer, | 96 Cryptographer* cryptographer, |
| 97 ModelType type, | 97 ModelType type, |
| 98 bool is_encrypted) { | 98 bool is_encrypted) { |
| 99 if (type == PASSWORDS || type == NIGORI) { | 99 if (type == PASSWORDS || type == NIGORI) { |
| 100 NOTREACHED(); | 100 NOTREACHED(); |
| 101 return true; | 101 return true; |
| 102 } | 102 } |
| 103 std::string type_tag = ModelTypeToRootTag(type); | 103 std::string type_tag = ModelTypeToRootTag(type); |
| 104 Entry type_root(trans, GET_BY_SERVER_TAG, type_tag); | 104 Entry type_root(trans, GET_BY_SERVER_TAG, type_tag); |
| 105 if (!type_root.good()) { | 105 if (!type_root.good()) { |
| 106 NOTREACHED(); | 106 NOTREACHED(); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 150 return false; | 150 return false; |
| 151 } | 151 } |
| 152 } | 152 } |
| 153 // Push the successor. | 153 // Push the successor. |
| 154 to_visit.push(child.Get(NEXT_ID)); | 154 to_visit.push(child.Get(NEXT_ID)); |
| 155 } | 155 } |
| 156 return true; | 156 return true; |
| 157 } | 157 } |
| 158 | 158 |
| 159 bool UpdateEntryWithEncryption( | 159 bool UpdateEntryWithEncryption( |
| 160 syncer::Cryptographer* cryptographer, | 160 Cryptographer* cryptographer, |
| 161 const sync_pb::EntitySpecifics& new_specifics, | 161 const sync_pb::EntitySpecifics& new_specifics, |
| 162 syncable::MutableEntry* entry) { | 162 syncable::MutableEntry* entry) { |
| 163 syncer::ModelType type = syncer::GetModelTypeFromSpecifics(new_specifics); | 163 ModelType type = GetModelTypeFromSpecifics(new_specifics); |
| 164 DCHECK_GE(type, syncer::FIRST_REAL_MODEL_TYPE); | 164 DCHECK_GE(type, FIRST_REAL_MODEL_TYPE); |
| 165 const sync_pb::EntitySpecifics& old_specifics = entry->Get(SPECIFICS); | 165 const sync_pb::EntitySpecifics& old_specifics = entry->Get(SPECIFICS); |
| 166 const syncer::ModelTypeSet encrypted_types = | 166 const ModelTypeSet encrypted_types = cryptographer->GetEncryptedTypes(); |
| 167 cryptographer->GetEncryptedTypes(); | |
| 168 // It's possible the nigori lost the set of encrypted types. If the current | 167 // It's possible the nigori lost the set of encrypted types. If the current |
| 169 // specifics are already encrypted, we want to ensure we continue encrypting. | 168 // specifics are already encrypted, we want to ensure we continue encrypting. |
| 170 bool was_encrypted = old_specifics.has_encrypted(); | 169 bool was_encrypted = old_specifics.has_encrypted(); |
| 171 sync_pb::EntitySpecifics generated_specifics; | 170 sync_pb::EntitySpecifics generated_specifics; |
| 172 if (new_specifics.has_encrypted()) { | 171 if (new_specifics.has_encrypted()) { |
| 173 NOTREACHED() << "New specifics already has an encrypted blob."; | 172 NOTREACHED() << "New specifics already has an encrypted blob."; |
| 174 return false; | 173 return false; |
| 175 } | 174 } |
| 176 if ((!SpecificsNeedsEncryption(encrypted_types, new_specifics) && | 175 if ((!SpecificsNeedsEncryption(encrypted_types, new_specifics) && |
| 177 !was_encrypted) || | 176 !was_encrypted) || |
| 178 !cryptographer->is_initialized()) { | 177 !cryptographer->is_initialized()) { |
| 179 // No encryption required or we are unable to encrypt. | 178 // No encryption required or we are unable to encrypt. |
| 180 generated_specifics.CopyFrom(new_specifics); | 179 generated_specifics.CopyFrom(new_specifics); |
| 181 } else { | 180 } else { |
| 182 // Encrypt new_specifics into generated_specifics. | 181 // Encrypt new_specifics into generated_specifics. |
| 183 if (VLOG_IS_ON(2)) { | 182 if (VLOG_IS_ON(2)) { |
| 184 scoped_ptr<DictionaryValue> value(entry->ToValue()); | 183 scoped_ptr<DictionaryValue> value(entry->ToValue()); |
| 185 std::string info; | 184 std::string info; |
| 186 base::JSONWriter::WriteWithOptions(value.get(), | 185 base::JSONWriter::WriteWithOptions(value.get(), |
| 187 base::JSONWriter::OPTIONS_PRETTY_PRINT, | 186 base::JSONWriter::OPTIONS_PRETTY_PRINT, |
| 188 &info); | 187 &info); |
| 189 DVLOG(2) << "Encrypting specifics of type " | 188 DVLOG(2) << "Encrypting specifics of type " |
| 190 << syncer::ModelTypeToString(type) | 189 << ModelTypeToString(type) |
| 191 << " with content: " | 190 << " with content: " |
| 192 << info; | 191 << info; |
| 193 } | 192 } |
| 194 // Only copy over the old specifics if it is of the right type and already | 193 // Only copy over the old specifics if it is of the right type and already |
| 195 // encrypted. The first time we encrypt a node we start from scratch, hence | 194 // encrypted. The first time we encrypt a node we start from scratch, hence |
| 196 // removing all the unencrypted data, but from then on we only want to | 195 // removing all the unencrypted data, but from then on we only want to |
| 197 // update the node if the data changes or the encryption key changes. | 196 // update the node if the data changes or the encryption key changes. |
| 198 if (syncer::GetModelTypeFromSpecifics(old_specifics) == type && | 197 if (GetModelTypeFromSpecifics(old_specifics) == type && |
| 199 was_encrypted) { | 198 was_encrypted) { |
| 200 generated_specifics.CopyFrom(old_specifics); | 199 generated_specifics.CopyFrom(old_specifics); |
| 201 } else { | 200 } else { |
| 202 syncer::AddDefaultFieldValue(type, &generated_specifics); | 201 AddDefaultFieldValue(type, &generated_specifics); |
| 203 } | 202 } |
| 204 // Does not change anything if underlying encrypted blob was already up | 203 // Does not change anything if underlying encrypted blob was already up |
| 205 // to date and encrypted with the default key. | 204 // to date and encrypted with the default key. |
| 206 if (!cryptographer->Encrypt(new_specifics, | 205 if (!cryptographer->Encrypt(new_specifics, |
| 207 generated_specifics.mutable_encrypted())) { | 206 generated_specifics.mutable_encrypted())) { |
| 208 NOTREACHED() << "Could not encrypt data for node of type " | 207 NOTREACHED() << "Could not encrypt data for node of type " |
| 209 << syncer::ModelTypeToString(type); | 208 << ModelTypeToString(type); |
| 210 return false; | 209 return false; |
| 211 } | 210 } |
| 212 } | 211 } |
| 213 | 212 |
| 214 // It's possible this entry was encrypted but didn't properly overwrite the | 213 // It's possible this entry was encrypted but didn't properly overwrite the |
| 215 // non_unique_name (see crbug.com/96314). | 214 // non_unique_name (see crbug.com/96314). |
| 216 bool encrypted_without_overwriting_name = (was_encrypted && | 215 bool encrypted_without_overwriting_name = (was_encrypted && |
| 217 entry->Get(syncable::NON_UNIQUE_NAME) != kEncryptedString); | 216 entry->Get(syncable::NON_UNIQUE_NAME) != kEncryptedString); |
| 218 | 217 |
| 219 // If we're encrypted but the name wasn't overwritten properly we still want | 218 // If we're encrypted but the name wasn't overwritten properly we still want |
| 220 // to rewrite the entry, irrespective of whether the specifics match. | 219 // to rewrite the entry, irrespective of whether the specifics match. |
| 221 if (!encrypted_without_overwriting_name && | 220 if (!encrypted_without_overwriting_name && |
| 222 old_specifics.SerializeAsString() == | 221 old_specifics.SerializeAsString() == |
| 223 generated_specifics.SerializeAsString()) { | 222 generated_specifics.SerializeAsString()) { |
| 224 DVLOG(2) << "Specifics of type " << syncer::ModelTypeToString(type) | 223 DVLOG(2) << "Specifics of type " << ModelTypeToString(type) |
| 225 << " already match, dropping change."; | 224 << " already match, dropping change."; |
| 226 return true; | 225 return true; |
| 227 } | 226 } |
| 228 | 227 |
| 229 if (generated_specifics.has_encrypted()) { | 228 if (generated_specifics.has_encrypted()) { |
| 230 // Overwrite the possibly sensitive non-specifics data. | 229 // Overwrite the possibly sensitive non-specifics data. |
| 231 entry->Put(syncable::NON_UNIQUE_NAME, kEncryptedString); | 230 entry->Put(syncable::NON_UNIQUE_NAME, kEncryptedString); |
| 232 // For bookmarks we actually put bogus data into the unencrypted specifics, | 231 // For bookmarks we actually put bogus data into the unencrypted specifics, |
| 233 // else the server will try to do it for us. | 232 // else the server will try to do it for us. |
| 234 if (type == syncer::BOOKMARKS) { | 233 if (type == BOOKMARKS) { |
| 235 sync_pb::BookmarkSpecifics* bookmark_specifics = | 234 sync_pb::BookmarkSpecifics* bookmark_specifics = |
| 236 generated_specifics.mutable_bookmark(); | 235 generated_specifics.mutable_bookmark(); |
| 237 if (!entry->Get(syncable::IS_DIR)) | 236 if (!entry->Get(syncable::IS_DIR)) |
| 238 bookmark_specifics->set_url(kEncryptedString); | 237 bookmark_specifics->set_url(kEncryptedString); |
| 239 bookmark_specifics->set_title(kEncryptedString); | 238 bookmark_specifics->set_title(kEncryptedString); |
| 240 } | 239 } |
| 241 } | 240 } |
| 242 entry->Put(syncable::SPECIFICS, generated_specifics); | 241 entry->Put(syncable::SPECIFICS, generated_specifics); |
| 243 DVLOG(1) << "Overwriting specifics of type " | 242 DVLOG(1) << "Overwriting specifics of type " |
| 244 << syncer::ModelTypeToString(type) | 243 << ModelTypeToString(type) |
| 245 << " and marking for syncing."; | 244 << " and marking for syncing."; |
| 246 syncable::MarkForSyncing(entry); | 245 syncable::MarkForSyncing(entry); |
| 247 return true; | 246 return true; |
| 248 } | 247 } |
| 249 | 248 |
| 250 } // namespace syncable | 249 } // namespace syncable |
| 251 } // namespace syncer | 250 } // namespace syncer |
| OLD | NEW |