diff options
author | stuartmorgan@chromium.org <stuartmorgan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-30 00:51:16 +0000 |
---|---|---|
committer | stuartmorgan@chromium.org <stuartmorgan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-30 00:51:16 +0000 |
commit | c01ec9515d1892e2a62d3e4423a2bb37ac79e551 (patch) | |
tree | e5906c772e92e04e3b333716b78a4b43d185c7f6 /chrome/browser/password_manager | |
parent | 70d56be631ab6616cd7661468df6ba91c5821193 (diff) | |
download | chromium_src-c01ec9515d1892e2a62d3e4423a2bb37ac79e551.zip chromium_src-c01ec9515d1892e2a62d3e4423a2bb37ac79e551.tar.gz chromium_src-c01ec9515d1892e2a62d3e4423a2bb37ac79e551.tar.bz2 |
Re-land the password store work from bug 8205, with changes that should fix bug 12479. The Linux pieces are still disabled, however.
BUG=8205
TEST=Password autofill should continue to work on Windows.
Review URL: http://codereview.chromium.org/114057
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17273 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/password_manager')
15 files changed, 1257 insertions, 152 deletions
diff --git a/chrome/browser/password_manager/password_form_manager.cc b/chrome/browser/password_manager/password_form_manager.cc index cf40a1b..26ebd4c 100644 --- a/chrome/browser/password_manager/password_form_manager.cc +++ b/chrome/browser/password_manager/password_form_manager.cc @@ -21,7 +21,7 @@ PasswordFormManager::PasswordFormManager(Profile* profile, observed_form_(observed_form), is_new_login_(true), password_manager_(password_manager), - pending_login_query_(NULL), + pending_login_query_(0), preferred_match_(NULL), state_(PRE_MATCHING_PHASE), profile_(profile) { @@ -168,25 +168,21 @@ void PasswordFormManager::FetchMatchingLoginsFromWebDatabase() { DCHECK_EQ(state_, PRE_MATCHING_PHASE); DCHECK(!pending_login_query_); state_ = MATCHING_PHASE; - WebDataService* web_data_service = - profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); - if (!web_data_service) { + PasswordStore* password_store = + profile_->GetPasswordStore(Profile::EXPLICIT_ACCESS); + if (!password_store) { NOTREACHED(); return; } - pending_login_query_ = web_data_service->GetLogins(observed_form_, this); + pending_login_query_ = password_store->GetLogins(observed_form_, this); } bool PasswordFormManager::HasCompletedMatching() { return state_ == POST_MATCHING_PHASE; } -void PasswordFormManager::OnRequestDone(WebDataService::Handle h, - const WDTypedResult* result) { - // Get the result from the database into a usable form. - const WDResult<std::vector<PasswordForm*> >* r = - static_cast<const WDResult<std::vector<PasswordForm*> >*>(result); - std::vector<PasswordForm*> logins_result = r->GetValue(); +void PasswordFormManager::OnRequestDone(int handle, + const std::vector<PasswordForm*>& logins_result) { // Note that the result gets deleted after this call completes, but we own // the PasswordForm objects pointed to by the result vector, thus we keep // copies to a minimum here. @@ -239,14 +235,6 @@ void PasswordFormManager::OnRequestDone(WebDataService::Handle h, // We're done matching now. state_ = POST_MATCHING_PHASE; - if (best_score <= 0) { -#if defined(OS_WIN) - state_ = PRE_MATCHING_PHASE; - FetchMatchingIE7LoginFromWebDatabase(); -#endif - return; - } - for (std::vector<PasswordForm>::const_iterator it = empties.begin(); it != empties.end(); ++it) { // If we don't already have a result with the same username, add the @@ -272,30 +260,17 @@ void PasswordFormManager::OnRequestDone(WebDataService::Handle h, } } -void PasswordFormManager::OnWebDataServiceRequestDone(WebDataService::Handle h, - const WDTypedResult* result) { +void PasswordFormManager::OnPasswordStoreRequestDone( + int handle, const std::vector<PasswordForm*>& result) { DCHECK_EQ(state_, MATCHING_PHASE); - DCHECK_EQ(pending_login_query_, h); - DCHECK(result); - pending_login_query_ = NULL; + DCHECK_EQ(pending_login_query_, handle); - if (!result) + if (result.empty()) { + state_ = POST_MATCHING_PHASE; return; - - switch (result->GetType()) { - case PASSWORD_RESULT: { - OnRequestDone(h, result); - break; - } -#if defined(OS_WIN) - case PASSWORD_IE7_RESULT: { - OnIE7RequestDone(h, result); - break; - } -#endif - default: - NOTREACHED(); } + + OnRequestDone(handle, result); } bool PasswordFormManager::IgnoreResult(const PasswordForm& form) const { @@ -322,14 +297,15 @@ void PasswordFormManager::SaveAsNewLogin() { DCHECK(!profile_->IsOffTheRecord()); - WebDataService* web_data_service = - profile_->GetWebDataService(Profile::IMPLICIT_ACCESS); - if (!web_data_service) { + PasswordStore* password_store = + profile_->GetPasswordStore(Profile::IMPLICIT_ACCESS); + if (!password_store) { NOTREACHED(); return; } + pending_credentials_.date_created = Time::Now(); - web_data_service->AddLogin(pending_credentials_); + password_store->AddLogin(pending_credentials_); } void PasswordFormManager::UpdateLogin() { @@ -341,9 +317,9 @@ void PasswordFormManager::UpdateLogin() { DCHECK(!IsNewLogin() && pending_credentials_.preferred); DCHECK(!profile_->IsOffTheRecord()); - WebDataService* web_data_service = - profile_->GetWebDataService(Profile::IMPLICIT_ACCESS); - if (!web_data_service) { + PasswordStore* password_store = + profile_->GetPasswordStore(Profile::IMPLICIT_ACCESS); + if (!password_store) { NOTREACHED(); return; } @@ -355,7 +331,7 @@ void PasswordFormManager::UpdateLogin() { iter->second->preferred) { // This wasn't the selected login but it used to be preferred. iter->second->preferred = false; - web_data_service->UpdateLogin(*iter->second); + password_store->UpdateLogin(*iter->second); } } // Update the new preferred login. @@ -380,23 +356,20 @@ void PasswordFormManager::UpdateLogin() { PasswordForm copy(pending_credentials_); copy.origin = observed_form_.origin; copy.action = observed_form_.action; - web_data_service->AddLogin(copy); + password_store->AddLogin(copy); } else { - web_data_service->UpdateLogin(pending_credentials_); + password_store->UpdateLogin(pending_credentials_); } } void PasswordFormManager::CancelLoginsQuery() { - if (!pending_login_query_) - return; - WebDataService* web_data_service = - profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); - if (!web_data_service) { - NOTREACHED(); + PasswordStore* password_store = + profile_->GetPasswordStore(Profile::IMPLICIT_ACCESS); + if (!password_store) { + // Can be NULL in unit tests. return; } - web_data_service->CancelRequest(pending_login_query_); - pending_login_query_ = NULL; + password_store->CancelLoginsQuery(pending_login_query_); } int PasswordFormManager::ScoreResult(const PasswordForm& candidate) const { diff --git a/chrome/browser/password_manager/password_form_manager.h b/chrome/browser/password_manager/password_form_manager.h index 332eea3..90ffc86 100644 --- a/chrome/browser/password_manager/password_form_manager.h +++ b/chrome/browser/password_manager/password_form_manager.h @@ -8,6 +8,7 @@ #include "build/build_config.h" #include "base/stl_util-inl.h" +#include "chrome/browser/password_manager/password_store.h" #include "chrome/browser/webdata/web_data_service.h" #include "webkit/glue/password_form.h" @@ -16,7 +17,7 @@ class Profile; // Per-password-form-{on-page, dialog} class responsible for interactions // between a given form, the per-tab PasswordManager, and the web database. -class PasswordFormManager : public WebDataServiceConsumer { +class PasswordFormManager : public PasswordStoreConsumer { public: // web_data_service allows access to current profile's Web Data // password_manager owns this object @@ -34,9 +35,6 @@ class PasswordFormManager : public WebDataServiceConsumer { // Retrieves potential matching logins from the database. void FetchMatchingLoginsFromWebDatabase(); -#if defined(OS_WIN) - void FetchMatchingIE7LoginFromWebDatabase(); -#endif // Simple state-check to verify whether this object as received a callback // from the web database and completed its matching phase. Note that the @@ -60,19 +58,12 @@ class PasswordFormManager : public WebDataServiceConsumer { // managed by this. bool IsNewLogin(); - // WebDataServiceConsumer implementation. If matches were found - // (in *result), this is where we determine we need to autofill. - virtual void OnWebDataServiceRequestDone(WebDataService::Handle h, - const WDTypedResult* result); - // Determines if we need to autofill given the results of the query. - void OnRequestDone(WebDataService::Handle h, const WDTypedResult* result); + void OnRequestDone(int handle, const std::vector<PasswordForm*>& result); -#if defined(OS_WIN) - // Determines if we need to autofill given the results of the query in the - // ie7_password table. - void OnIE7RequestDone(WebDataService::Handle h, const WDTypedResult* result); -#endif + // PasswordStoreConsumer implementation. + virtual void OnPasswordStoreRequestDone( + int handle, const std::vector<PasswordForm*>& result); // A user opted to 'never remember' passwords for this form. // Blacklist it so that from now on when it is seen we ignore it. @@ -141,7 +132,7 @@ class PasswordFormManager : public WebDataServiceConsumer { const PasswordManager* const password_manager_; // Handle to any pending WebDataService::GetLogins query. - WebDataService::Handle pending_login_query_; + int pending_login_query_; // Convenience pointer to entry in best_matches_ that is marked // as preferred. This is only allowed to be null if there are no best matches diff --git a/chrome/browser/password_manager/password_form_manager_win.cc b/chrome/browser/password_manager/password_form_manager_win.cc index a075617..e69de29 100644 --- a/chrome/browser/password_manager/password_form_manager_win.cc +++ b/chrome/browser/password_manager/password_form_manager_win.cc @@ -1,77 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/password_manager/password_form_manager.h" - -#include "base/string_util.h" -#include "chrome/browser/password_manager/ie7_password.h" -#include "chrome/browser/password_manager/password_manager.h" -#include "chrome/browser/profile.h" - -void PasswordFormManager::FetchMatchingIE7LoginFromWebDatabase() { - DCHECK_EQ(state_, PRE_MATCHING_PHASE); - DCHECK(!pending_login_query_); - state_ = MATCHING_PHASE; - WebDataService* web_data_service = - profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); - if (!web_data_service) { - NOTREACHED(); - return; - } - - IE7PasswordInfo info; - std::wstring url = ASCIIToWide(observed_form_.origin.spec()); - info.url_hash = ie7_password::GetUrlHash(url); - pending_login_query_ = web_data_service->GetIE7Login(info, this); -} - -void PasswordFormManager::OnIE7RequestDone(WebDataService::Handle h, - const WDTypedResult* result) { - // Get the result from the database into a usable form. - const WDResult<IE7PasswordInfo>* r = - static_cast<const WDResult<IE7PasswordInfo>*>(result); - IE7PasswordInfo result_value = r->GetValue(); - - state_ = POST_MATCHING_PHASE; - - if (!result_value.encrypted_data.empty()) { - // We got a result. - // Delete the entry. If it's good we will add it to the real saved password - // table. - WebDataService* web_data_service = - profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); - if (!web_data_service) { - NOTREACHED(); - return; - } - web_data_service->RemoveIE7Login(result_value); - - std::wstring username; - std::wstring password; - std::wstring url = ASCIIToWide(observed_form_.origin.spec()); - if (!ie7_password::DecryptPassword(url, result_value.encrypted_data, - &username, &password)) { - return; - } - - PasswordForm* auto_fill = new PasswordForm(observed_form_); - auto_fill->username_value = username; - auto_fill->password_value = password; - auto_fill->preferred = true; - auto_fill->ssl_valid = observed_form_.origin.SchemeIsSecure(); - auto_fill->date_created = result_value.date_created; - // Add this PasswordForm to the saved password table. - web_data_service->AddLogin(*auto_fill); - - if (IgnoreResult(*auto_fill)) { - delete auto_fill; - return; - } - - best_matches_[auto_fill->username_value] = auto_fill; - preferred_match_ = auto_fill; - password_manager_->Autofill(observed_form_, best_matches_, - preferred_match_); - } -} diff --git a/chrome/browser/password_manager/password_manager.cc b/chrome/browser/password_manager/password_manager.cc index b558bf94..e09aa08 100644 --- a/chrome/browser/password_manager/password_manager.cc +++ b/chrome/browser/password_manager/password_manager.cc @@ -8,6 +8,7 @@ #include "app/resource_bundle.h" #include "base/stl_util-inl.h" #include "base/string_util.h" +#include "chrome/browser/password_manager/password_form_manager.h" #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/notification_registrar.h" @@ -234,8 +235,9 @@ void PasswordManager::Autofill( return; } default: - if (observer_) + if (observer_) { observer_->OnAutofillDataAvailable(preferred_match->username_value, preferred_match->password_value); + } } } diff --git a/chrome/browser/password_manager/password_manager.h b/chrome/browser/password_manager/password_manager.h index 9c80714..175e4cd 100644 --- a/chrome/browser/password_manager/password_manager.h +++ b/chrome/browser/password_manager/password_manager.h @@ -7,13 +7,13 @@ #include "base/scoped_ptr.h" #include "base/stl_util-inl.h" -#include "chrome/browser/password_manager/password_form_manager.h" #include "chrome/browser/tab_contents/infobar_delegate.h" #include "chrome/browser/views/login_view.h" #include "chrome/common/pref_member.h" #include "webkit/glue/password_form.h" #include "webkit/glue/password_form_dom_manager.h" +class PasswordFormManager; class PrefService; class TabContents; diff --git a/chrome/browser/password_manager/password_store.cc b/chrome/browser/password_manager/password_store.cc new file mode 100644 index 0000000..d84b788 --- /dev/null +++ b/chrome/browser/password_manager/password_store.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/password_manager/password_store.h" + +#include "base/scoped_ptr.h" +#include "base/task.h" + +using std::vector; + +PasswordStore::PasswordStore() : handle_(0) { +} + +bool PasswordStore::Init() { + thread_.reset(new base::Thread("Chrome_PasswordStore_Thread")); + + if (!thread_->Start()) { + thread_.reset(NULL); + return false; + } + + return true; +} + +void PasswordStore::ScheduleTask(Task* task) { + if (thread_.get()) { + thread_->message_loop()->PostTask(FROM_HERE, task); + } +} + +void PasswordStore::AddLogin(const PasswordForm& form) { + ScheduleTask(NewRunnableMethod( + this, &PasswordStore::AddLoginImpl, form)); +} + +void PasswordStore::UpdateLogin(const PasswordForm& form) { + ScheduleTask(NewRunnableMethod( + this, &PasswordStore::UpdateLoginImpl, form)); +} + +void PasswordStore::RemoveLogin(const PasswordForm& form) { + ScheduleTask(NewRunnableMethod( + this, &PasswordStore::RemoveLoginImpl, form)); +} + +int PasswordStore::GetLogins(const PasswordForm& form, + PasswordStoreConsumer* consumer) { + int handle = handle_++; + GetLoginsRequest* request = new GetLoginsRequest(form, consumer, handle); + + pending_requests_.insert(handle); + + ScheduleTask(NewRunnableMethod(this, &PasswordStore::GetLoginsImpl, request)); + return handle; +} + +void PasswordStore::NotifyConsumer(GetLoginsRequest* request, + const vector<PasswordForm*> forms) { + scoped_ptr<GetLoginsRequest> request_ptr(request); + + DCHECK(MessageLoop::current() == thread_->message_loop()); + request->message_loop->PostTask(FROM_HERE, + NewRunnableMethod(this, + &PasswordStore::NotifyConsumerImpl, + request->consumer, request->handle, forms)); +} + +void PasswordStore::NotifyConsumerImpl(PasswordStoreConsumer* consumer, + int handle, + const vector<PasswordForm*> forms) { + // Don't notify the consumer if the request was canceled. + if (pending_requests_.find(handle) == pending_requests_.end()) + return; + pending_requests_.erase(handle); + + consumer->OnPasswordStoreRequestDone(handle, forms); +} + +void PasswordStore::CancelLoginsQuery(int handle) { + pending_requests_.erase(handle); +} + +PasswordStore::GetLoginsRequest::GetLoginsRequest( + const PasswordForm& form, + PasswordStoreConsumer* consumer, + int handle) + : form(form), + consumer(consumer), + handle(handle), + message_loop(MessageLoop::current()) { +} diff --git a/chrome/browser/password_manager/password_store.h b/chrome/browser/password_manager/password_store.h new file mode 100644 index 0000000..7b2144c --- /dev/null +++ b/chrome/browser/password_manager/password_store.h @@ -0,0 +1,115 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE +#define CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE + +#include <set> +#include <vector> + +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "base/thread.h" +#include "webkit/glue/password_form.h" + +class Profile; +class Task; + +class PasswordStoreConsumer { + public: + virtual ~PasswordStoreConsumer() {} + // Call this when the request is finished. If there are no results, call it + // anyway with an empty vector. + virtual void OnPasswordStoreRequestDone( + int handle, const std::vector<PasswordForm*>& result) = 0; +}; + +// Interface for storing form passwords in a platform-specific secure way. +// The login request/manipulation API is not threadsafe. +class PasswordStore : public base::RefCountedThreadSafe<PasswordStore> { + public: + PasswordStore(); + virtual ~PasswordStore() {} + + // Reimplement this to add custom initialization. Always call this too. + virtual bool Init(); + + // Adds the given PasswordForm to the secure password store asynchronously. + void AddLogin(const PasswordForm& form); + // Updates the matching PasswordForm in the secure password store (async). + void UpdateLogin(const PasswordForm& form); + // Removes the matching PasswordForm from the secure password store (async). + void RemoveLogin(const PasswordForm& form); + // Searches for a matching PasswordForm and returns a handle so the async + // request can be tracked. Implement the PasswordStoreConsumer interface to + // be notified on completion. + int GetLogins(const PasswordForm& form, + PasswordStoreConsumer* consumer); + + // Cancels a previous GetLogins query (async) + virtual void CancelLoginsQuery(int handle); + + protected: + // Simple container class that represents a GetLogins request. + // Created in GetLogins and passed to GetLoginsImpl. + struct GetLoginsRequest { + GetLoginsRequest(const PasswordForm& f, + PasswordStoreConsumer* c, + int handle); + + // The query form that was originally passed to GetLogins + PasswordForm form; + // The consumer to notify when this GetLogins request is complete + PasswordStoreConsumer* consumer; + // A unique handle for the request + int handle; + // The message loop that the GetLogins request was made from. We send the + // result back to the consumer in this same message loop. + MessageLoop* message_loop; + + DISALLOW_COPY_AND_ASSIGN(GetLoginsRequest); + }; + + // Schedule the given task to be run in the PasswordStore's own thread. + void ScheduleTask(Task* task); + + // These will be run in PasswordStore's own thread. + // Synchronous implementation to add the given login. + virtual void AddLoginImpl(const PasswordForm& form) = 0; + // Synchronous implementation to update the given login. + virtual void UpdateLoginImpl(const PasswordForm& form) = 0; + // Synchronous implementation to remove the given login. + virtual void RemoveLoginImpl(const PasswordForm& form) = 0; + // Should find all PasswordForms with the same signon_realm. The results + // will then be scored by the PasswordFormManager. Once they are found + // (or not), the consumer should be notified. + virtual void GetLoginsImpl(GetLoginsRequest* request) = 0; + + // Notifies the consumer that GetLoginsImpl() is complete. + void NotifyConsumer(GetLoginsRequest* request, + const std::vector<PasswordForm*> forms); + + // Next handle to return from GetLogins() to allow callers to track + // their request. + int handle_; + + // Thread that the synchronous methods are run in. + scoped_ptr<base::Thread> thread_; + + private: + // Called by NotifyConsumer, but runs in the consumer's thread. Will not + // call the consumer if the request was canceled. This extra layer is here so + // that PasswordStoreConsumer doesn't have to be reference counted (we assume + // consumers will cancel their requests before they are destroyed). + void NotifyConsumerImpl(PasswordStoreConsumer* consumer, int handle, + const std::vector<PasswordForm*> forms); + + // List of pending request handles. Handles are removed from the set when + // they finish or are canceled. + std::set<int> pending_requests_; + + DISALLOW_COPY_AND_ASSIGN(PasswordStore); +}; + +#endif // CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE diff --git a/chrome/browser/password_manager/password_store_default.cc b/chrome/browser/password_manager/password_store_default.cc new file mode 100644 index 0000000..c7e89c5 --- /dev/null +++ b/chrome/browser/password_manager/password_store_default.cc @@ -0,0 +1,83 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/password_manager/password_store_default.h" +#include "chrome/browser/webdata/web_data_service.h" +#include "chrome/common/chrome_constants.h" + +#include "base/logging.h" +#include "base/task.h" + +PasswordStoreDefault::PasswordStoreDefault(WebDataService* web_data_service) + : web_data_service_(web_data_service) { +} + +void PasswordStoreDefault::AddLoginImpl(const PasswordForm& form) { + web_data_service_->AddLogin(form); +} + +void PasswordStoreDefault::RemoveLoginImpl(const PasswordForm& form) { + web_data_service_->RemoveLogin(form); +} + +void PasswordStoreDefault::UpdateLoginImpl(const PasswordForm& form) { + web_data_service_->UpdateLogin(form); +} + +void PasswordStoreDefault::GetLoginsImpl(GetLoginsRequest* request) { + int web_data_handle = web_data_service_->GetLogins(request->form, this); + AddPendingWebDataServiceRequest(web_data_handle, request); +} + +void PasswordStoreDefault::OnWebDataServiceRequestDone( + WebDataService::Handle h, + const WDTypedResult *result) { + // Look up this handle in our request map to get the original + // GetLoginsRequest. + PendingRequestMap::iterator it(pending_requests_.find(h)); + DCHECK(it != pending_requests_.end()); + + GetLoginsRequest* request = it->second; + pending_requests_.erase(it); + + DCHECK(result); + if (!result) + return; + + const WDResult<std::vector<PasswordForm*> >* r = + static_cast<const WDResult<std::vector<PasswordForm*> >*>(result); + + NotifyConsumer(request, r->GetValue()); + + RemovePendingWebDataServiceRequest(h); +} + +void PasswordStoreDefault::AddPendingWebDataServiceRequest( + WebDataService::Handle handle, GetLoginsRequest* request) { + pending_requests_.insert(PendingRequestMap::value_type(handle, request)); + + // WebDataService callbacks are non-retaining. Since there would be a race + // around cancelling the requests in the desctructor vs. getting a callback + // in this worker thread, just make sure that we stick around instead. + this->AddRef(); +} + +void PasswordStoreDefault::RemovePendingWebDataServiceRequest( + WebDataService::Handle handle) { + PendingRequestMap::iterator it(pending_requests_.find(handle)); + DCHECK(it != pending_requests_.end()); + pending_requests_.erase(it); + + // Balance the AddRef in AddPendingWebDataServiceRequest. + this->Release(); +} + +PasswordStore::GetLoginsRequest* + PasswordStoreDefault::GetLoginsRequestForWebDataServiceRequest( + WebDataService::Handle handle) { + PendingRequestMap::iterator it(pending_requests_.find(handle)); + if (it == pending_requests_.end()) + return NULL; + return it->second; +} diff --git a/chrome/browser/password_manager/password_store_default.h b/chrome/browser/password_manager/password_store_default.h new file mode 100644 index 0000000..c979da19 --- /dev/null +++ b/chrome/browser/password_manager/password_store_default.h @@ -0,0 +1,67 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_DEFAULT +#define CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_DEFAULT + +#include <map> + +#include "base/file_path.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "chrome/browser/password_manager/password_store.h" +#include "chrome/browser/webdata/web_data_service.h" + +class Task; + +// Simple password store implementation that delegates everything to +// the WebDatabase. +// TODO(stuartmorgan): This is a temprorary shim for Windows from the new +// PasswordStore interface to the old model of storing passwords in the +// WebDatabase. This should be replaced by a self-contained Windows +// implementation once PasswordStore is completed. +class PasswordStoreDefault : public PasswordStore, + public WebDataServiceConsumer { + public: + explicit PasswordStoreDefault(WebDataService* web_data_service); + virtual ~PasswordStoreDefault() {} + + protected: + // Implements PasswordStore interface. + void AddLoginImpl(const PasswordForm& form); + void UpdateLoginImpl(const PasswordForm& form); + void RemoveLoginImpl(const PasswordForm& form); + void GetLoginsImpl(GetLoginsRequest* request); + + // Called when a WebDataService method finishes. + // Overrides must call RemovePendingWebDataServiceRequest. + virtual void OnWebDataServiceRequestDone(WebDataService::Handle h, + const WDTypedResult* result); + + // Keeps track of a pending WebDataService request, and the original + // GetLogins request it is associated with. + // Increases the reference count of |this|, so must be balanced with a + // call to RemovePendingWebDataServiceRequest. + void AddPendingWebDataServiceRequest(WebDataService::Handle handle, + GetLoginsRequest* request); + // Removes a WebDataService request from the list of pending requests, + // and reduces the reference count of |this|. + void RemovePendingWebDataServiceRequest(WebDataService::Handle handle); + // Returns the GetLoginsRequest associated with |handle|. + GetLoginsRequest* GetLoginsRequestForWebDataServiceRequest( + WebDataService::Handle handle); + + scoped_refptr<WebDataService> web_data_service_; + + private: + // Methods in this class call async WebDataService methods. This mapping + // remembers which WebDataService request corresponds to which PasswordStore + // request. + typedef std::map<WebDataService::Handle, GetLoginsRequest*> PendingRequestMap; + PendingRequestMap pending_requests_; + + DISALLOW_COPY_AND_ASSIGN(PasswordStoreDefault); +}; + +#endif // CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_DEFAULT diff --git a/chrome/browser/password_manager/password_store_gnome.cc b/chrome/browser/password_manager/password_store_gnome.cc new file mode 100644 index 0000000..93f3b6f --- /dev/null +++ b/chrome/browser/password_manager/password_store_gnome.cc @@ -0,0 +1,177 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/password_manager/password_store_gnome.h" + +#include <string> + +#include "base/logging.h" +#include "base/string_util.h" +#include "base/task.h" +#include "base/time.h" + +using std::map; +using std::string; +using std::vector; + +// Schema is analagous to the fields in PasswordForm. +const GnomeKeyringPasswordSchema PasswordStoreGnome::kGnomeSchema = { + GNOME_KEYRING_ITEM_GENERIC_SECRET, { + { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, + { "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, + { "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, + { "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, + { "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, + { "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, + { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, + { "ssl_valid", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 }, + { "preferred", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 }, + { "date_created", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, + { "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 }, + { "scheme", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 }, + { NULL } + } +}; + +PasswordStoreGnome::PasswordStoreGnome() { +} + +PasswordStoreGnome::~PasswordStoreGnome() { +} + +bool PasswordStoreGnome::Init() { + return PasswordStore::Init() && gnome_keyring_is_available(); +} + +void PasswordStoreGnome::AddLoginImpl(const PasswordForm& form) { + AutoLock l(gnome_keyring_lock_); + GnomeKeyringResult result = gnome_keyring_store_password_sync( + &kGnomeSchema, + NULL, // Default keyring. + // TODO(johnmaguire@google.com): Localise this. + "Form password stored by Chrome", + WideToASCII(form.password_value).c_str(), + "origin_url", form.origin.spec().c_str(), + "action_url", form.action.spec().c_str(), + "username_element", form.username_element.c_str(), + "username_value", form.username_value.c_str(), + "password_element", form.password_element.c_str(), + "submit_element", form.submit_element.c_str(), + "signon_realm", form.signon_realm.c_str(), + "ssl_valid", form.ssl_valid, + "preferred", form.preferred, + "date_created", Int64ToString(base::Time::Now().ToTimeT()).c_str(), + "blacklisted_by_user", form.blacklisted_by_user, + "scheme", form.scheme, + NULL); + + if (result != GNOME_KEYRING_RESULT_OK) { + LOG(ERROR) << "Keyring save failed: " + << gnome_keyring_result_to_message(result); + } +} + +void PasswordStoreGnome::UpdateLoginImpl(const PasswordForm& form) { + AddLoginImpl(form); // Add & Update are the same in gnome keyring. +} + +void PasswordStoreGnome::RemoveLoginImpl(const PasswordForm& form) { + AutoLock l(gnome_keyring_lock_); + GnomeKeyringResult result = gnome_keyring_delete_password_sync( + &kGnomeSchema, + "origin_url", form.origin.spec().c_str(), + "action_url", form.action.spec().c_str(), + "username_element", form.username_element.c_str(), + "username_value", form.username_value.c_str(), + "password_element", form.password_element.c_str(), + "submit_element", form.submit_element.c_str(), + "signon_realm", form.signon_realm.c_str(), + "ssl_valid", form.ssl_valid, + "preferred", form.preferred, + "date_created", Int64ToString(form.date_created.ToTimeT()).c_str(), + "blacklisted_by_user", form.blacklisted_by_user, + "scheme", form.scheme, + NULL); + if (result != GNOME_KEYRING_RESULT_OK) { + LOG(ERROR) << "Keyring delete failed: " + << gnome_keyring_result_to_message(result); + } +} + +void PasswordStoreGnome::GetLoginsImpl(GetLoginsRequest* request) { + AutoLock l(gnome_keyring_lock_); + GList* found = NULL; + // Search gnome keyring for matching passwords. + GnomeKeyringResult result = gnome_keyring_find_itemsv_sync( + GNOME_KEYRING_ITEM_GENERIC_SECRET, + &found, + "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING, + request->form.signon_realm.c_str(), + NULL); + vector<PasswordForm*> forms; + if (result == GNOME_KEYRING_RESULT_NO_MATCH) { + NotifyConsumer(request, forms); + return; + } else if (result != GNOME_KEYRING_RESULT_OK) { + LOG(ERROR) << "Keyring find failed: " + << gnome_keyring_result_to_message(result); + NotifyConsumer(request, forms); + return; + } + + // Parse all the results from the returned GList into a + // vector<PasswordForm*>. PasswordForms are allocated on the heap. These + // will be deleted by the consumer. + GList* element = g_list_first(found); + while (element != NULL) { + GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data); + char* password = data->secret; + + GnomeKeyringAttributeList* attributes = data->attributes; + // Read the string & int attributes into the appropriate map. + map<string, string> string_attribute_map; + map<string, uint32> uint_attribute_map; + for (unsigned int i = 0; i < attributes->len; ++i) { + GnomeKeyringAttribute attribute = + gnome_keyring_attribute_list_index(attributes, i); + if (attribute.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING) { + string_attribute_map[string(attribute.name)] = + string(attribute.value.string); + } else if (attribute.type == GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32) { + uint_attribute_map[string(attribute.name)] = attribute.value.integer; + } + } + + PasswordForm* form = new PasswordForm(); + form->origin = GURL(string_attribute_map["origin_url"]); + form->action = GURL(string_attribute_map["action_url"]); + form->username_element = + ASCIIToWide(string(string_attribute_map["username_element"])); + form->username_value = + ASCIIToWide(string(string_attribute_map["username_value"])); + form->password_element = + ASCIIToWide(string(string_attribute_map["password_element"])); + form->password_value = ASCIIToWide(string(password)); + form->submit_element = + ASCIIToWide(string(string_attribute_map["submit_element"])); + form->signon_realm = uint_attribute_map["signon_realm"]; + form->ssl_valid = uint_attribute_map["ssl_valid"]; + form->preferred = uint_attribute_map["preferred"]; + string date = string_attribute_map["date_created"]; + int64 date_created = 0; + DCHECK(StringToInt64(date, &date_created) && date_created != 0); + form->date_created = base::Time::FromTimeT(date_created); + form->blacklisted_by_user = uint_attribute_map["blacklisted_by_user"]; + form->scheme = + static_cast<PasswordForm::Scheme>(uint_attribute_map["scheme"]); + + forms.push_back(form); + + element = g_list_next(element); + } + gnome_keyring_found_list_free(found); + found = NULL; + + NotifyConsumer(request, forms); +} diff --git a/chrome/browser/password_manager/password_store_gnome.h b/chrome/browser/password_manager/password_store_gnome.h new file mode 100644 index 0000000..abbd054 --- /dev/null +++ b/chrome/browser/password_manager/password_store_gnome.h @@ -0,0 +1,40 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_GNOME +#define CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_GNOME + +extern "C" { +#include <gnome-keyring.h> +} + +#include "base/lock.h" +#include "chrome/browser/password_manager/password_store.h" + +class Profile; +class Task; + +// PasswordStore implementation using Gnome Keyring. +class PasswordStoreGnome : public PasswordStore { + public: + PasswordStoreGnome(); + virtual ~PasswordStoreGnome(); + + virtual bool Init(); + + private: + void AddLoginImpl(const PasswordForm& form); + void UpdateLoginImpl(const PasswordForm& form); + void RemoveLoginImpl(const PasswordForm& form); + void GetLoginsImpl(GetLoginsRequest* request); + + static const GnomeKeyringPasswordSchema kGnomeSchema; + + // Mutex for all interactions with Gnome Keyring. + Lock gnome_keyring_lock_; + + DISALLOW_COPY_AND_ASSIGN(PasswordStoreGnome); +}; + +#endif // CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_GNOME diff --git a/chrome/browser/password_manager/password_store_kwallet.cc b/chrome/browser/password_manager/password_store_kwallet.cc new file mode 100644 index 0000000..1c50e3e --- /dev/null +++ b/chrome/browser/password_manager/password_store_kwallet.cc @@ -0,0 +1,387 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/password_manager/password_store_kwallet.h" + +#include <sstream> + +#include "base/logging.h" +#include "base/md5.h" +#include "base/pickle.h" +#include "base/string_util.h" +#include "base/task.h" + +using std::string; +using std::vector; + +const char* PasswordStoreKWallet::kAppId = "Chrome"; +const char* PasswordStoreKWallet::kKWalletFolder = "Chrome Form Data"; + +const char* PasswordStoreKWallet::kKWalletServiceName = "org.kde.kwalletd"; +const char* PasswordStoreKWallet::kKWalletPath = "/modules/kwalletd"; +const char* PasswordStoreKWallet::kKWalletInterface = "org.kde.KWallet"; +const char* PasswordStoreKWallet::kKLauncherServiceName = "org.kde.klauncher"; +const char* PasswordStoreKWallet::kKLauncherPath = "/KLauncher"; +const char* PasswordStoreKWallet::kKLauncherInterface = "org.kde.KLauncher"; + +PasswordStoreKWallet::PasswordStoreKWallet() + : error_(NULL), + connection_(NULL), + proxy_(NULL) { +} + +PasswordStoreKWallet::~PasswordStoreKWallet() { + if (proxy_) { + g_object_unref(proxy_); + } +} + +bool PasswordStoreKWallet::Init() { + thread_.reset(new base::Thread("Chrome_KeyringThread")); + + if (!thread_->Start()) { + thread_.reset(NULL); + return false; + } + + // Initialize threading in dbus-glib - it should be fine for + // dbus_g_thread_init to be called multiple times. + if (!g_thread_supported()) + g_thread_init(NULL); + dbus_g_thread_init(); + + // Get a connection to the session bus. + connection_ = dbus_g_bus_get(DBUS_BUS_SESSION, &error_); + if (CheckError()) + return false; + + if (!StartKWalletd()) return false; + if (!InitWallet()) return false; + + return true; +} + +bool PasswordStoreKWallet::StartKWalletd() { + // Sadly kwalletd doesn't use DBUS activation, so we have to make a call to + // klauncher to start it. + DBusGProxy* klauncher_proxy = + dbus_g_proxy_new_for_name(connection_, kKLauncherServiceName, + kKLauncherPath, kKLauncherInterface); + + char* empty_string_list = NULL; + int ret = 1; + char* error = NULL; + dbus_g_proxy_call(klauncher_proxy, "start_service_by_desktop_name", &error_, + G_TYPE_STRING, "kwalletd", // serviceName + G_TYPE_STRV, &empty_string_list, // urls + G_TYPE_STRV, &empty_string_list, // envs + G_TYPE_STRING, "", // startup_id + G_TYPE_BOOLEAN, (gboolean) false, // blind + G_TYPE_INVALID, + G_TYPE_INT, &ret, // result + G_TYPE_STRING, NULL, // dubsName + G_TYPE_STRING, &error, // error + G_TYPE_INT, NULL, // pid + G_TYPE_INVALID); + + if (error && *error) { + LOG(ERROR) << "Error launching kwalletd: " << error; + ret = 1; // Make sure we return false after freeing. + } + + g_free(error); + g_object_unref(klauncher_proxy); + + if (CheckError() || ret != 0) + return false; + return true; +} + +bool PasswordStoreKWallet::InitWallet() { + // Make a proxy to KWallet. + proxy_ = dbus_g_proxy_new_for_name(connection_, kKWalletServiceName, + kKWalletPath, kKWalletInterface); + + // Check KWallet is enabled. + gboolean is_enabled = false; + dbus_g_proxy_call(proxy_, "isEnabled", &error_, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &is_enabled, + G_TYPE_INVALID); + if (CheckError() || !is_enabled) + return false; + + // Get the wallet name. + char* wallet_name = NULL; + dbus_g_proxy_call(proxy_, "networkWallet", &error_, + G_TYPE_INVALID, + G_TYPE_STRING, &wallet_name, + G_TYPE_INVALID); + if (CheckError() || !wallet_name) + return false; + + wallet_name_.assign(wallet_name); + g_free(wallet_name); + + return true; +} + +void PasswordStoreKWallet::AddLoginImpl(const PasswordForm& form) { + AutoLock l(kwallet_lock_); + int wallet_handle = WalletHandle(); + if (wallet_handle == kInvalidKWalletHandle) + return; + + PasswordFormList forms; + GetLoginsList(&forms, form, wallet_handle); + + forms.push_back(const_cast<PasswordForm*>(&form)); + + SetLoginsList(forms, form, wallet_handle); +} + +void PasswordStoreKWallet::UpdateLoginImpl(const PasswordForm& form) { + AutoLock l(kwallet_lock_); + int wallet_handle = WalletHandle(); + if (wallet_handle == kInvalidKWalletHandle) + return; + + PasswordFormList forms; + GetLoginsList(&forms, form, wallet_handle); + + for (uint i = 0; i < forms.size(); ++i) { + if (CompareForms(form, *forms[i])) { + forms.erase(forms.begin() + i); + forms.insert(forms.begin() + i, const_cast<PasswordForm*>(&form)); + } + } + + SetLoginsList(forms, form, wallet_handle); +} + +void PasswordStoreKWallet::RemoveLoginImpl(const PasswordForm& form) { + AutoLock l(kwallet_lock_); + int wallet_handle = WalletHandle(); + if (wallet_handle == kInvalidKWalletHandle) + return; + + PasswordFormList forms; + GetLoginsList(&forms, form, wallet_handle); + + for (uint i = 0; i < forms.size(); ++i) { + if (CompareForms(form, *forms[i])) { + forms.erase(forms.begin() + i); + --i; + } + } + + if (forms.empty()) { + // No items left? Remove the entry from the wallet. + int ret = 0; + dbus_g_proxy_call(proxy_, "removeEntry", &error_, + G_TYPE_INT, wallet_handle, // handle + G_TYPE_STRING, kKWalletFolder, // folder + G_TYPE_STRING, form.signon_realm.c_str(), // key + G_TYPE_STRING, kAppId, // appid + G_TYPE_INVALID, + G_TYPE_INT, &ret, + G_TYPE_INVALID); + CheckError(); + if (ret) + LOG(ERROR) << "Bad return code " << ret << " from kwallet removeEntry"; + } else { + // Otherwise update the entry in the wallet. + SetLoginsList(forms, form, wallet_handle); + } +} + +void PasswordStoreKWallet::GetLoginsImpl(GetLoginsRequest* request) { + PasswordFormList forms; + + AutoLock l(kwallet_lock_); + int wallet_handle = WalletHandle(); + if (wallet_handle != kInvalidKWalletHandle) + GetLoginsList(&forms, request->form, wallet_handle); + + NotifyConsumer(request, forms); +} + +void PasswordStoreKWallet::GetLoginsList(PasswordFormList* forms, + const PasswordForm& key, + int wallet_handle) { + // Is there an entry in the wallet? + gboolean has_entry = false; + dbus_g_proxy_call(proxy_, "hasEntry", &error_, + G_TYPE_INT, wallet_handle, // handle + G_TYPE_STRING, kKWalletFolder, // folder + G_TYPE_STRING, key.signon_realm.c_str(), // key + G_TYPE_STRING, kAppId, // appid + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &has_entry, + G_TYPE_INVALID); + + if (CheckError() || !has_entry) + return; + + GArray* byte_array = NULL; + dbus_g_proxy_call(proxy_, "readEntry", &error_, + G_TYPE_INT, wallet_handle, // handle + G_TYPE_STRING, kKWalletFolder, // folder + G_TYPE_STRING, key.signon_realm.c_str(), // key + G_TYPE_STRING, kAppId, // appid + G_TYPE_INVALID, + DBUS_TYPE_G_UCHAR_ARRAY, &byte_array, + G_TYPE_INVALID); + + if (CheckError() || !byte_array || !byte_array->len) + return; + + Pickle pickle(byte_array->data, byte_array->len); + DeserializeValue(key, pickle, forms); +} + +void PasswordStoreKWallet::SetLoginsList(const PasswordFormList& forms, + const PasswordForm& key, + int wallet_handle) { + Pickle value; + SerializeValue(forms, &value); + + // Convert the pickled bytes to a GByteArray. + GArray* byte_array = g_array_sized_new(false, false, sizeof(char), + value.size()); + g_array_append_vals(byte_array, value.data(), value.size()); + + // Make the call. + int ret = 0; + dbus_g_proxy_call(proxy_, "writeEntry", &error_, + G_TYPE_INT, wallet_handle, // handle + G_TYPE_STRING, kKWalletFolder, // folder + G_TYPE_STRING, key.signon_realm.c_str(), // key + DBUS_TYPE_G_UCHAR_ARRAY, byte_array, // value + G_TYPE_STRING, kAppId, // appid + G_TYPE_INVALID, + G_TYPE_INT, &ret, + G_TYPE_INVALID); + g_array_free(byte_array, true); + + CheckError(); + if (ret) + LOG(ERROR) << "Bad return code " << ret << " from kwallet writeEntry"; +} + +bool PasswordStoreKWallet::CompareForms(const PasswordForm& a, + const PasswordForm& b) { + return a.origin == b.origin && + a.password_element == b.password_element && + a.signon_realm == b.signon_realm && + a.submit_element == b.submit_element && + a.username_element == b.username_element && + a.username_value == b.username_value; +} + +void PasswordStoreKWallet::SerializeValue(const PasswordFormList& forms, + Pickle* pickle) { + pickle->WriteInt(forms.size()); + for (PasswordFormList::const_iterator it = forms.begin() ; + it != forms.end() ; ++it) { + const PasswordForm* form = *it; + pickle->WriteInt(form->scheme); + pickle->WriteString(form->origin.spec()); + pickle->WriteString(form->action.spec()); + pickle->WriteWString(form->username_element); + pickle->WriteWString(form->username_value); + pickle->WriteWString(form->password_element); + pickle->WriteWString(form->password_value); + pickle->WriteWString(form->submit_element); + pickle->WriteBool(form->ssl_valid); + pickle->WriteBool(form->preferred); + pickle->WriteBool(form->blacklisted_by_user); + } +} + +void PasswordStoreKWallet::DeserializeValue(const PasswordForm& key, + const Pickle& pickle, + PasswordFormList* forms) { + void* iter = NULL; + + int count = 0; + pickle.ReadInt(&iter, &count); + + for (int i = 0; i < count; ++i) { + PasswordForm* form = new PasswordForm(); + form->signon_realm.assign(key.signon_realm); + + pickle.ReadInt(&iter, reinterpret_cast<int*>(&form->scheme)); + ReadGURL(pickle, &iter, &form->origin); + ReadGURL(pickle, &iter, &form->action); + pickle.ReadWString(&iter, &form->username_element); + pickle.ReadWString(&iter, &form->username_value); + pickle.ReadWString(&iter, &form->password_element); + pickle.ReadWString(&iter, &form->password_value); + pickle.ReadWString(&iter, &form->submit_element); + pickle.ReadBool(&iter, &form->ssl_valid); + pickle.ReadBool(&iter, &form->preferred); + pickle.ReadBool(&iter, &form->blacklisted_by_user); + forms->push_back(form); + } +} + +void PasswordStoreKWallet::ReadGURL(const Pickle& pickle, void** iter, + GURL* url) { + string url_string; + pickle.ReadString(iter, &url_string); + *url = GURL(url_string); +} + +bool PasswordStoreKWallet::CheckError() { + if (error_) { + LOG(ERROR) << "Failed to complete KWallet call: " << error_->message; + g_error_free(error_); + error_ = NULL; + return true; + } + return false; +} + +int PasswordStoreKWallet::WalletHandle() { + // Open the wallet. + int handle = kInvalidKWalletHandle; + dbus_g_proxy_call(proxy_, "open", &error_, + G_TYPE_STRING, wallet_name_.c_str(), // wallet + G_TYPE_INT64, 0LL, // wid + G_TYPE_STRING, kAppId, // appid + G_TYPE_INVALID, + G_TYPE_INT, &handle, + G_TYPE_INVALID); + if (CheckError() || handle == kInvalidKWalletHandle) + return kInvalidKWalletHandle; + + // Check if our folder exists. + gboolean has_folder = false; + dbus_g_proxy_call(proxy_, "hasFolder", &error_, + G_TYPE_INT, handle, // handle + G_TYPE_STRING, kKWalletFolder, // folder + G_TYPE_STRING, kAppId, // appid + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &has_folder, + G_TYPE_INVALID); + if (CheckError()) + return kInvalidKWalletHandle; + + // Create it if it didn't. + if (!has_folder) { + gboolean success = false; + dbus_g_proxy_call(proxy_, "createFolder", &error_, + G_TYPE_INT, handle, // handle + G_TYPE_STRING, kKWalletFolder, // folder + G_TYPE_STRING, kAppId, // appid + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &success, + G_TYPE_INVALID); + if (CheckError() || !success) + return kInvalidKWalletHandle; + } + + return handle; +} diff --git a/chrome/browser/password_manager/password_store_kwallet.h b/chrome/browser/password_manager/password_store_kwallet.h new file mode 100644 index 0000000..f0ca283 --- /dev/null +++ b/chrome/browser/password_manager/password_store_kwallet.h @@ -0,0 +1,111 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_KWALLET +#define CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_KWALLET + +#include <dbus/dbus-glib.h> +#include <glib.h> + +#include <string> +#include <vector> + +#include "base/lock.h" +#include "base/thread.h" +#include "chrome/browser/password_manager/password_store.h" +#include "webkit/glue/password_form.h" + +class Pickle; +class Profile; +class Task; + +class PasswordStoreKWallet : public PasswordStore { + public: + PasswordStoreKWallet(); + virtual ~PasswordStoreKWallet(); + + bool Init(); + + private: + typedef std::vector<PasswordForm*> PasswordFormList; + + // Implements PasswordStore interface. + void AddLoginImpl(const PasswordForm& form); + void UpdateLoginImpl(const PasswordForm& form); + void RemoveLoginImpl(const PasswordForm& form); + void GetLoginsImpl(GetLoginsRequest* request); + + // Initialisation. + bool StartKWalletd(); + bool InitWallet(); + + // Reads a list of PasswordForms from the wallet that match the signon_realm + // of key. + void GetLoginsList(PasswordFormList* forms, const PasswordForm& key, + int wallet_handle); + + // Writes a list of PasswordForms to the wallet with the signon_realm from + // key. Overwrites any existing list for this key. + void SetLoginsList(const PasswordFormList& forms, const PasswordForm& key, + int wallet_handle); + + // Checks if the last dbus call returned an error. If it did, logs the error + // message, frees it and returns true. + // This must be called after every dbus call. + bool CheckError(); + + // Opens the wallet and ensures that the "Chrome Form Data" folder exists. + // Returns kInvalidWalletHandle on error. + int WalletHandle(); + + // Compares two PasswordForms and returns true if they are the same. + // Checks only the fields that we persist in KWallet, and ignores + // password_value. + static bool CompareForms(const PasswordForm& a, const PasswordForm& b); + + // Serializes a list of PasswordForms to be stored in the wallet. + static void SerializeValue(const PasswordFormList& forms, Pickle* pickle); + + // Deserializes a list of PasswordForms from the wallet. + static void DeserializeValue(const PasswordForm& key, const Pickle& pickle, + PasswordFormList* forms); + + // Convenience function to read a GURL from a Pickle. Assumes the URL has + // been written as a std::string. + static void ReadGURL(const Pickle& pickle, void** iter, GURL* url); + + // Name of the application - will appear in kwallet's dialogs. + static const char* kAppId; + // Name of the folder to store passwords in. + static const char* kKWalletFolder; + + // DBUS stuff. + static const char* kKWalletServiceName; + static const char* kKWalletPath; + static const char* kKWalletInterface; + static const char* kKLauncherServiceName; + static const char* kKLauncherPath; + static const char* kKLauncherInterface; + + // Invalid handle returned by WalletHandle(). + static const int kInvalidKWalletHandle = -1; + + // Controls all access to kwallet dbus calls. + Lock kwallet_lock_; + + // Error from the last dbus call. NULL when there's no error. Freed and + // cleared by CheckError(). + GError* error_; + // Connection to the dbus session bus. + DBusGConnection* connection_; + // Proxy to the kwallet dbus service. + DBusGProxy* proxy_; + + // The name of the wallet we've opened. Set during Init(). + std::string wallet_name_; + + DISALLOW_COPY_AND_ASSIGN(PasswordStoreKWallet); +}; + +#endif // CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_KWALLET diff --git a/chrome/browser/password_manager/password_store_win.cc b/chrome/browser/password_manager/password_store_win.cc new file mode 100644 index 0000000..7eea14f --- /dev/null +++ b/chrome/browser/password_manager/password_store_win.cc @@ -0,0 +1,105 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/password_manager/password_store_win.h" + +#include "base/logging.h" +#include "base/string_util.h" +#include "chrome/browser/password_manager/ie7_password.h" +#include "chrome/browser/password_manager/password_manager.h" +#include "chrome/browser/profile.h" + +using std::map; +using std::vector; + +PasswordStoreWin::PasswordStoreWin(WebDataService* web_data_service) + : PasswordStoreDefault(web_data_service) { +} + +void PasswordStoreWin::OnWebDataServiceRequestDone( + WebDataService::Handle h, const WDTypedResult *result) { + // Look up this handle in our request map to get the original + // GetLoginsRequest. + GetLoginsRequest* request = GetLoginsRequestForWebDataServiceRequest(h); + DCHECK(request); + // Remove our pending request, but make sure that we won't be destroyed until + // we're done with this function. + scoped_refptr<PasswordStoreWin> life_preserver(this); + RemovePendingWebDataServiceRequest(h); + + DCHECK(result); + if (!result) + return; + + switch (result->GetType()) { + case PASSWORD_RESULT: { + // This is a response from WebDataService::GetLogins. + const WDResult<std::vector<PasswordForm*> >* r = + static_cast<const WDResult<std::vector<PasswordForm*> >*>(result); + std::vector<PasswordForm*> result_value = r->GetValue(); + + if (result_value.size()) { + // If we found some results then return them now. + NotifyConsumer(request, result_value); + return; + } else { + // Otherwise try finding IE7 logins. + IE7PasswordInfo info; + std::wstring url = ASCIIToWide(request->form.origin.spec()); + info.url_hash = ie7_password::GetUrlHash(url); + + if (web_data_service_->IsRunning()) { + WebDataService::Handle web_data_handle = + web_data_service_->GetIE7Login(info, this); + AddPendingWebDataServiceRequest(web_data_handle, request); + } + } + break; + } + + case PASSWORD_IE7_RESULT: { + // This is a response from WebDataService::GetIE7Login. + PasswordForm* ie7_form = GetIE7Result(result, request->form); + + std::vector<PasswordForm*> forms; + if (ie7_form) + forms.push_back(ie7_form); + + NotifyConsumer(request, forms); + break; + } + } +} + +PasswordForm* PasswordStoreWin::GetIE7Result(const WDTypedResult *result, + const PasswordForm& form) { + const WDResult<IE7PasswordInfo>* r = + static_cast<const WDResult<IE7PasswordInfo>*>(result); + IE7PasswordInfo info = r->GetValue(); + + if (!info.encrypted_data.empty()) { + // We got a result. + // Delete the entry. If it's good we will add it to the real saved password + // table. + web_data_service_->RemoveIE7Login(info); + std::wstring username; + std::wstring password; + std::wstring url = ASCIIToWide(form.origin.spec()); + if (!ie7_password::DecryptPassword(url, info.encrypted_data, + &username, &password)) { + return NULL; + } + + PasswordForm* auto_fill = new PasswordForm(form); + auto_fill->username_value = username; + auto_fill->password_value = password; + auto_fill->preferred = true; + auto_fill->ssl_valid = form.origin.SchemeIsSecure(); + auto_fill->date_created = info.date_created; + // Add this PasswordForm to the saved password table. + AddLogin(*auto_fill); + return auto_fill; + } + return NULL; +} diff --git a/chrome/browser/password_manager/password_store_win.h b/chrome/browser/password_manager/password_store_win.h new file mode 100644 index 0000000..a56b416 --- /dev/null +++ b/chrome/browser/password_manager/password_store_win.h @@ -0,0 +1,39 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_WIN +#define CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_WIN + +#include <map> +#include <vector> + +#include "base/file_path.h" +#include "base/scoped_ptr.h" +#include "chrome/browser/password_manager/password_store.h" +#include "chrome/browser/password_manager/password_store_default.h" +#include "chrome/browser/webdata/web_data_service.h" +#include "chrome/browser/webdata/web_database.h" + +// Windows PasswordStore implementation that uses the default implementation, +// but also uses IE7 passwords if no others found. +class PasswordStoreWin : public PasswordStoreDefault { + public: + // FilePath specifies path to WebDatabase. + explicit PasswordStoreWin(WebDataService* web_data_service); + virtual ~PasswordStoreWin() {} + + private: + // See PasswordStoreDefault. + void OnWebDataServiceRequestDone(WebDataService::Handle h, + const WDTypedResult* result); + + // Gets logins from IE7 if no others are found. Also copies them into + // Chrome's WebDatabase so we don't need to look next time. + PasswordForm* GetIE7Result(const WDTypedResult* result, + const PasswordForm& form); + + DISALLOW_COPY_AND_ASSIGN(PasswordStoreWin); +}; + +#endif // CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_STORE_WIN |