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 |