Only grant permissions to new extensions from sync if they have the expected version
[chromium-blink-merge.git] / chrome / browser / password_manager / password_manager_util_win.cc
blob994a174dc4ee42af5f6e6264e00abb378ac69aff
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.
6 #include <windows.h>
7 #include <LM.h>
8 #include <wincred.h>
10 // SECURITY_WIN32 must be defined in order to get
11 // EXTENDED_NAME_FORMAT enumeration.
12 #define SECURITY_WIN32 1
13 #include <security.h>
14 #undef SECURITY_WIN32
16 #include "chrome/browser/password_manager/password_manager_util_win.h"
18 #include "base/bind.h"
19 #include "base/bind_helpers.h"
20 #include "base/memory/scoped_ptr.h"
21 #include "base/metrics/histogram_macros.h"
22 #include "base/prefs/pref_service.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/threading/worker_pool.h"
25 #include "base/time/time.h"
26 #include "base/win/windows_version.h"
27 #include "chrome/browser/browser_process.h"
28 #include "chrome/grit/chromium_strings.h"
29 #include "components/password_manager/core/browser/password_manager.h"
30 #include "components/password_manager/core/common/password_manager_pref_names.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "content/public/browser/render_view_host.h"
33 #include "content/public/browser/render_widget_host_view.h"
34 #include "ui/aura/window.h"
35 #include "ui/aura/window_tree_host.h"
36 #include "ui/base/l10n/l10n_util.h"
38 namespace password_manager_util_win {
39 namespace {
41 enum OsPasswordStatus {
42 PASSWORD_STATUS_UNKNOWN = 0,
43 PASSWORD_STATUS_UNSUPPORTED,
44 PASSWORD_STATUS_BLANK,
45 PASSWORD_STATUS_NONBLANK,
46 PASSWORD_STATUS_WIN_DOMAIN,
47 // NOTE: Add new status types only immediately above this line. Also,
48 // make sure the enum list in tools/histogram/histograms.xml is
49 // updated with any change in here.
50 MAX_PASSWORD_STATUS
53 const unsigned kMaxPasswordRetries = 3;
55 const unsigned kCredUiDefaultFlags =
56 CREDUI_FLAGS_GENERIC_CREDENTIALS |
57 CREDUI_FLAGS_EXCLUDE_CERTIFICATES |
58 CREDUI_FLAGS_KEEP_USERNAME |
59 CREDUI_FLAGS_ALWAYS_SHOW_UI |
60 CREDUI_FLAGS_DO_NOT_PERSIST;
62 struct PasswordCheckPrefs {
63 PasswordCheckPrefs() : pref_last_changed_(0), blank_password_(false) {}
65 void Read(PrefService* local_state);
66 void Write(PrefService* local_state);
68 int64 pref_last_changed_;
69 bool blank_password_;
72 void PasswordCheckPrefs::Read(PrefService* local_state) {
73 blank_password_ =
74 local_state->GetBoolean(password_manager::prefs::kOsPasswordBlank);
75 pref_last_changed_ =
76 local_state->GetInt64(password_manager::prefs::kOsPasswordLastChanged);
79 void PasswordCheckPrefs::Write(PrefService* local_state) {
80 local_state->SetBoolean(password_manager::prefs::kOsPasswordBlank,
81 blank_password_);
82 local_state->SetInt64(password_manager::prefs::kOsPasswordLastChanged,
83 pref_last_changed_);
86 int64 GetPasswordLastChanged(const WCHAR* username) {
87 LPUSER_INFO_1 user_info = NULL;
88 DWORD age = 0;
90 NET_API_STATUS ret = NetUserGetInfo(NULL, username, 1, (LPBYTE*) &user_info);
92 if (ret == NERR_Success) {
93 // Returns seconds since last password change.
94 age = user_info->usri1_password_age;
95 NetApiBufferFree(user_info);
96 } else {
97 return -1;
100 base::Time changed = base::Time::Now() - base::TimeDelta::FromSeconds(age);
102 return changed.ToInternalValue();
105 bool CheckBlankPasswordWithPrefs(const WCHAR* username,
106 PasswordCheckPrefs* prefs) {
107 int64 last_changed = GetPasswordLastChanged(username);
109 // If we cannot determine when the password was last changed
110 // then assume the password is not blank
111 if (last_changed == -1)
112 return false;
114 bool blank_password = prefs->blank_password_;
115 bool need_recheck = true;
116 if (prefs->pref_last_changed_ > 0 &&
117 last_changed <= prefs->pref_last_changed_) {
118 need_recheck = false;
121 if (need_recheck) {
122 HANDLE handle = INVALID_HANDLE_VALUE;
124 // Attempt to login using blank password.
125 DWORD logon_result = LogonUser(username,
126 L".",
127 L"",
128 LOGON32_LOGON_NETWORK,
129 LOGON32_PROVIDER_DEFAULT,
130 &handle);
132 // Win XP and later return ERROR_ACCOUNT_RESTRICTION for blank password.
133 if (logon_result)
134 CloseHandle(handle);
136 // In the case the password is blank, then LogonUser returns a failure,
137 // handle is INVALID_HANDLE_VALUE, and GetLastError() is
138 // ERROR_ACCOUNT_RESTRICTION.
139 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms681385
140 blank_password = (logon_result ||
141 GetLastError() == ERROR_ACCOUNT_RESTRICTION);
144 // Account for clock skew between pulling the password age and
145 // writing to the preferences by adding a small skew factor here.
146 last_changed += base::Time::kMicrosecondsPerSecond;
148 // Update the preferences with new values.
149 prefs->pref_last_changed_ = last_changed;
150 prefs->blank_password_ = blank_password;
151 return blank_password;
154 // Wrapper around CheckBlankPasswordWithPrefs to be called on UI thread.
155 bool CheckBlankPassword(const WCHAR* username) {
156 PrefService* local_state = g_browser_process->local_state();
157 PasswordCheckPrefs prefs;
158 prefs.Read(local_state);
159 bool result = CheckBlankPasswordWithPrefs(username, &prefs);
160 prefs.Write(local_state);
161 return result;
164 void GetOsPasswordStatusInternal(PasswordCheckPrefs* prefs,
165 OsPasswordStatus* status) {
166 DWORD username_length = CREDUI_MAX_USERNAME_LENGTH;
167 WCHAR username[CREDUI_MAX_USERNAME_LENGTH+1] = {};
168 *status = PASSWORD_STATUS_UNKNOWN;
170 if (GetUserNameEx(NameUserPrincipal, username, &username_length)) {
171 // If we are on a domain, it is almost certain that the password is not
172 // blank, but we do not actively check any further than this to avoid any
173 // failed login attempts hitting the domain controller.
174 *status = PASSWORD_STATUS_WIN_DOMAIN;
175 } else {
176 username_length = CREDUI_MAX_USERNAME_LENGTH;
177 // CheckBlankPasswordWithPrefs() isn't safe to call on before Windows 7.
178 // http://crbug.com/345916
179 if (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
180 GetUserName(username, &username_length)) {
181 *status = CheckBlankPasswordWithPrefs(username, prefs) ?
182 PASSWORD_STATUS_BLANK :
183 PASSWORD_STATUS_NONBLANK;
188 void ReplyOsPasswordStatus(scoped_ptr<PasswordCheckPrefs> prefs,
189 scoped_ptr<OsPasswordStatus> status) {
190 PrefService* local_state = g_browser_process->local_state();
191 prefs->Write(local_state);
192 UMA_HISTOGRAM_ENUMERATION("PasswordManager.OsPasswordStatus", *status,
193 MAX_PASSWORD_STATUS);
196 void GetOsPasswordStatus() {
197 // Preferences can be accessed on the UI thread only.
198 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
199 PrefService* local_state = g_browser_process->local_state();
200 scoped_ptr<PasswordCheckPrefs> prefs(new PasswordCheckPrefs);
201 prefs->Read(local_state);
202 scoped_ptr<OsPasswordStatus> status(
203 new OsPasswordStatus(PASSWORD_STATUS_UNKNOWN));
204 bool posted = base::WorkerPool::PostTaskAndReply(
205 FROM_HERE,
206 base::Bind(&GetOsPasswordStatusInternal, prefs.get(), status.get()),
207 base::Bind(&ReplyOsPasswordStatus, base::Passed(&prefs),
208 base::Passed(&status)),
209 true);
210 if (!posted) {
211 UMA_HISTOGRAM_ENUMERATION("PasswordManager.OsPasswordStatus",
212 PASSWORD_STATUS_UNKNOWN, MAX_PASSWORD_STATUS);
216 } // namespace
218 void DelayReportOsPassword() {
219 content::BrowserThread::PostDelayedTask(content::BrowserThread::UI, FROM_HERE,
220 base::Bind(&GetOsPasswordStatus),
221 base::TimeDelta::FromSeconds(40));
224 bool AuthenticateUser(gfx::NativeWindow window) {
225 bool retval = false;
226 CREDUI_INFO cui = {};
227 WCHAR username[CREDUI_MAX_USERNAME_LENGTH+1] = {};
228 WCHAR displayname[CREDUI_MAX_USERNAME_LENGTH+1] = {};
229 WCHAR password[CREDUI_MAX_PASSWORD_LENGTH+1] = {};
230 DWORD username_length = CREDUI_MAX_USERNAME_LENGTH;
231 base::string16 product_name = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
232 base::string16 password_prompt =
233 l10n_util::GetStringUTF16(IDS_PASSWORDS_PAGE_AUTHENTICATION_PROMPT);
234 HANDLE handle = INVALID_HANDLE_VALUE;
235 int tries = 0;
236 bool use_displayname = false;
237 bool use_principalname = false;
238 DWORD logon_result = 0;
240 // Disable password manager reauthentication before Windows 7.
241 // This is because of an interaction between LogonUser() and the sandbox.
242 // http://crbug.com/345916
243 if (base::win::GetVersion() < base::win::VERSION_WIN7)
244 return true;
246 // On a domain, we obtain the User Principal Name
247 // for domain authentication.
248 if (GetUserNameEx(NameUserPrincipal, username, &username_length)) {
249 use_principalname = true;
250 } else {
251 username_length = CREDUI_MAX_USERNAME_LENGTH;
252 // Otherwise, we're a workstation, use the plain local username.
253 if (!GetUserName(username, &username_length)) {
254 DLOG(ERROR) << "Unable to obtain username " << GetLastError();
255 return false;
256 } else {
257 // As we are on a workstation, it's possible the user
258 // has no password, so check here.
259 if (CheckBlankPassword(username))
260 return true;
264 // Try and obtain a friendly display name.
265 username_length = CREDUI_MAX_USERNAME_LENGTH;
266 if (GetUserNameEx(NameDisplay, displayname, &username_length))
267 use_displayname = true;
269 cui.cbSize = sizeof(CREDUI_INFO);
270 cui.hwndParent = NULL;
271 cui.hwndParent = window->GetHost()->GetAcceleratedWidget();
273 cui.pszMessageText = password_prompt.c_str();
274 cui.pszCaptionText = product_name.c_str();
276 cui.hbmBanner = NULL;
277 BOOL save_password = FALSE;
278 DWORD credErr = NO_ERROR;
280 do {
281 tries++;
283 // TODO(wfh) Make sure we support smart cards here.
284 credErr = CredUIPromptForCredentials(
285 &cui,
286 product_name.c_str(),
287 NULL,
289 use_displayname ? displayname : username,
290 CREDUI_MAX_USERNAME_LENGTH+1,
291 password,
292 CREDUI_MAX_PASSWORD_LENGTH+1,
293 &save_password,
294 kCredUiDefaultFlags |
295 (tries > 1 ? CREDUI_FLAGS_INCORRECT_PASSWORD : 0));
297 if (credErr == NO_ERROR) {
298 logon_result = LogonUser(username,
299 use_principalname ? NULL : L".",
300 password,
301 LOGON32_LOGON_NETWORK,
302 LOGON32_PROVIDER_DEFAULT,
303 &handle);
304 if (logon_result) {
305 retval = true;
306 CloseHandle(handle);
307 } else {
308 if (GetLastError() == ERROR_ACCOUNT_RESTRICTION &&
309 wcslen(password) == 0) {
310 // Password is blank, so permit.
311 retval = true;
312 } else {
313 DLOG(WARNING) << "Unable to authenticate " << GetLastError();
316 SecureZeroMemory(password, sizeof(password));
318 } while (credErr == NO_ERROR &&
319 (retval == false && tries < kMaxPasswordRetries));
320 return retval;
323 } // namespace password_manager_util_win