| 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..75492abb8cff56b57d182e3196bbc93f25e98de0
|
| --- /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 "chrome/browser/password_manager/password_manager_util.h"
|
| +
|
| +#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/common/pref_names.h"
|
| +#include "content/public/browser/render_view_host.h"
|
| +#include "content/public/browser/render_widget_host_view.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) {
|
| + 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);
|
| + } else {
|
| + return -1;
|
| + }
|
| +
|
| + base::Time changed = base::Time::Now() - base::TimeDelta::FromSeconds(age);
|
| +
|
| + return changed.ToInternalValue();
|
| +}
|
| +
|
| +static bool CheckBlankPassword(WCHAR* username) {
|
| + PrefService* local_state = g_browser_process->local_state();
|
| + int64 last_changed = GetPasswordLastChanged(username);
|
| + bool need_recheck = true;
|
| + bool blank_password = false;
|
| +
|
| + // If we cannot determine when the password was last changed
|
| + // then assume the password is not blank
|
| + if (last_changed == -1)
|
| + return false;
|
| +
|
| + blank_password = local_state->GetBoolean(prefs::kOsPasswordBlank);
|
| + 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)
|
| + CloseHandle(handle);
|
| +
|
| + // In the case the password is blank, then LogonUser returns a failure,
|
| + // handle is INVALID_HANDLE_VALUE, and GetLastError() is
|
| + // ERROR_ACCOUNT_RESTRICTION.
|
| + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms681385
|
| + 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(gfx::NativeWindow window) {
|
| + 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))
|
| + return true;
|
| + }
|
| + }
|
| +
|
| + // Try and obtain a friendly display name.
|
| + username_length = CREDUI_MAX_USERNAME_LENGTH;
|
| + if (GetUserNameEx(NameDisplay, displayname, &username_length))
|
| + use_displayname = true;
|
| +
|
| + cui.cbSize = sizeof(CREDUI_INFO);
|
| + cui.hwndParent = NULL;
|
| +#if defined(USE_AURA)
|
| + cui.hwndParent = window->GetDispatcher()->host()->GetAcceleratedWidget();
|
| +#else
|
| + cui.hwndParent = window;
|
| +#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,
|
| + 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
|
|
|