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 3// found in the LICENSE file. 4 5#include "chrome/browser/sync/glue/password_model_associator.h" 6 7#include <set> 8 9#include "base/location.h" 10#include "base/metrics/histogram.h" 11#include "base/stl_util.h" 12#include "base/strings/utf_string_conversions.h" 13#include "chrome/browser/password_manager/password_store.h" 14#include "chrome/browser/sync/profile_sync_service.h" 15#include "content/public/common/password_form.h" 16#include "net/base/escape.h" 17#include "sync/api/sync_error.h" 18#include "sync/internal_api/public/read_node.h" 19#include "sync/internal_api/public/read_transaction.h" 20#include "sync/internal_api/public/write_node.h" 21#include "sync/internal_api/public/write_transaction.h" 22#include "sync/protocol/password_specifics.pb.h" 23 24using content::BrowserThread; 25 26namespace browser_sync { 27 28const char kPasswordTag[] = "google_chrome_passwords"; 29 30PasswordModelAssociator::PasswordModelAssociator( 31 ProfileSyncService* sync_service, 32 PasswordStore* password_store, 33 DataTypeErrorHandler* error_handler) 34 : sync_service_(sync_service), 35 password_store_(password_store), 36 password_node_id_(syncer::kInvalidId), 37 abort_association_requested_(false), 38 expected_loop_(base::MessageLoop::current()), 39 error_handler_(error_handler) { 40 DCHECK(sync_service_); 41#if defined(OS_MACOSX) 42 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); 43#else 44 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 45#endif 46} 47 48PasswordModelAssociator::~PasswordModelAssociator() { 49 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); 50} 51 52syncer::SyncError PasswordModelAssociator::AssociateModels( 53 syncer::SyncMergeResult* local_merge_result, 54 syncer::SyncMergeResult* syncer_merge_result) { 55 DCHECK(expected_loop_ == base::MessageLoop::current()); 56 57 // We must not be holding a transaction when we interact with the password 58 // store, as it can post tasks to the UI thread which can itself be blocked 59 // on our transaction, resulting in deadlock. (http://crbug.com/70658) 60 std::vector<content::PasswordForm*> passwords; 61 if (!password_store_->FillAutofillableLogins(&passwords) || 62 !password_store_->FillBlacklistLogins(&passwords)) { 63 STLDeleteElements(&passwords); 64 65 // Password store often fails to load passwords. Track failures with UMA. 66 // (http://crbug.com/249000) 67 UMA_HISTOGRAM_ENUMERATION("Sync.LocalDataFailedToLoad", 68 ModelTypeToHistogramInt(syncer::PASSWORDS), 69 syncer::MODEL_TYPE_COUNT); 70 return syncer::SyncError(FROM_HERE, 71 syncer::SyncError::DATATYPE_ERROR, 72 "Could not get the password entries.", 73 model_type()); 74 } 75 76 PasswordVector new_passwords; 77 PasswordVector updated_passwords; 78 { 79 base::AutoLock lock(association_lock_); 80 if (abort_association_requested_) 81 return syncer::SyncError(); 82 83 std::set<std::string> current_passwords; 84 syncer::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare()); 85 syncer::ReadNode password_root(&trans); 86 if (password_root.InitByTagLookup(kPasswordTag) != 87 syncer::BaseNode::INIT_OK) { 88 return error_handler_->CreateAndUploadError( 89 FROM_HERE, 90 "Server did not create the top-level password node. We " 91 "might be running against an out-of-date server.", 92 model_type()); 93 } 94 95 for (std::vector<content::PasswordForm*>::iterator ix = 96 passwords.begin(); 97 ix != passwords.end(); ++ix) { 98 std::string tag = MakeTag(**ix); 99 100 syncer::ReadNode node(&trans); 101 if (node.InitByClientTagLookup(syncer::PASSWORDS, tag) == 102 syncer::BaseNode::INIT_OK) { 103 const sync_pb::PasswordSpecificsData& password = 104 node.GetPasswordSpecifics(); 105 DCHECK_EQ(tag, MakeTag(password)); 106 107 content::PasswordForm new_password; 108 109 if (MergePasswords(password, **ix, &new_password)) { 110 syncer::WriteNode write_node(&trans); 111 if (write_node.InitByClientTagLookup(syncer::PASSWORDS, tag) != 112 syncer::BaseNode::INIT_OK) { 113 STLDeleteElements(&passwords); 114 return error_handler_->CreateAndUploadError( 115 FROM_HERE, 116 "Failed to edit password sync node.", 117 model_type()); 118 } 119 WriteToSyncNode(new_password, &write_node); 120 updated_passwords.push_back(new_password); 121 } 122 123 Associate(&tag, node.GetId()); 124 } else { 125 syncer::WriteNode node(&trans); 126 syncer::WriteNode::InitUniqueByCreationResult result = 127 node.InitUniqueByCreation(syncer::PASSWORDS, password_root, tag); 128 if (result != syncer::WriteNode::INIT_SUCCESS) { 129 STLDeleteElements(&passwords); 130 return error_handler_->CreateAndUploadError( 131 FROM_HERE, 132 "Failed to create password sync node.", 133 model_type()); 134 } 135 136 WriteToSyncNode(**ix, &node); 137 138 Associate(&tag, node.GetId()); 139 } 140 141 current_passwords.insert(tag); 142 } 143 144 STLDeleteElements(&passwords); 145 146 int64 sync_child_id = password_root.GetFirstChildId(); 147 while (sync_child_id != syncer::kInvalidId) { 148 syncer::ReadNode sync_child_node(&trans); 149 if (sync_child_node.InitByIdLookup(sync_child_id) != 150 syncer::BaseNode::INIT_OK) { 151 return error_handler_->CreateAndUploadError( 152 FROM_HERE, 153 "Failed to fetch child node.", 154 model_type()); 155 } 156 const sync_pb::PasswordSpecificsData& password = 157 sync_child_node.GetPasswordSpecifics(); 158 std::string tag = MakeTag(password); 159 160 // The password only exists on the server. Add it to the local 161 // model. 162 if (current_passwords.find(tag) == current_passwords.end()) { 163 content::PasswordForm new_password; 164 165 CopyPassword(password, &new_password); 166 Associate(&tag, sync_child_node.GetId()); 167 new_passwords.push_back(new_password); 168 } 169 170 sync_child_id = sync_child_node.GetSuccessorId(); 171 } 172 } 173 174 // We must not be holding a transaction when we interact with the password 175 // store, as it can post tasks to the UI thread which can itself be blocked 176 // on our transaction, resulting in deadlock. (http://crbug.com/70658) 177 return WriteToPasswordStore(&new_passwords, 178 &updated_passwords, 179 NULL); 180} 181 182bool PasswordModelAssociator::DeleteAllNodes( 183 syncer::WriteTransaction* trans) { 184 DCHECK(expected_loop_ == base::MessageLoop::current()); 185 for (PasswordToSyncIdMap::iterator node_id = id_map_.begin(); 186 node_id != id_map_.end(); ++node_id) { 187 syncer::WriteNode sync_node(trans); 188 if (sync_node.InitByIdLookup(node_id->second) != 189 syncer::BaseNode::INIT_OK) { 190 LOG(ERROR) << "Typed url node lookup failed."; 191 return false; 192 } 193 sync_node.Tombstone(); 194 } 195 196 id_map_.clear(); 197 id_map_inverse_.clear(); 198 return true; 199} 200 201syncer::SyncError PasswordModelAssociator::DisassociateModels() { 202 id_map_.clear(); 203 id_map_inverse_.clear(); 204 return syncer::SyncError(); 205} 206 207bool PasswordModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) { 208 DCHECK(has_nodes); 209 *has_nodes = false; 210 int64 password_sync_id; 211 if (!GetSyncIdForTaggedNode(kPasswordTag, &password_sync_id)) { 212 LOG(ERROR) << "Server did not create the top-level password node. We " 213 << "might be running against an out-of-date server."; 214 return false; 215 } 216 syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare()); 217 218 syncer::ReadNode password_node(&trans); 219 if (password_node.InitByIdLookup(password_sync_id) != 220 syncer::BaseNode::INIT_OK) { 221 LOG(ERROR) << "Server did not create the top-level password node. We " 222 << "might be running against an out-of-date server."; 223 return false; 224 } 225 226 // The sync model has user created nodes if the password folder has any 227 // children. 228 *has_nodes = password_node.HasChildren(); 229 return true; 230} 231 232void PasswordModelAssociator::AbortAssociation() { 233 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 234 base::AutoLock lock(association_lock_); 235 abort_association_requested_ = true; 236} 237 238bool PasswordModelAssociator::CryptoReadyIfNecessary() { 239 // We only access the cryptographer while holding a transaction. 240 syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare()); 241 // We always encrypt passwords, so no need to check if encryption is enabled. 242 return sync_service_->IsCryptographerReady(&trans); 243} 244 245const std::string* PasswordModelAssociator::GetChromeNodeFromSyncId( 246 int64 sync_id) { 247 return NULL; 248} 249 250bool PasswordModelAssociator::InitSyncNodeFromChromeId( 251 const std::string& node_id, 252 syncer::BaseNode* sync_node) { 253 return false; 254} 255 256int64 PasswordModelAssociator::GetSyncIdFromChromeId( 257 const std::string& password) { 258 PasswordToSyncIdMap::const_iterator iter = id_map_.find(password); 259 return iter == id_map_.end() ? syncer::kInvalidId : iter->second; 260} 261 262void PasswordModelAssociator::Associate( 263 const std::string* password, int64 sync_id) { 264 DCHECK(expected_loop_ == base::MessageLoop::current()); 265 DCHECK_NE(syncer::kInvalidId, sync_id); 266 DCHECK(id_map_.find(*password) == id_map_.end()); 267 DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end()); 268 id_map_[*password] = sync_id; 269 id_map_inverse_[sync_id] = *password; 270} 271 272void PasswordModelAssociator::Disassociate(int64 sync_id) { 273 DCHECK(expected_loop_ == base::MessageLoop::current()); 274 SyncIdToPasswordMap::iterator iter = id_map_inverse_.find(sync_id); 275 if (iter == id_map_inverse_.end()) 276 return; 277 CHECK(id_map_.erase(iter->second)); 278 id_map_inverse_.erase(iter); 279} 280 281bool PasswordModelAssociator::GetSyncIdForTaggedNode(const std::string& tag, 282 int64* sync_id) { 283 syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare()); 284 syncer::ReadNode sync_node(&trans); 285 if (sync_node.InitByTagLookup(tag.c_str()) != syncer::BaseNode::INIT_OK) 286 return false; 287 *sync_id = sync_node.GetId(); 288 return true; 289} 290 291syncer::SyncError PasswordModelAssociator::WriteToPasswordStore( 292 const PasswordVector* new_passwords, 293 const PasswordVector* updated_passwords, 294 const PasswordVector* deleted_passwords) { 295 if (new_passwords) { 296 for (PasswordVector::const_iterator password = new_passwords->begin(); 297 password != new_passwords->end(); ++password) { 298 password_store_->AddLoginImpl(*password); 299 } 300 } 301 302 if (updated_passwords) { 303 for (PasswordVector::const_iterator password = updated_passwords->begin(); 304 password != updated_passwords->end(); ++password) { 305 password_store_->UpdateLoginImpl(*password); 306 } 307 } 308 309 if (deleted_passwords) { 310 for (PasswordVector::const_iterator password = deleted_passwords->begin(); 311 password != deleted_passwords->end(); ++password) { 312 password_store_->RemoveLoginImpl(*password); 313 } 314 } 315 316 if (new_passwords || updated_passwords || deleted_passwords) { 317 // We have to notify password store observers of the change by hand since 318 // we use internal password store interfaces to make changes synchronously. 319 password_store_->PostNotifyLoginsChanged(); 320 } 321 return syncer::SyncError(); 322} 323 324// static 325void PasswordModelAssociator::CopyPassword( 326 const sync_pb::PasswordSpecificsData& password, 327 content::PasswordForm* new_password) { 328 new_password->scheme = 329 static_cast<content::PasswordForm::Scheme>(password.scheme()); 330 new_password->signon_realm = password.signon_realm(); 331 new_password->origin = GURL(password.origin()); 332 new_password->action = GURL(password.action()); 333 new_password->username_element = 334 UTF8ToUTF16(password.username_element()); 335 new_password->password_element = 336 UTF8ToUTF16(password.password_element()); 337 new_password->username_value = 338 UTF8ToUTF16(password.username_value()); 339 new_password->password_value = 340 UTF8ToUTF16(password.password_value()); 341 new_password->ssl_valid = password.ssl_valid(); 342 new_password->preferred = password.preferred(); 343 new_password->date_created = 344 base::Time::FromInternalValue(password.date_created()); 345 new_password->blacklisted_by_user = 346 password.blacklisted(); 347} 348 349// static 350bool PasswordModelAssociator::MergePasswords( 351 const sync_pb::PasswordSpecificsData& password, 352 const content::PasswordForm& password_form, 353 content::PasswordForm* new_password) { 354 DCHECK(new_password); 355 356 if (password.scheme() == password_form.scheme && 357 password_form.signon_realm == password.signon_realm() && 358 password_form.origin.spec() == password.origin() && 359 password_form.action.spec() == password.action() && 360 UTF16ToUTF8(password_form.username_element) == 361 password.username_element() && 362 UTF16ToUTF8(password_form.password_element) == 363 password.password_element() && 364 UTF16ToUTF8(password_form.username_value) == 365 password.username_value() && 366 UTF16ToUTF8(password_form.password_value) == 367 password.password_value() && 368 password.ssl_valid() == password_form.ssl_valid && 369 password.preferred() == password_form.preferred && 370 password.date_created() == password_form.date_created.ToInternalValue() && 371 password.blacklisted() == password_form.blacklisted_by_user) { 372 return false; 373 } 374 375 // If the passwords differ, we take the one that was created more recently. 376 if (base::Time::FromInternalValue(password.date_created()) <= 377 password_form.date_created) { 378 *new_password = password_form; 379 } else { 380 CopyPassword(password, new_password); 381 } 382 383 return true; 384} 385 386// static 387void PasswordModelAssociator::WriteToSyncNode( 388 const content::PasswordForm& password_form, 389 syncer::WriteNode* node) { 390 sync_pb::PasswordSpecificsData password; 391 password.set_scheme(password_form.scheme); 392 password.set_signon_realm(password_form.signon_realm); 393 password.set_origin(password_form.origin.spec()); 394 password.set_action(password_form.action.spec()); 395 password.set_username_element(UTF16ToUTF8(password_form.username_element)); 396 password.set_password_element(UTF16ToUTF8(password_form.password_element)); 397 password.set_username_value(UTF16ToUTF8(password_form.username_value)); 398 password.set_password_value(UTF16ToUTF8(password_form.password_value)); 399 password.set_ssl_valid(password_form.ssl_valid); 400 password.set_preferred(password_form.preferred); 401 password.set_date_created(password_form.date_created.ToInternalValue()); 402 password.set_blacklisted(password_form.blacklisted_by_user); 403 404 node->SetPasswordSpecifics(password); 405} 406 407// static 408std::string PasswordModelAssociator::MakeTag( 409 const content::PasswordForm& password) { 410 return MakeTag(password.origin.spec(), 411 UTF16ToUTF8(password.username_element), 412 UTF16ToUTF8(password.username_value), 413 UTF16ToUTF8(password.password_element), 414 password.signon_realm); 415} 416 417// static 418std::string PasswordModelAssociator::MakeTag( 419 const sync_pb::PasswordSpecificsData& password) { 420 return MakeTag(password.origin(), 421 password.username_element(), 422 password.username_value(), 423 password.password_element(), 424 password.signon_realm()); 425} 426 427// static 428std::string PasswordModelAssociator::MakeTag( 429 const std::string& origin_url, 430 const std::string& username_element, 431 const std::string& username_value, 432 const std::string& password_element, 433 const std::string& signon_realm) { 434 return net::EscapePath(origin_url) + "|" + 435 net::EscapePath(username_element) + "|" + 436 net::EscapePath(username_value) + "|" + 437 net::EscapePath(password_element) + "|" + 438 net::EscapePath(signon_realm); 439} 440 441} // namespace browser_sync 442