Chromium Code Reviews| Index: chrome/browser/password_manager/password_manager_util_win.cc |
| diff --git a/chrome/browser/password_manager/password_manager_util_win.cc b/chrome/browser/password_manager/password_manager_util_win.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..02f21ab17dda8d945a81b8b3ba8785fb4ac0e491 |
| --- /dev/null |
| +++ b/chrome/browser/password_manager/password_manager_util_win.cc |
| @@ -0,0 +1,224 @@ |
| +// Copyright (c) 2013 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. |
| + |
| +// windows.h must be first otherwise Win8 SDK breaks. |
| +#include <windows.h> |
| +#include <wincred.h> |
| +#include <LM.h> |
| + |
| +// SECURITY_WIN32 must be defined in order to get |
| +// EXTENDED_NAME_FORMAT enumeration. |
| +#define SECURITY_WIN32 1 |
| +#include <security.h> |
| +#undef SECURITY_WIN32 |
| + |
| +#include "base/prefs/pref_registry_simple.h" |
| +#include "base/prefs/pref_service.h" |
| +#include "base/safe_numerics.h" |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "base/time/time.h" |
| +#include "chrome/browser/browser_process.h" |
| +#include "chrome/browser/password_manager/password_manager.h" |
| +#include "chrome/browser/password_manager/password_manager_util.h" |
| +#include "chrome/common/pref_names.h" |
| +#include "content/public/browser/render_view_host.h" |
| +#include "content/public/browser/render_widget_host_view.h" |
| +#include "content/public/browser/web_contents.h" |
| +#include "grit/chromium_strings.h" |
| +#include "grit/generated_resources.h" |
| +#include "ui/base/l10n/l10n_util.h" |
| + |
| +#if defined(USE_AURA) |
| +#include "ui/aura/root_window.h" |
| +#include "ui/aura/window.h" |
| +#endif |
| + |
| +// static |
| +void PasswordManager::RegisterLocalPrefs(PrefRegistrySimple* registry) { |
| + registry->RegisterInt64Pref(prefs::kOsPasswordLastChanged, 0); |
| + registry->RegisterBooleanPref(prefs::kOsPasswordBlank, false); |
| +} |
| + |
| +namespace password_manager_util { |
| + |
| +const unsigned kMaxPasswordRetries = 3; |
| + |
| +const unsigned kCredUiDefaultFlags = |
| + CREDUI_FLAGS_GENERIC_CREDENTIALS | |
| + CREDUI_FLAGS_EXCLUDE_CERTIFICATES | |
| + CREDUI_FLAGS_KEEP_USERNAME | |
| + CREDUI_FLAGS_ALWAYS_SHOW_UI | |
| + CREDUI_FLAGS_DO_NOT_PERSIST; |
| + |
| +static int64 getPasswordLastChanged(WCHAR* username) { |
|
Patrick Dubroy
2013/11/12 15:41:39
Nit: Function names should start with a capital le
Will Harris
2013/11/26 22:07:12
Done.
|
| + LPUSER_INFO_1 user_info = NULL; |
| + DWORD age = 0; |
| + |
| + NET_API_STATUS ret = NetUserGetInfo(NULL, username, 1, (LPBYTE*) &user_info); |
| + |
| + if (ret == NERR_Success) { |
| + // Returns seconds since last password change. |
| + age = user_info->usri1_password_age; |
| + NetApiBufferFree(user_info); |
| + } |
| + |
| + base::Time changed = base::Time::Now() - base::TimeDelta::FromSeconds(age); |
| + |
| + return changed.ToInternalValue(); |
| +} |
| + |
| +static bool checkBlankPassword(WCHAR* username) { |
|
Patrick Dubroy
2013/11/12 15:41:39
Nit: Function names should start with a capital le
Will Harris
2013/11/26 22:07:12
Done.
|
| + PrefService* local_state = g_browser_process->local_state(); |
| + int64 last_changed = getPasswordLastChanged(username); |
| + bool need_recheck = true; |
| + bool blank_password = false; |
| + |
| + if (local_state->HasPrefPath(prefs::kOsPasswordBlank)) { |
| + blank_password = local_state->GetBoolean(prefs::kOsPasswordBlank); |
| + if (local_state->HasPrefPath(prefs::kOsPasswordLastChanged)) { |
| + int64 pref_last_changed = |
| + local_state->GetInt64(prefs::kOsPasswordLastChanged); |
| + if (pref_last_changed > 0 && |
| + last_changed <= pref_last_changed) { |
| + need_recheck = false; |
| + } |
| + } |
| + } |
| + |
| + if (need_recheck) { |
| + HANDLE handle = INVALID_HANDLE_VALUE; |
| + |
| + // Attempt to login using blank password. |
| + DWORD logon_result = LogonUser(username, |
| + L".", |
| + L"", |
| + LOGON32_LOGON_NETWORK, |
| + LOGON32_PROVIDER_DEFAULT, |
| + &handle); |
| + |
| + // Win XP and later return ERROR_ACCOUNT_RESTRICTION for blank password. |
| + if (logon_result) { |
|
Patrick Dubroy
2013/11/12 15:41:39
Nit: No braces necessary here.
Will Harris
2013/11/26 22:07:12
Done.
|
| + CloseHandle(handle); |
| + } |
| + |
| + blank_password = (logon_result || |
| + GetLastError() == ERROR_ACCOUNT_RESTRICTION); |
| + } |
| + |
| + // Account for clock skew between pulling the password age and |
| + // writing to the preferences by adding a small skew factor here. |
| + last_changed += base::Time::kMicrosecondsPerSecond; |
| + |
| + // Save the blank password status for later. |
| + local_state->SetBoolean(prefs::kOsPasswordBlank, blank_password); |
| + local_state->SetInt64(prefs::kOsPasswordLastChanged, last_changed); |
| + |
| + return blank_password; |
| +} |
| + |
| +bool AuthenticateUser(content::WebContents* web_contents) { |
| + bool retval = false; |
| + CREDUI_INFO cui = {}; |
| + WCHAR username[CREDUI_MAX_USERNAME_LENGTH+1] = {}; |
| + WCHAR displayname[CREDUI_MAX_USERNAME_LENGTH+1] = {}; |
| + WCHAR password[CREDUI_MAX_PASSWORD_LENGTH+1] = {}; |
| + DWORD username_length = CREDUI_MAX_USERNAME_LENGTH; |
| + std::wstring product_name = |
| + UTF16ToWide(l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); |
| + std::wstring password_prompt = |
| + UTF16ToWide(l10n_util::GetStringUTF16( |
| + IDS_PASSWORDS_PAGE_AUTHENTICATION_PROMPT)); |
| + HANDLE handle = INVALID_HANDLE_VALUE; |
| + int tries = 0; |
| + bool use_displayname = false; |
| + bool use_principalname = false; |
| + DWORD logon_result = 0; |
| + |
| + // On a domain, we obtain the User Principal Name |
| + // for domain authentication. |
| + if (GetUserNameEx(NameUserPrincipal, username, &username_length)) { |
| + use_principalname = true; |
| + } else { |
| + username_length = CREDUI_MAX_USERNAME_LENGTH; |
| + // Otherwise, we're a workstation, use the plain local username. |
| + if (!GetUserName(username, &username_length)) { |
| + DLOG(ERROR) << "Unable to obtain username " << GetLastError(); |
| + return false; |
| + } else { |
| + // As we are on a workstation, it's possible the user |
| + // has no password, so check here. |
| + if (checkBlankPassword(username)) { |
|
Patrick Dubroy
2013/11/12 15:41:39
Nit: No braces necessary here.
Will Harris
2013/11/26 22:07:12
Done.
|
| + return true; |
| + } |
| + } |
| + } |
| + |
| + // Try and obtain a friendly display name. |
| + username_length = CREDUI_MAX_USERNAME_LENGTH; |
| + if (GetUserNameEx(NameDisplay, displayname, &username_length)) { |
|
Patrick Dubroy
2013/11/12 15:41:39
Nit: No braces necessary here.
Will Harris
2013/11/26 22:07:12
Done.
|
| + use_displayname = true; |
| + } |
| + |
| + cui.cbSize = sizeof(CREDUI_INFO); |
| + cui.hwndParent = NULL; |
| + |
| + gfx::NativeView view = web_contents->GetRenderViewHost()-> |
| + GetView()->GetNativeView(); |
| +#if defined(USE_AURA) |
| + cui.hwndParent = view->GetDispatcher()->GetAcceleratedWidget(); |
| +#else |
| + cui.hwndParent = view; |
| +#endif |
| + |
| + cui.pszMessageText = password_prompt.c_str(); |
| + cui.pszCaptionText = product_name.c_str(); |
| + |
| + cui.hbmBanner = NULL; |
| + BOOL save_password = FALSE; |
| + DWORD credErr = NO_ERROR; |
| + |
| + do { |
| + tries++; |
| + |
| + // TODO(wfh) Make sure we support smart cards here. |
| + credErr = CredUIPromptForCredentials( |
| + &cui, |
|
Patrick Dubroy
2013/11/12 15:41:39
This column of args should be indented 4 spaces pa
Will Harris
2013/11/26 22:07:12
Done.
|
| + product_name.c_str(), |
| + NULL, |
| + 0, |
| + use_displayname ? displayname : username, |
| + CREDUI_MAX_USERNAME_LENGTH+1, |
| + password, |
| + CREDUI_MAX_PASSWORD_LENGTH+1, |
| + &save_password, |
| + kCredUiDefaultFlags | |
| + (tries > 1 ? CREDUI_FLAGS_INCORRECT_PASSWORD : 0)); |
| + |
| + if (credErr == NO_ERROR) { |
| + logon_result = LogonUser(username, |
| + use_principalname ? NULL : L".", |
| + password, |
| + LOGON32_LOGON_NETWORK, |
| + LOGON32_PROVIDER_DEFAULT, |
| + &handle); |
| + if (logon_result) { |
| + retval = true; |
| + CloseHandle(handle); |
| + } else { |
| + if (GetLastError() == ERROR_ACCOUNT_RESTRICTION && |
| + wcslen(password) == 0) { |
| + // Password is blank, so permit. |
| + retval = true; |
| + } else { |
| + DLOG(WARNING) << "Unable to authenticate " << GetLastError(); |
| + } |
| + } |
| + SecureZeroMemory(password, sizeof(password)); |
| + } |
| + } while (credErr == NO_ERROR && |
| + (retval == false && tries < kMaxPasswordRetries)); |
| + return retval; |
| +} |
| + |
| +} // namespace password_manager_util |