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..146670794f33f3e288990390dde5cf0c8e02ed58 |
| --- /dev/null |
| +++ b/chrome/browser/password_manager/password_manager_util_win.cc |
| @@ -0,0 +1,296 @@ |
| +// 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 <ntsecapi.h> |
| +#include <wincred.h> |
| +#include <subauth.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/strings/utf_string_conversions.h" |
| +#include "grit/chromium_strings.h" |
| +#include "grit/generated_resources.h" |
| +#include "ui/base/l10n/l10n_util.h" |
| + |
| +namespace password_manager_util { |
| + |
| +// http://msdn.microsoft.com/en-us/library/cc245617.aspx. |
| +#define SAM_USERALLINFORMATION 21 |
| + |
| +// http://msdn.microsoft.com/en-us/library/cc245522.aspx. |
| +#define DOMAIN_READ 0x00020084 |
| +#define DOMAIN_LOOKUP 0x00000200 |
| + |
| +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; |
| + |
| +// http://msdn.microsoft.com/en-us/library/cc245753.aspx. |
| +typedef NTSTATUS (WINAPI *SamConnectPtr)(PUNICODE_STRING ServerName, |
| + PSAM_HANDLE ServerHandle, |
| + DWORD DesiredAccess, |
| + PVOID); |
| + |
| +// http://msdn.microsoft.com/en-us/library/cc245748.aspx. |
| +typedef NTSTATUS (WINAPI *SamOpenDomainPtr)(SAM_HANDLE ServerHandle, |
| + DWORD dwAccess, |
| + PSID DomainId, |
| + PSAM_HANDLE DomainHandle); |
| + |
| +// http://msdn.microsoft.com/en-us/library/cc245712.aspx. |
| +typedef NTSTATUS (WINAPI *SamLookupNamesInDomainPtr)(SAM_HANDLE DomainHandle, |
| + ULONG Count, |
| + PUNICODE_STRING Names, |
| + PULONG* RelativeIds, |
| + PULONG* Use); |
| + |
| +// http://msdn.microsoft.com/en-us/library/cc245752.aspx. |
| +typedef NTSTATUS (WINAPI *SamOpenUserPtr)(SAM_HANDLE DomainHandle, |
| + DWORD dwAccess, |
| + DWORD UserId, |
| + PSAM_HANDLE UserHandle); |
| + |
| +// http://msdn.microsoft.com/en-us/library/cc245786.aspx. |
| +typedef NTSTATUS (WINAPI *SamQueryInformationUserPtr)( |
| + SAM_HANDLE UserHandle, |
| + DWORD UserInformationClass, |
| + PVOID Buffer); |
| + |
| +// http://msdn.microsoft.com/en-us/library/cc245722.aspx. |
| +typedef NTSTATUS (WINAPI *SamCloseHandlePtr)(PSAM_HANDLE SamHandle); |
| +typedef NTSTATUS (WINAPI *SamFreeMemoryPtr)(PVOID Buffer); |
| + |
| +static bool checkBlankPassword(WCHAR* pUsername) { |
| + SAM_HANDLE handleSam = INVALID_HANDLE_VALUE; |
| + HMODULE samlibDll = LoadLibrary(L"samlib.dll"); |
|
cpu_(ooo_6.6-7.5)
2013/10/25 17:04:43
you are loading the library in the UI thread?
|
| + SAM_HANDLE handleDomain = NULL; |
| + SAM_HANDLE handleUser = NULL; |
| + UNICODE_STRING username = {wcslen(pUsername) * sizeof(WCHAR), |
| + wcslen(pUsername) * sizeof(WCHAR), |
| + pUsername}; |
| + bool hasBlankPassword = false; |
| + |
| + if (samlibDll) { |
| + SamConnectPtr sam_connect_func = |
| + reinterpret_cast<SamConnectPtr>( |
| + GetProcAddress(samlibDll,"SamConnect")); |
| + SamOpenDomainPtr sam_open_domain_func = |
| + reinterpret_cast<SamOpenDomainPtr>( |
| + GetProcAddress(samlibDll,"SamOpenDomain")); |
| + SamLookupNamesInDomainPtr sam_lookup_names_in_domain_func = |
| + reinterpret_cast<SamLookupNamesInDomainPtr>( |
| + GetProcAddress(samlibDll,"SamLookupNamesInDomain")); |
| + SamOpenUserPtr sam_open_user_func = |
| + reinterpret_cast<SamOpenUserPtr>( |
| + GetProcAddress(samlibDll,"SamOpenUser")); |
| + SamQueryInformationUserPtr sam_query_information_user_func = |
| + reinterpret_cast<SamQueryInformationUserPtr>( |
| + GetProcAddress(samlibDll,"SamQueryInformationUser")); |
| + SamCloseHandlePtr sam_close_handle_func = |
| + reinterpret_cast<SamCloseHandlePtr>( |
| + GetProcAddress(samlibDll,"SamCloseHandle")); |
| + SamFreeMemoryPtr sam_free_memory_func = |
| + reinterpret_cast<SamFreeMemoryPtr>( |
| + GetProcAddress(samlibDll,"SamFreeMemory")); |
| + |
| + if ((sam_connect_func == NULL) || |
| + (sam_open_domain_func == NULL) || |
| + (sam_lookup_names_in_domain_func == NULL) || |
| + (sam_open_user_func == NULL) || |
| + (sam_query_information_user_func == NULL) || |
| + (sam_close_handle_func == NULL) || |
| + (sam_free_memory_func == NULL)) |
| + { |
| + FreeLibrary(samlibDll); |
| + return false; |
| + } |
| + |
| + LSA_OBJECT_ATTRIBUTES connectionAttrib = {}; |
| + LSA_HANDLE handlePolicy = NULL; |
| + |
| + // http://msdn2.microsoft.com/en-us/library/ms721895(VS.85).aspx |
| + PPOLICY_ACCOUNT_DOMAIN_INFO structInfoPolicy; |
| + |
| + connectionAttrib.Length = sizeof(LSA_OBJECT_ATTRIBUTES); |
| + |
| + NTSTATUS retval = LsaOpenPolicy(NULL, |
| + &connectionAttrib, |
| + POLICY_VIEW_LOCAL_INFORMATION, |
| + &handlePolicy); |
| + |
| + if (retval == STATUS_SUCCESS) { |
| + // http://msdn.microsoft.com/en-us/library/aa378313(VS.85).aspx |
| + retval = LsaQueryInformationPolicy(handlePolicy, |
| + PolicyAccountDomainInformation, |
| + reinterpret_cast<PVOID*>( |
| + &structInfoPolicy)); |
| + |
| + if (retval == STATUS_SUCCESS) { |
| + // http://msdn.microsoft.com/en-us/library/cc245753.aspx |
| + retval = sam_connect_func(NULL, &handleSam, MAXIMUM_ALLOWED, NULL); |
| + |
| + if (retval == STATUS_SUCCESS) { |
| + // http://msdn.microsoft.com/en-us/library/cc245748.aspx |
| + retval = sam_open_domain_func(handleSam, |
| + DOMAIN_READ | DOMAIN_LOOKUP, |
| + structInfoPolicy->DomainSid, |
| + &handleDomain); |
| + |
| + if (retval == STATUS_SUCCESS) { |
| + PULONG relativeIds = NULL; |
| + PULONG use = NULL; |
| + // Get the RID for the given user name. |
| + // http://msdn.microsoft.com/en-us/library/cc245712.aspx. |
| + retval = sam_lookup_names_in_domain_func(handleDomain, |
| + 1, |
| + &username, |
| + &relativeIds, |
| + &use); |
| + if (retval == STATUS_SUCCESS) { |
| + PUSER_ALL_INFORMATION ptrUserInfo = NULL; |
| + // http://msdn.microsoft.com/en-us/library/cc245752.aspx |
| + retval = sam_open_user_func(handleDomain, |
| + GENERIC_READ, |
| + relativeIds[0], |
| + &handleUser); |
| + |
| + if (retval == STATUS_SUCCESS) { |
| + // http://msdn.microsoft.com/en-us/library/cc245786.aspx |
| + retval = sam_query_information_user_func(handleUser, |
| + SAM_USERALLINFORMATION, |
| + &ptrUserInfo); |
| + if (retval == STATUS_SUCCESS) { |
| + // If either the dBCSPwd attribute or the unicodePwd |
| + // attribute does not have a value, or if either of these is |
| + // equal to the respective hash of a zero-length string, |
| + // PasswordCanChange MUST be 0. |
| + // http://msdn.microsoft.com/en-us/library/cc245738.aspx |
| + if (ptrUserInfo->PasswordCanChange.HighPart == 0 && |
| + ptrUserInfo->PasswordCanChange.LowPart == 0 ) { |
| + hasBlankPassword = true; |
| + } |
| + sam_free_memory_func(ptrUserInfo); |
| + } |
| + // http://msdn.microsoft.com/en-us/library/cc245722.aspx |
| + sam_close_handle_func(&handleUser); |
| + } |
| + } |
| + } |
| + } |
| + LsaFreeMemory(structInfoPolicy); |
| + } |
| + LsaClose(handlePolicy); |
| + } |
| + FreeLibrary(samlibDll); |
| + } |
| + return hasBlankPassword; |
| +} |
| + |
| + |
| +bool AuthenticateUser() { |
| + 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; |
| + 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 |