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_registry_simple.h"
19 #include "base/prefs/pref_service.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/time/time.h"
22 #include "chrome/browser/browser_process.h"
23 #include "chrome/browser/password_manager/password_manager.h"
24 #include "chrome/common/pref_names.h"
25 #include "content/public/browser/render_view_host.h"
26 #include "content/public/browser/render_widget_host_view.h"
27 #include "grit/chromium_strings.h"
28 #include "grit/generated_resources.h"
29 #include "ui/base/l10n/l10n_util.h"
32 #include "ui/aura/root_window.h"
33 #include "ui/aura/window.h"
37 void PasswordManager::RegisterLocalPrefs(PrefRegistrySimple
* registry
) {
38 registry
->RegisterInt64Pref(prefs::kOsPasswordLastChanged
, 0);
39 registry
->RegisterBooleanPref(prefs::kOsPasswordBlank
, false);
42 namespace password_manager_util
{
44 const unsigned kMaxPasswordRetries
= 3;
46 const unsigned kCredUiDefaultFlags
=
47 CREDUI_FLAGS_GENERIC_CREDENTIALS
|
48 CREDUI_FLAGS_EXCLUDE_CERTIFICATES
|
49 CREDUI_FLAGS_KEEP_USERNAME
|
50 CREDUI_FLAGS_ALWAYS_SHOW_UI
|
51 CREDUI_FLAGS_DO_NOT_PERSIST
;
53 static int64
GetPasswordLastChanged(WCHAR
* username
) {
54 LPUSER_INFO_1 user_info
= NULL
;
57 NET_API_STATUS ret
= NetUserGetInfo(NULL
, username
, 1, (LPBYTE
*) &user_info
);
59 if (ret
== NERR_Success
) {
60 // Returns seconds since last password change.
61 age
= user_info
->usri1_password_age
;
62 NetApiBufferFree(user_info
);
67 base::Time changed
= base::Time::Now() - base::TimeDelta::FromSeconds(age
);
69 return changed
.ToInternalValue();
72 static bool CheckBlankPassword(WCHAR
* username
) {
73 PrefService
* local_state
= g_browser_process
->local_state();
74 int64 last_changed
= GetPasswordLastChanged(username
);
75 bool need_recheck
= true;
76 bool blank_password
= false;
78 // If we cannot determine when the password was last changed
79 // then assume the password is not blank
80 if (last_changed
== -1)
83 blank_password
= local_state
->GetBoolean(prefs::kOsPasswordBlank
);
84 int64 pref_last_changed
=
85 local_state
->GetInt64(prefs::kOsPasswordLastChanged
);
86 if (pref_last_changed
> 0 && last_changed
<= pref_last_changed
) {
91 HANDLE handle
= INVALID_HANDLE_VALUE
;
93 // Attempt to login using blank password.
94 DWORD logon_result
= LogonUser(username
,
97 LOGON32_LOGON_NETWORK
,
98 LOGON32_PROVIDER_DEFAULT
,
101 // Win XP and later return ERROR_ACCOUNT_RESTRICTION for blank password.
105 // In the case the password is blank, then LogonUser returns a failure,
106 // handle is INVALID_HANDLE_VALUE, and GetLastError() is
107 // ERROR_ACCOUNT_RESTRICTION.
108 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms681385
109 blank_password
= (logon_result
||
110 GetLastError() == ERROR_ACCOUNT_RESTRICTION
);
113 // Account for clock skew between pulling the password age and
114 // writing to the preferences by adding a small skew factor here.
115 last_changed
+= base::Time::kMicrosecondsPerSecond
;
117 // Save the blank password status for later.
118 local_state
->SetBoolean(prefs::kOsPasswordBlank
, blank_password
);
119 local_state
->SetInt64(prefs::kOsPasswordLastChanged
, last_changed
);
121 return blank_password
;
124 OsPasswordStatus
GetOsPasswordStatus() {
125 DWORD username_length
= CREDUI_MAX_USERNAME_LENGTH
;
126 WCHAR username
[CREDUI_MAX_USERNAME_LENGTH
+1] = {};
127 OsPasswordStatus retVal
= PASSWORD_STATUS_UNKNOWN
;
129 if (GetUserNameEx(NameUserPrincipal
, username
, &username_length
)) {
130 // If we are on a domain, it is almost certain that the password is not
131 // blank, but we do not actively check any further than this to avoid any
132 // failed login attempts hitting the domain controller.
133 retVal
= PASSWORD_STATUS_WIN_DOMAIN
;
135 username_length
= CREDUI_MAX_USERNAME_LENGTH
;
136 if (GetUserName(username
, &username_length
)) {
137 retVal
= CheckBlankPassword(username
) ? PASSWORD_STATUS_BLANK
:
138 PASSWORD_STATUS_NONBLANK
;
145 bool AuthenticateUser(gfx::NativeWindow window
) {
147 CREDUI_INFO cui
= {};
148 WCHAR username
[CREDUI_MAX_USERNAME_LENGTH
+1] = {};
149 WCHAR displayname
[CREDUI_MAX_USERNAME_LENGTH
+1] = {};
150 WCHAR password
[CREDUI_MAX_PASSWORD_LENGTH
+1] = {};
151 DWORD username_length
= CREDUI_MAX_USERNAME_LENGTH
;
152 std::wstring product_name
=
153 base::UTF16ToWide(l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
));
154 std::wstring password_prompt
=
155 base::UTF16ToWide(l10n_util::GetStringUTF16(
156 IDS_PASSWORDS_PAGE_AUTHENTICATION_PROMPT
));
157 HANDLE handle
= INVALID_HANDLE_VALUE
;
159 bool use_displayname
= false;
160 bool use_principalname
= false;
161 DWORD logon_result
= 0;
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
->GetDispatcher()->host()->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