1 // Copyright 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.
5 // windows.h must be first otherwise Win8 SDK breaks.
10 // SECURITY_WIN32 must be defined in order to get
11 // EXTENDED_NAME_FORMAT enumeration.
12 #define SECURITY_WIN32 1
16 #include "chrome/browser/password_manager/password_manager_util.h"
18 #include "base/prefs/pref_service.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/time/time.h"
21 #include "base/win/windows_version.h"
22 #include "chrome/browser/browser_process.h"
23 #include "chrome/grit/chromium_strings.h"
24 #include "components/password_manager/core/browser/password_manager.h"
25 #include "components/password_manager/core/common/password_manager_pref_names.h"
26 #include "content/public/browser/render_view_host.h"
27 #include "content/public/browser/render_widget_host_view.h"
28 #include "ui/base/l10n/l10n_util.h"
31 #include "ui/aura/window.h"
32 #include "ui/aura/window_tree_host.h"
35 namespace password_manager_util
{
37 const unsigned kMaxPasswordRetries
= 3;
39 const unsigned kCredUiDefaultFlags
=
40 CREDUI_FLAGS_GENERIC_CREDENTIALS
|
41 CREDUI_FLAGS_EXCLUDE_CERTIFICATES
|
42 CREDUI_FLAGS_KEEP_USERNAME
|
43 CREDUI_FLAGS_ALWAYS_SHOW_UI
|
44 CREDUI_FLAGS_DO_NOT_PERSIST
;
46 static int64
GetPasswordLastChanged(WCHAR
* username
) {
47 LPUSER_INFO_1 user_info
= NULL
;
50 NET_API_STATUS ret
= NetUserGetInfo(NULL
, username
, 1, (LPBYTE
*) &user_info
);
52 if (ret
== NERR_Success
) {
53 // Returns seconds since last password change.
54 age
= user_info
->usri1_password_age
;
55 NetApiBufferFree(user_info
);
60 base::Time changed
= base::Time::Now() - base::TimeDelta::FromSeconds(age
);
62 return changed
.ToInternalValue();
65 static bool CheckBlankPassword(WCHAR
* username
) {
66 PrefService
* local_state
= g_browser_process
->local_state();
67 int64 last_changed
= GetPasswordLastChanged(username
);
68 bool need_recheck
= true;
69 bool blank_password
= false;
71 // If we cannot determine when the password was last changed
72 // then assume the password is not blank
73 if (last_changed
== -1)
77 local_state
->GetBoolean(password_manager::prefs::kOsPasswordBlank
);
78 int64 pref_last_changed
=
79 local_state
->GetInt64(password_manager::prefs::kOsPasswordLastChanged
);
80 if (pref_last_changed
> 0 && last_changed
<= pref_last_changed
) {
85 HANDLE handle
= INVALID_HANDLE_VALUE
;
87 // Attempt to login using blank password.
88 DWORD logon_result
= LogonUser(username
,
91 LOGON32_LOGON_NETWORK
,
92 LOGON32_PROVIDER_DEFAULT
,
95 // Win XP and later return ERROR_ACCOUNT_RESTRICTION for blank password.
99 // In the case the password is blank, then LogonUser returns a failure,
100 // handle is INVALID_HANDLE_VALUE, and GetLastError() is
101 // ERROR_ACCOUNT_RESTRICTION.
102 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms681385
103 blank_password
= (logon_result
||
104 GetLastError() == ERROR_ACCOUNT_RESTRICTION
);
107 // Account for clock skew between pulling the password age and
108 // writing to the preferences by adding a small skew factor here.
109 last_changed
+= base::Time::kMicrosecondsPerSecond
;
111 // Save the blank password status for later.
112 local_state
->SetBoolean(password_manager::prefs::kOsPasswordBlank
,
114 local_state
->SetInt64(password_manager::prefs::kOsPasswordLastChanged
,
117 return blank_password
;
120 OsPasswordStatus
GetOsPasswordStatus() {
121 DWORD username_length
= CREDUI_MAX_USERNAME_LENGTH
;
122 WCHAR username
[CREDUI_MAX_USERNAME_LENGTH
+1] = {};
123 OsPasswordStatus retVal
= PASSWORD_STATUS_UNKNOWN
;
125 if (GetUserNameEx(NameUserPrincipal
, username
, &username_length
)) {
126 // If we are on a domain, it is almost certain that the password is not
127 // blank, but we do not actively check any further than this to avoid any
128 // failed login attempts hitting the domain controller.
129 retVal
= PASSWORD_STATUS_WIN_DOMAIN
;
131 username_length
= CREDUI_MAX_USERNAME_LENGTH
;
132 if (GetUserName(username
, &username_length
)) {
133 retVal
= CheckBlankPassword(username
) ? PASSWORD_STATUS_BLANK
:
134 PASSWORD_STATUS_NONBLANK
;
141 bool AuthenticateUser(gfx::NativeWindow window
) {
143 CREDUI_INFO cui
= {};
144 WCHAR username
[CREDUI_MAX_USERNAME_LENGTH
+1] = {};
145 WCHAR displayname
[CREDUI_MAX_USERNAME_LENGTH
+1] = {};
146 WCHAR password
[CREDUI_MAX_PASSWORD_LENGTH
+1] = {};
147 DWORD username_length
= CREDUI_MAX_USERNAME_LENGTH
;
148 base::string16 product_name
= l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
);
149 base::string16 password_prompt
=
150 l10n_util::GetStringUTF16(IDS_PASSWORDS_PAGE_AUTHENTICATION_PROMPT
);
151 HANDLE handle
= INVALID_HANDLE_VALUE
;
153 bool use_displayname
= false;
154 bool use_principalname
= false;
155 DWORD logon_result
= 0;
157 // Disable password manager reauthentication before Windows 7.
158 // This is because of an interaction between LogonUser() and the sandbox.
159 // http://crbug.com/345916
160 if (base::win::GetVersion() < base::win::VERSION_WIN7
)
163 // On a domain, we obtain the User Principal Name
164 // for domain authentication.
165 if (GetUserNameEx(NameUserPrincipal
, username
, &username_length
)) {
166 use_principalname
= true;
168 username_length
= CREDUI_MAX_USERNAME_LENGTH
;
169 // Otherwise, we're a workstation, use the plain local username.
170 if (!GetUserName(username
, &username_length
)) {
171 DLOG(ERROR
) << "Unable to obtain username " << GetLastError();
174 // As we are on a workstation, it's possible the user
175 // has no password, so check here.
176 if (CheckBlankPassword(username
))
181 // Try and obtain a friendly display name.
182 username_length
= CREDUI_MAX_USERNAME_LENGTH
;
183 if (GetUserNameEx(NameDisplay
, displayname
, &username_length
))
184 use_displayname
= true;
186 cui
.cbSize
= sizeof(CREDUI_INFO
);
187 cui
.hwndParent
= NULL
;
188 #if defined(USE_AURA)
189 cui
.hwndParent
= window
->GetHost()->GetAcceleratedWidget();
191 cui
.hwndParent
= window
;
194 cui
.pszMessageText
= password_prompt
.c_str();
195 cui
.pszCaptionText
= product_name
.c_str();
197 cui
.hbmBanner
= NULL
;
198 BOOL save_password
= FALSE
;
199 DWORD credErr
= NO_ERROR
;
204 // TODO(wfh) Make sure we support smart cards here.
205 credErr
= CredUIPromptForCredentials(
207 product_name
.c_str(),
210 use_displayname
? displayname
: username
,
211 CREDUI_MAX_USERNAME_LENGTH
+1,
213 CREDUI_MAX_PASSWORD_LENGTH
+1,
215 kCredUiDefaultFlags
|
216 (tries
> 1 ? CREDUI_FLAGS_INCORRECT_PASSWORD
: 0));
218 if (credErr
== NO_ERROR
) {
219 logon_result
= LogonUser(username
,
220 use_principalname
? NULL
: L
".",
222 LOGON32_LOGON_NETWORK
,
223 LOGON32_PROVIDER_DEFAULT
,
229 if (GetLastError() == ERROR_ACCOUNT_RESTRICTION
&&
230 wcslen(password
) == 0) {
231 // Password is blank, so permit.
234 DLOG(WARNING
) << "Unable to authenticate " << GetLastError();
237 SecureZeroMemory(password
, sizeof(password
));
239 } while (credErr
== NO_ERROR
&&
240 (retval
== false && tries
< kMaxPasswordRetries
));
244 } // namespace password_manager_util