Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2013 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 // windows.h must be first otherwise Win8 SDK breaks. | |
| 6 #include <windows.h> | |
| 7 #include <ntsecapi.h> | |
| 8 #include <wincred.h> | |
| 9 #include <subauth.h> | |
| 10 | |
| 11 // SECURITY_WIN32 must be defined in order to get | |
| 12 // EXTENDED_NAME_FORMAT enumeration. | |
| 13 #define SECURITY_WIN32 1 | |
| 14 #include <security.h> | |
| 15 #undef SECURITY_WIN32 | |
| 16 | |
| 17 #include "base/strings/utf_string_conversions.h" | |
| 18 #include "grit/chromium_strings.h" | |
| 19 #include "grit/generated_resources.h" | |
| 20 #include "ui/base/l10n/l10n_util.h" | |
| 21 | |
| 22 namespace password_manager_util { | |
| 23 | |
| 24 // http://msdn.microsoft.com/en-us/library/cc245617.aspx. | |
| 25 #define SAM_USERALLINFORMATION 21 | |
| 26 | |
| 27 // http://msdn.microsoft.com/en-us/library/cc245522.aspx. | |
| 28 #define DOMAIN_READ 0x00020084 | |
| 29 #define DOMAIN_LOOKUP 0x00000200 | |
| 30 | |
| 31 const unsigned kMaxPasswordRetries = 3; | |
| 32 const unsigned kCredUiDefaultFlags = | |
| 33 CREDUI_FLAGS_GENERIC_CREDENTIALS | | |
| 34 CREDUI_FLAGS_EXCLUDE_CERTIFICATES | | |
| 35 CREDUI_FLAGS_KEEP_USERNAME | | |
| 36 CREDUI_FLAGS_ALWAYS_SHOW_UI | | |
| 37 CREDUI_FLAGS_DO_NOT_PERSIST; | |
| 38 | |
| 39 // http://msdn.microsoft.com/en-us/library/cc245753.aspx. | |
| 40 typedef NTSTATUS (WINAPI *SamConnectPtr)(PUNICODE_STRING ServerName, | |
| 41 PSAM_HANDLE ServerHandle, | |
| 42 DWORD DesiredAccess, | |
| 43 PVOID); | |
| 44 | |
| 45 // http://msdn.microsoft.com/en-us/library/cc245748.aspx. | |
| 46 typedef NTSTATUS (WINAPI *SamOpenDomainPtr)(SAM_HANDLE ServerHandle, | |
| 47 DWORD dwAccess, | |
| 48 PSID DomainId, | |
| 49 PSAM_HANDLE DomainHandle); | |
| 50 | |
| 51 // http://msdn.microsoft.com/en-us/library/cc245712.aspx. | |
| 52 typedef NTSTATUS (WINAPI *SamLookupNamesInDomainPtr)(SAM_HANDLE DomainHandle, | |
| 53 ULONG Count, | |
| 54 PUNICODE_STRING Names, | |
| 55 PULONG* RelativeIds, | |
| 56 PULONG* Use); | |
| 57 | |
| 58 // http://msdn.microsoft.com/en-us/library/cc245752.aspx. | |
| 59 typedef NTSTATUS (WINAPI *SamOpenUserPtr)(SAM_HANDLE DomainHandle, | |
| 60 DWORD dwAccess, | |
| 61 DWORD UserId, | |
| 62 PSAM_HANDLE UserHandle); | |
| 63 | |
| 64 // http://msdn.microsoft.com/en-us/library/cc245786.aspx. | |
| 65 typedef NTSTATUS (WINAPI *SamQueryInformationUserPtr)( | |
| 66 SAM_HANDLE UserHandle, | |
| 67 DWORD UserInformationClass, | |
| 68 PVOID Buffer); | |
| 69 | |
| 70 // http://msdn.microsoft.com/en-us/library/cc245722.aspx. | |
| 71 typedef NTSTATUS (WINAPI *SamCloseHandlePtr)(PSAM_HANDLE SamHandle); | |
| 72 typedef NTSTATUS (WINAPI *SamFreeMemoryPtr)(PVOID Buffer); | |
| 73 | |
| 74 static bool checkBlankPassword(WCHAR* pUsername) { | |
| 75 SAM_HANDLE handleSam = INVALID_HANDLE_VALUE; | |
| 76 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?
| |
| 77 SAM_HANDLE handleDomain = NULL; | |
| 78 SAM_HANDLE handleUser = NULL; | |
| 79 UNICODE_STRING username = {wcslen(pUsername) * sizeof(WCHAR), | |
| 80 wcslen(pUsername) * sizeof(WCHAR), | |
| 81 pUsername}; | |
| 82 bool hasBlankPassword = false; | |
| 83 | |
| 84 if (samlibDll) { | |
| 85 SamConnectPtr sam_connect_func = | |
| 86 reinterpret_cast<SamConnectPtr>( | |
| 87 GetProcAddress(samlibDll,"SamConnect")); | |
| 88 SamOpenDomainPtr sam_open_domain_func = | |
| 89 reinterpret_cast<SamOpenDomainPtr>( | |
| 90 GetProcAddress(samlibDll,"SamOpenDomain")); | |
| 91 SamLookupNamesInDomainPtr sam_lookup_names_in_domain_func = | |
| 92 reinterpret_cast<SamLookupNamesInDomainPtr>( | |
| 93 GetProcAddress(samlibDll,"SamLookupNamesInDomain")); | |
| 94 SamOpenUserPtr sam_open_user_func = | |
| 95 reinterpret_cast<SamOpenUserPtr>( | |
| 96 GetProcAddress(samlibDll,"SamOpenUser")); | |
| 97 SamQueryInformationUserPtr sam_query_information_user_func = | |
| 98 reinterpret_cast<SamQueryInformationUserPtr>( | |
| 99 GetProcAddress(samlibDll,"SamQueryInformationUser")); | |
| 100 SamCloseHandlePtr sam_close_handle_func = | |
| 101 reinterpret_cast<SamCloseHandlePtr>( | |
| 102 GetProcAddress(samlibDll,"SamCloseHandle")); | |
| 103 SamFreeMemoryPtr sam_free_memory_func = | |
| 104 reinterpret_cast<SamFreeMemoryPtr>( | |
| 105 GetProcAddress(samlibDll,"SamFreeMemory")); | |
| 106 | |
| 107 if ((sam_connect_func == NULL) || | |
| 108 (sam_open_domain_func == NULL) || | |
| 109 (sam_lookup_names_in_domain_func == NULL) || | |
| 110 (sam_open_user_func == NULL) || | |
| 111 (sam_query_information_user_func == NULL) || | |
| 112 (sam_close_handle_func == NULL) || | |
| 113 (sam_free_memory_func == NULL)) | |
| 114 { | |
| 115 FreeLibrary(samlibDll); | |
| 116 return false; | |
| 117 } | |
| 118 | |
| 119 LSA_OBJECT_ATTRIBUTES connectionAttrib = {}; | |
| 120 LSA_HANDLE handlePolicy = NULL; | |
| 121 | |
| 122 // http://msdn2.microsoft.com/en-us/library/ms721895(VS.85).aspx | |
| 123 PPOLICY_ACCOUNT_DOMAIN_INFO structInfoPolicy; | |
| 124 | |
| 125 connectionAttrib.Length = sizeof(LSA_OBJECT_ATTRIBUTES); | |
| 126 | |
| 127 NTSTATUS retval = LsaOpenPolicy(NULL, | |
| 128 &connectionAttrib, | |
| 129 POLICY_VIEW_LOCAL_INFORMATION, | |
| 130 &handlePolicy); | |
| 131 | |
| 132 if (retval == STATUS_SUCCESS) { | |
| 133 // http://msdn.microsoft.com/en-us/library/aa378313(VS.85).aspx | |
| 134 retval = LsaQueryInformationPolicy(handlePolicy, | |
| 135 PolicyAccountDomainInformation, | |
| 136 reinterpret_cast<PVOID*>( | |
| 137 &structInfoPolicy)); | |
| 138 | |
| 139 if (retval == STATUS_SUCCESS) { | |
| 140 // http://msdn.microsoft.com/en-us/library/cc245753.aspx | |
| 141 retval = sam_connect_func(NULL, &handleSam, MAXIMUM_ALLOWED, NULL); | |
| 142 | |
| 143 if (retval == STATUS_SUCCESS) { | |
| 144 // http://msdn.microsoft.com/en-us/library/cc245748.aspx | |
| 145 retval = sam_open_domain_func(handleSam, | |
| 146 DOMAIN_READ | DOMAIN_LOOKUP, | |
| 147 structInfoPolicy->DomainSid, | |
| 148 &handleDomain); | |
| 149 | |
| 150 if (retval == STATUS_SUCCESS) { | |
| 151 PULONG relativeIds = NULL; | |
| 152 PULONG use = NULL; | |
| 153 // Get the RID for the given user name. | |
| 154 // http://msdn.microsoft.com/en-us/library/cc245712.aspx. | |
| 155 retval = sam_lookup_names_in_domain_func(handleDomain, | |
| 156 1, | |
| 157 &username, | |
| 158 &relativeIds, | |
| 159 &use); | |
| 160 if (retval == STATUS_SUCCESS) { | |
| 161 PUSER_ALL_INFORMATION ptrUserInfo = NULL; | |
| 162 // http://msdn.microsoft.com/en-us/library/cc245752.aspx | |
| 163 retval = sam_open_user_func(handleDomain, | |
| 164 GENERIC_READ, | |
| 165 relativeIds[0], | |
| 166 &handleUser); | |
| 167 | |
| 168 if (retval == STATUS_SUCCESS) { | |
| 169 // http://msdn.microsoft.com/en-us/library/cc245786.aspx | |
| 170 retval = sam_query_information_user_func(handleUser, | |
| 171 SAM_USERALLINFORMATION, | |
| 172 &ptrUserInfo); | |
| 173 if (retval == STATUS_SUCCESS) { | |
| 174 // If either the dBCSPwd attribute or the unicodePwd | |
| 175 // attribute does not have a value, or if either of these is | |
| 176 // equal to the respective hash of a zero-length string, | |
| 177 // PasswordCanChange MUST be 0. | |
| 178 // http://msdn.microsoft.com/en-us/library/cc245738.aspx | |
| 179 if (ptrUserInfo->PasswordCanChange.HighPart == 0 && | |
| 180 ptrUserInfo->PasswordCanChange.LowPart == 0 ) { | |
| 181 hasBlankPassword = true; | |
| 182 } | |
| 183 sam_free_memory_func(ptrUserInfo); | |
| 184 } | |
| 185 // http://msdn.microsoft.com/en-us/library/cc245722.aspx | |
| 186 sam_close_handle_func(&handleUser); | |
| 187 } | |
| 188 } | |
| 189 } | |
| 190 } | |
| 191 LsaFreeMemory(structInfoPolicy); | |
| 192 } | |
| 193 LsaClose(handlePolicy); | |
| 194 } | |
| 195 FreeLibrary(samlibDll); | |
| 196 } | |
| 197 return hasBlankPassword; | |
| 198 } | |
| 199 | |
| 200 | |
| 201 bool AuthenticateUser() { | |
| 202 bool retval = false; | |
| 203 CREDUI_INFO cui = {}; | |
| 204 WCHAR username[CREDUI_MAX_USERNAME_LENGTH+1] = {}; | |
| 205 WCHAR displayname[CREDUI_MAX_USERNAME_LENGTH+1] = {}; | |
| 206 WCHAR password[CREDUI_MAX_PASSWORD_LENGTH+1] = {}; | |
| 207 DWORD username_length = CREDUI_MAX_USERNAME_LENGTH; | |
| 208 std::wstring product_name = | |
| 209 UTF16ToWide(l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); | |
| 210 std::wstring password_prompt = | |
| 211 UTF16ToWide(l10n_util::GetStringUTF16( | |
| 212 IDS_PASSWORDS_PAGE_AUTHENTICATION_PROMPT)); | |
| 213 HANDLE handle = INVALID_HANDLE_VALUE; | |
| 214 int tries = 0; | |
| 215 bool use_displayname = false; | |
| 216 bool use_principalname = false; | |
| 217 DWORD logon_result = 0; | |
| 218 | |
| 219 // On a domain, we obtain the User Principal Name | |
| 220 // for domain authentication. | |
| 221 if (GetUserNameEx(NameUserPrincipal, username, &username_length)) { | |
| 222 use_principalname = true; | |
| 223 } else { | |
| 224 username_length = CREDUI_MAX_USERNAME_LENGTH; | |
| 225 // Otherwise, we're a workstation, use the plain local username. | |
| 226 if (!GetUserName(username, &username_length)) { | |
| 227 DLOG(ERROR) << "Unable to obtain username " << GetLastError(); | |
| 228 return false; | |
| 229 } else { | |
| 230 // As we are on a workstation, it's possible the user | |
| 231 // has no password, so check here. | |
| 232 if (checkBlankPassword(username)) { | |
| 233 return true; | |
| 234 } | |
| 235 } | |
| 236 } | |
| 237 | |
| 238 // Try and obtain a friendly display name. | |
| 239 username_length = CREDUI_MAX_USERNAME_LENGTH; | |
| 240 if (GetUserNameEx(NameDisplay, displayname, &username_length)) { | |
| 241 use_displayname = true; | |
| 242 } | |
| 243 | |
| 244 cui.cbSize = sizeof(CREDUI_INFO); | |
| 245 cui.hwndParent = NULL; | |
| 246 cui.pszMessageText = password_prompt.c_str(); | |
| 247 cui.pszCaptionText = product_name.c_str(); | |
| 248 | |
| 249 cui.hbmBanner = NULL; | |
| 250 BOOL save_password = FALSE; | |
| 251 DWORD credErr = NO_ERROR; | |
| 252 | |
| 253 do { | |
| 254 tries++; | |
| 255 | |
| 256 // TODO(wfh) Make sure we support smart cards here. | |
| 257 credErr = CredUIPromptForCredentials( | |
| 258 &cui, | |
| 259 product_name.c_str(), | |
| 260 NULL, | |
| 261 0, | |
| 262 use_displayname ? displayname : username, | |
| 263 CREDUI_MAX_USERNAME_LENGTH+1, | |
| 264 password, | |
| 265 CREDUI_MAX_PASSWORD_LENGTH+1, | |
| 266 &save_password, | |
| 267 kCredUiDefaultFlags | | |
| 268 (tries > 1 ? CREDUI_FLAGS_INCORRECT_PASSWORD : 0)); | |
| 269 | |
| 270 if (credErr == NO_ERROR) { | |
| 271 logon_result = LogonUser(username, | |
| 272 use_principalname ? NULL : L".", | |
| 273 password, | |
| 274 LOGON32_LOGON_NETWORK, | |
| 275 LOGON32_PROVIDER_DEFAULT, | |
| 276 &handle); | |
| 277 if (logon_result) { | |
| 278 retval = true; | |
| 279 CloseHandle(handle); | |
| 280 } else { | |
| 281 if (GetLastError() == ERROR_ACCOUNT_RESTRICTION && | |
| 282 wcslen(password) == 0) { | |
| 283 // Password is blank, so permit. | |
| 284 retval = true; | |
| 285 } else { | |
| 286 DLOG(WARNING) << "Unable to authenticate " << GetLastError(); | |
| 287 } | |
| 288 } | |
| 289 SecureZeroMemory(password, sizeof(password)); | |
| 290 } | |
| 291 } while (credErr == NO_ERROR && | |
| 292 (retval == false && tries < kMaxPasswordRetries)); | |
| 293 return retval; | |
| 294 } | |
| 295 | |
| 296 } // namespace password_manager_util | |
| OLD | NEW |