1 // Copyright 2018 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 #include "chrome/credential_provider/gaiacp/os_user_manager.h"
6
7 #include <windows.h>
8
9 #include <lm.h>
10
11 #include <sddl.h> // For ConvertSidToStringSid()
12 #include <userenv.h> // For GetUserProfileDirectory()
13 #include <wincrypt.h> // For CryptXXX()
14
15 #include <atlconv.h>
16
17 #include <malloc.h>
18 #include <memory.h>
19 #include <stdlib.h>
20
21 #include <iomanip>
22 #include <memory>
23
24 #include "base/files/file_path.h"
25 #include "base/files/file_util.h"
26 #include "base/macros.h"
27 #include "base/scoped_native_library.h"
28 #include "base/stl_util.h"
29 #include "base/strings/string_util.h"
30 #include "base/strings/stringprintf.h"
31 #include "base/strings/utf_string_conversions.h"
32 #include "base/win/registry.h"
33 #include "base/win/win_util.h"
34 #include "chrome/credential_provider/gaiacp/gcp_utils.h"
35 #include "chrome/credential_provider/gaiacp/logging.h"
36
37 namespace credential_provider {
38
39 namespace {
40
GetDomainControllerServerForDomain(const wchar_t * domain,LPBYTE * server)41 HRESULT GetDomainControllerServerForDomain(const wchar_t* domain,
42 LPBYTE* server) {
43 DCHECK(domain);
44 base::string16 local_domain = OSUserManager::GetLocalDomain();
45 // If the domain is the local domain, then there is no domain controller.
46 if (wcsicmp(local_domain.c_str(), domain) == 0) {
47 return S_OK;
48 }
49
50 NET_API_STATUS nsts = ::NetGetDCName(nullptr, domain, server);
51 if (nsts != NERR_Success) {
52 LOGFN(ERROR) << "NetGetDCName nsts=" << nsts;
53 }
54 return HRESULT_FROM_WIN32(nsts);
55 }
56
57 } // namespace
58
59 // static
GetInstanceStorage()60 OSUserManager** OSUserManager::GetInstanceStorage() {
61 static OSUserManager* instance = new OSUserManager();
62 return &instance;
63 }
64
65 // static
Get()66 OSUserManager* OSUserManager::Get() {
67 return *GetInstanceStorage();
68 }
69
70 // static
SetInstanceForTesting(OSUserManager * instance)71 void OSUserManager::SetInstanceForTesting(OSUserManager* instance) {
72 *GetInstanceStorage() = instance;
73 }
74
75 // static
IsDeviceDomainJoined()76 bool OSUserManager::IsDeviceDomainJoined() {
77 return base::win::IsEnrolledToDomain();
78 }
79
80 // static
GetLocalDomain()81 base::string16 OSUserManager::GetLocalDomain() {
82 // If the domain is the current computer, then there is no domain controller.
83 wchar_t computer_name[MAX_COMPUTERNAME_LENGTH + 1];
84 DWORD length = base::size(computer_name);
85 if (!::GetComputerNameW(computer_name, &length))
86 return base::string16();
87
88 return base::string16(computer_name, length);
89 }
90
~OSUserManager()91 OSUserManager::~OSUserManager() {}
92
93 #define IS_PASSWORD_STRONG_ENOUGH() \
94 (cur_length > kMinPasswordLength) && \
95 (has_upper + has_lower + has_digit + has_punct > 3)
96
GenerateRandomPassword(wchar_t * password,int length)97 HRESULT OSUserManager::GenerateRandomPassword(wchar_t* password, int length) {
98 HRESULT hr;
99 HCRYPTPROV prov;
100
101 // TODO(rogerta): read password policy GPOs to see what the password policy
102 // is for this machine in order to create one that adheres correctly. For
103 // now will generate a random password that fits typical strong password
104 // policies on windows.
105 const wchar_t kValidPasswordChars[] =
106 L"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
107 L"abcdefghijklmnopqrstuvwxyz"
108 L"`1234567890-="
109 L"~!@#$%^&*()_+"
110 L"[]\\;',./"
111 L"{}|:\"<>?";
112
113 if (length < kMinPasswordLength)
114 return E_INVALIDARG;
115
116 if (!::CryptAcquireContext(&prov, nullptr, nullptr, PROV_RSA_FULL,
117 CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
118 hr = HRESULT_FROM_WIN32(::GetLastError());
119 LOGFN(ERROR) << "CryptAcquireContext hr=" << putHR(hr);
120 return hr;
121 }
122
123 int cur_length;
124 int has_upper;
125 int has_lower;
126 int has_digit;
127 int has_punct;
128 do {
129 cur_length = 0;
130 has_upper = 0;
131 has_lower = 0;
132 has_digit = 0;
133 has_punct = 0;
134
135 wchar_t* p = password;
136 int remaining_length = length;
137
138 while (remaining_length > 1) {
139 BYTE r;
140 if (!::CryptGenRandom(prov, sizeof(r), &r)) {
141 hr = HRESULT_FROM_WIN32(::GetLastError());
142 LOGFN(ERROR) << "CryptGenRandom hr=" << putHR(hr);
143 ::CryptReleaseContext(prov, 0);
144 return hr;
145 }
146
147 wchar_t c =
148 kValidPasswordChars[r % (base::size(kValidPasswordChars) - 1)];
149 *p++ = c;
150 ++cur_length;
151 --remaining_length;
152
153 // Check if we have all the requirements for a strong password.
154 if (isupper(c))
155 has_upper = 1;
156 if (islower(c))
157 has_lower = 1;
158 if (isdigit(c))
159 has_digit = 1;
160 if (ispunct(c))
161 has_punct = 1;
162
163 if (IS_PASSWORD_STRONG_ENOUGH())
164 break;
165 }
166
167 // Make sure password is terminated.
168 *p = 0;
169
170 // Because this is a random function, there is a chance that the password
171 // might not be strong enough by the time the code reaches this point. This
172 // could happen if two categories of characters were never included.
173 //
174 // This highest probability of this happening would be if the caller
175 // specified the shortest password allowed and the missing characters came
176 // from the smallest two sets (say digits and lower case letters).
177 //
178 // This probability is equal to 1 minus the probability that all characters
179 // are upper case or punctuation:
180 //
181 // P(!strong) = 1 - P(all chars either upper or punctuation)
182 //
183 // There are valid 96 characters in all, of which 56 are not digits or
184 // lower case. The probability that any single character is upper case or
185 // punctuation is 56/96. If the minimum length is used, then:
186 //
187 // P(all chars either upper or punctuation) = (56/96)^23 = 4.1e-6
188 //
189 // P(!strong) = 1 - 1.8e-4 = 0.999996
190 //
191 // or about 4 in every million. The exponent is 23 and not 24 because the
192 // minimum password length includes the null terminiator.
193 //
194 // This means:
195 // - ~4 in every million will run this loop at least twice
196 // - ~4 in every 250 billion will run this loop at least thrice
197 // - ~4 in every 6.25e13 will run this loop at least four times
198 } while (!IS_PASSWORD_STRONG_ENOUGH());
199
200 ::CryptReleaseContext(prov, 0);
201
202 return S_OK;
203 }
204
GetUserFullname(const wchar_t * domain,const wchar_t * username,base::string16 * fullname)205 HRESULT OSUserManager::GetUserFullname(const wchar_t* domain,
206 const wchar_t* username,
207 base::string16* fullname) {
208 DCHECK(fullname);
209 LPBYTE domain_server_buffer = nullptr;
210 HRESULT hr =
211 GetDomainControllerServerForDomain(domain, &domain_server_buffer);
212 if (FAILED(hr))
213 return hr;
214
215 std::unique_ptr<wchar_t, void (*)(wchar_t*)> domain_to_query(
216 reinterpret_cast<wchar_t*>(domain_server_buffer), [](wchar_t* p) {
217 if (p)
218 ::NetApiBufferFree(p);
219 });
220
221 LPBYTE buffer = nullptr;
222 NET_API_STATUS nsts =
223 ::NetUserGetInfo(domain_to_query.get(), username, 11, &buffer);
224 if (nsts != NERR_Success) {
225 LOGFN(ERROR) << "NetUserGetInfo(get full name) nsts=" << nsts;
226 return HRESULT_FROM_WIN32(nsts);
227 }
228
229 USER_INFO_11* user_info = reinterpret_cast<USER_INFO_11*>(buffer);
230 *fullname = user_info->usri11_full_name;
231 return S_OK;
232 }
233
AddUser(const wchar_t * username,const wchar_t * password,const wchar_t * fullname,const wchar_t * comment,bool add_to_users_group,BSTR * sid,DWORD * error)234 HRESULT OSUserManager::AddUser(const wchar_t* username,
235 const wchar_t* password,
236 const wchar_t* fullname,
237 const wchar_t* comment,
238 bool add_to_users_group,
239 BSTR* sid,
240 DWORD* error) {
241 DCHECK(sid);
242
243 base::string16 local_users_group_name;
244 // If adding to the local users group, make sure we can get the localized
245 // name for the group before proceeding.
246 if (add_to_users_group) {
247 HRESULT hr = LookupLocalizedNameForWellKnownSid(WinBuiltinUsersSid,
248 &local_users_group_name);
249 if (FAILED(hr)) {
250 LOGFN(ERROR) << "LookupLocalizedNameForWellKnownSid hr=" << putHR(hr);
251 return hr;
252 }
253 }
254
255 USER_INFO_1 info;
256 memset(&info, 0, sizeof(info));
257 info.usri1_comment = _wcsdup(comment);
258 info.usri1_flags =
259 UF_PASSWD_CANT_CHANGE | UF_DONT_EXPIRE_PASSWD | UF_NORMAL_ACCOUNT;
260 info.usri1_name = const_cast<wchar_t*>(username);
261 info.usri1_password = const_cast<wchar_t*>(password);
262 info.usri1_priv = USER_PRIV_USER;
263
264 NET_API_STATUS nsts =
265 ::NetUserAdd(nullptr, 1, reinterpret_cast<LPBYTE>(&info), error);
266 free(info.usri1_comment);
267
268 // Set the user's full name.
269 if (nsts == NERR_Success) {
270 USER_INFO_1011 info1011;
271 memset(&info1011, 0, sizeof(info1011));
272 info1011.usri1011_full_name = const_cast<wchar_t*>(fullname);
273 nsts = ::NetUserSetInfo(nullptr, info.usri1_name, 1011,
274 reinterpret_cast<LPBYTE>(&info1011), error);
275 if (nsts != NERR_Success) {
276 LOGFN(ERROR) << "NetUserSetInfo(set full name) nsts=" << nsts;
277 }
278 } else {
279 LOGFN(ERROR) << "NetUserAdd nsts=" << nsts;
280 return HRESULT_FROM_WIN32(nsts);
281 }
282
283 // Get the new user's SID and return it to caller.
284 LPBYTE buffer = nullptr;
285 nsts = ::NetUserGetInfo(nullptr, info.usri1_name, 4, &buffer);
286 if (nsts == NERR_Success) {
287 const USER_INFO_4* user_info = reinterpret_cast<const USER_INFO_4*>(buffer);
288 wchar_t* sidstr = nullptr;
289 if (::ConvertSidToStringSid(user_info->usri4_user_sid, &sidstr)) {
290 *sid = SysAllocString(T2COLE(sidstr));
291 LOGFN(VERBOSE) << "sid=" << sidstr;
292 ::LocalFree(sidstr);
293 } else {
294 LOGFN(ERROR) << "Could not convert SID to string";
295 *sid = nullptr;
296 nsts = NERR_ProgNeedsExtraMem;
297 }
298
299 if (nsts == NERR_Success && add_to_users_group) {
300 // Add to the well known local users group so that it appears on login
301 // screen.
302 LOCALGROUP_MEMBERS_INFO_0 member_info;
303 memset(&member_info, 0, sizeof(member_info));
304 member_info.lgrmi0_sid = user_info->usri4_user_sid;
305 nsts =
306 ::NetLocalGroupAddMembers(nullptr, local_users_group_name.c_str(), 0,
307 reinterpret_cast<LPBYTE>(&member_info), 1);
308 if (nsts != NERR_Success && nsts != ERROR_MEMBER_IN_ALIAS) {
309 LOGFN(ERROR) << "NetLocalGroupAddMembers nsts=" << nsts;
310 } else {
311 nsts = NERR_Success;
312 }
313 }
314
315 ::NetApiBufferFree(buffer);
316 }
317
318 return (nsts == NERR_Success ? S_OK : HRESULT_FROM_WIN32(nsts));
319 }
320
SetDefaultPasswordChangePolicies(const wchar_t * domain,const wchar_t * username)321 HRESULT OSUserManager::SetDefaultPasswordChangePolicies(
322 const wchar_t* domain,
323 const wchar_t* username) {
324 USER_INFO_1008 info1008;
325 DWORD error;
326 memset(&info1008, 0, sizeof(info1008));
327 info1008.usri1008_flags =
328 UF_PASSWD_CANT_CHANGE | UF_DONT_EXPIRE_PASSWD | UF_NORMAL_ACCOUNT;
329 NET_API_STATUS nsts = ::NetUserSetInfo(
330 domain, username, 1008, reinterpret_cast<LPBYTE>(&info1008), &error);
331 if (nsts != NERR_Success) {
332 LOGFN(ERROR) << "NetUserSetInfo(set password policies) nsts=" << nsts;
333 }
334 return HRESULT_FROM_WIN32(nsts);
335 }
336
ChangeUserPassword(const wchar_t * domain,const wchar_t * username,const wchar_t * old_password,const wchar_t * new_password)337 HRESULT OSUserManager::ChangeUserPassword(const wchar_t* domain,
338 const wchar_t* username,
339 const wchar_t* old_password,
340 const wchar_t* new_password) {
341 LPBYTE domain_server_buffer = nullptr;
342 HRESULT hr =
343 GetDomainControllerServerForDomain(domain, &domain_server_buffer);
344 if (FAILED(hr))
345 return hr;
346
347 std::unique_ptr<wchar_t, void (*)(wchar_t*)> domain_to_query(
348 reinterpret_cast<wchar_t*>(domain_server_buffer), [](wchar_t* p) {
349 if (p)
350 ::NetApiBufferFree(p);
351 });
352
353 // Remove the UF_PASSWD_CANT_CHANGE flag temporarily so that the password can
354 // be changed.
355 LPBYTE buffer = nullptr;
356 NET_API_STATUS nsts =
357 ::NetUserGetInfo(domain_to_query.get(), username, 4, &buffer);
358 if (nsts != NERR_Success) {
359 LOGFN(ERROR) << "NetUserGetInfo(get password change flag) nsts=" << nsts;
360 return HRESULT_FROM_WIN32(nsts);
361 }
362
363 USER_INFO_4* user_info = reinterpret_cast<USER_INFO_4*>(buffer);
364 DWORD original_user_flags = user_info->usri4_flags;
365
366 bool flags_changed = false;
367 if ((user_info->usri4_flags & UF_PASSWD_CANT_CHANGE) != 0) {
368 user_info->usri4_flags &= ~UF_PASSWD_CANT_CHANGE;
369 nsts =
370 ::NetUserSetInfo(domain_to_query.get(), username, 4, buffer, nullptr);
371 if (nsts != NERR_Success) {
372 LOGFN(ERROR) << "NetUserSetInfo(allow password change) nsts=" << nsts;
373 ::NetApiBufferFree(buffer);
374 return HRESULT_FROM_WIN32(nsts);
375 }
376
377 flags_changed = true;
378 }
379
380 base::string16 password_domain = base::StringPrintf(L"%ls", domain);
381
382 NET_API_STATUS changepassword_nsts = ::NetUserChangePassword(
383 password_domain.c_str(), username, old_password, new_password);
384 if (changepassword_nsts != NERR_Success) {
385 LOGFN(ERROR) << "Unable to change password for '" << username
386 << "' domain '" << password_domain
387 << "' nsts=" << changepassword_nsts;
388 }
389
390 if (flags_changed) {
391 user_info->usri4_flags = original_user_flags;
392 nsts =
393 ::NetUserSetInfo(domain_to_query.get(), username, 4, buffer, nullptr);
394 if (nsts != NERR_Success) {
395 LOGFN(ERROR) << "NetUserSetInfo(reset password change flag) nsts="
396 << nsts;
397 }
398 }
399
400 ::NetApiBufferFree(buffer);
401
402 return HRESULT_FROM_WIN32(changepassword_nsts);
403 }
404
SetUserPassword(const wchar_t * domain,const wchar_t * username,const wchar_t * password)405 HRESULT OSUserManager::SetUserPassword(const wchar_t* domain,
406 const wchar_t* username,
407 const wchar_t* password) {
408 LPBYTE domain_server_buffer = nullptr;
409 HRESULT hr =
410 GetDomainControllerServerForDomain(domain, &domain_server_buffer);
411 if (FAILED(hr))
412 return hr;
413
414 std::unique_ptr<wchar_t, void (*)(wchar_t*)> domain_to_query(
415 reinterpret_cast<wchar_t*>(domain_server_buffer), [](wchar_t* p) {
416 if (p)
417 ::NetApiBufferFree(p);
418 });
419
420 DWORD error = 0;
421 USER_INFO_1003 info1003;
422 NET_API_STATUS nsts;
423 memset(&info1003, 0, sizeof(info1003));
424 info1003.usri1003_password = const_cast<wchar_t*>(password);
425 nsts = ::NetUserSetInfo(domain_to_query.get(), username, 1003,
426 reinterpret_cast<LPBYTE>(&info1003), &error);
427 if (nsts != NERR_Success) {
428 LOGFN(ERROR) << "Unable to change password for '" << username
429 << "' nsts=" << nsts;
430 }
431
432 return HRESULT_FROM_WIN32(nsts);
433 }
434
SetUserFullname(const wchar_t * domain,const wchar_t * username,const wchar_t * full_name)435 HRESULT OSUserManager::SetUserFullname(const wchar_t* domain,
436 const wchar_t* username,
437 const wchar_t* full_name) {
438 LPBYTE domain_server_buffer = nullptr;
439 HRESULT hr =
440 GetDomainControllerServerForDomain(domain, &domain_server_buffer);
441 if (FAILED(hr))
442 return hr;
443
444 std::unique_ptr<wchar_t, void (*)(wchar_t*)> domain_to_query(
445 reinterpret_cast<wchar_t*>(domain_server_buffer), [](wchar_t* p) {
446 if (p)
447 ::NetApiBufferFree(p);
448 });
449
450 DWORD error = 0;
451 USER_INFO_1011 info1011;
452 NET_API_STATUS nsts;
453 memset(&info1011, 0, sizeof(info1011));
454 info1011.usri1011_full_name = const_cast<wchar_t*>(full_name);
455
456 nsts = ::NetUserSetInfo(domain_to_query.get(), username, 1011,
457 reinterpret_cast<LPBYTE>(&info1011), &error);
458 if (nsts != NERR_Success) {
459 LOGFN(ERROR) << "Unable to change full name on the account for '"
460 << username << "' nsts=" << nsts;
461 }
462
463 return HRESULT_FROM_WIN32(nsts);
464 }
465
IsWindowsPasswordValid(const wchar_t * domain,const wchar_t * username,const wchar_t * password)466 HRESULT OSUserManager::IsWindowsPasswordValid(const wchar_t* domain,
467 const wchar_t* username,
468 const wchar_t* password) {
469 // Check if the user exists before trying to log them on, because an error
470 // of ERROR_LOGON_FAILURE will be returned if the user does not exist
471 // or if the password is invalid. This function only wants to return
472 // S_FALSE on an ERROR_LOGON_FAILURE if the user exists.
473 PSID sid;
474 HRESULT hr = GetUserSID(domain, username, &sid);
475
476 if (SUCCEEDED(hr)) {
477 ::LocalFree(sid);
478 base::win::ScopedHandle handle;
479 hr = CreateLogonToken(domain, username, password, /*interactive=*/true,
480 &handle);
481 if (SUCCEEDED(hr))
482 return hr;
483
484 if (hr == HRESULT_FROM_WIN32(ERROR_LOGON_FAILURE)) {
485 return S_FALSE;
486 // The following error codes represent sign in restrictions for the user
487 // that are returned if the user's password is valid. In these cases we
488 // don't want to return that the password is not valid. This is used to
489 // make sure that we don't think we need to update the user's password
490 // when in fact it is valid but they just can't sign in.
491 } else if (hr == HRESULT_FROM_WIN32(ERROR_ACCOUNT_RESTRICTION) ||
492 hr == HRESULT_FROM_WIN32(ERROR_INVALID_LOGON_HOURS) ||
493 hr == HRESULT_FROM_WIN32(ERROR_INVALID_WORKSTATION) ||
494 hr == HRESULT_FROM_WIN32(ERROR_ACCOUNT_DISABLED) ||
495 hr == HRESULT_FROM_WIN32(ERROR_LOGON_TYPE_NOT_GRANTED)) {
496 return S_OK;
497 }
498 }
499
500 return hr;
501 }
502
CreateLogonToken(const wchar_t * domain,const wchar_t * username,const wchar_t * password,bool interactive,base::win::ScopedHandle * token)503 HRESULT OSUserManager::CreateLogonToken(const wchar_t* domain,
504 const wchar_t* username,
505 const wchar_t* password,
506 bool interactive,
507 base::win::ScopedHandle* token) {
508 return ::credential_provider::CreateLogonToken(domain, username, password,
509 interactive, token);
510 }
511
GetUserSID(const wchar_t * domain,const wchar_t * username,base::string16 * sid_string)512 HRESULT OSUserManager::GetUserSID(const wchar_t* domain,
513 const wchar_t* username,
514 base::string16* sid_string) {
515 DCHECK(sid_string);
516 sid_string->clear();
517
518 PSID sid;
519 HRESULT hr = GetUserSID(domain, username, &sid);
520
521 if (SUCCEEDED(hr)) {
522 wchar_t* sid_buffer;
523 if (::ConvertSidToStringSid(sid, &sid_buffer)) {
524 *sid_string = sid_buffer;
525 ::LocalFree(sid_buffer);
526 } else {
527 hr = HRESULT_FROM_WIN32(::GetLastError());
528 LOGFN(ERROR) << "ConvertStringSidToSid hr=" << putHR(hr);
529 }
530 ::LocalFree(sid);
531 }
532
533 return hr;
534 }
535
GetUserSID(const wchar_t * domain,const wchar_t * username,PSID * sid)536 HRESULT OSUserManager::GetUserSID(const wchar_t* domain,
537 const wchar_t* username,
538 PSID* sid) {
539 DCHECK(username);
540 DCHECK(sid);
541
542 char sid_buffer[256];
543 DWORD sid_length = base::size(sid_buffer);
544 wchar_t user_domain_buffer[kWindowsDomainBufferLength];
545 DWORD domain_length = base::size(user_domain_buffer);
546 SID_NAME_USE use;
547 base::string16 username_with_domain =
548 base::string16(domain) + L"\\" + username;
549
550 if (!::LookupAccountName(nullptr, username_with_domain.c_str(), sid_buffer,
551 &sid_length, user_domain_buffer, &domain_length,
552 &use)) {
553 return HRESULT_FROM_WIN32(::GetLastError());
554 }
555
556 // Check that the domain of the user found with LookupAccountName matches what
557 // is requested.
558 if (wcsicmp(domain, user_domain_buffer) != 0) {
559 LOGFN(ERROR) << "Domain mismatch " << domain << " " << user_domain_buffer;
560
561 return HRESULT_FROM_WIN32(ERROR_NONE_MAPPED);
562 }
563
564 *sid = ::LocalAlloc(LMEM_FIXED, sid_length);
565 ::CopySid(sid_length, *sid, sid_buffer);
566
567 return S_OK;
568 }
569
FindUserBySID(const wchar_t * sid,wchar_t * username,DWORD username_size,wchar_t * domain,DWORD domain_size)570 HRESULT OSUserManager::FindUserBySID(const wchar_t* sid,
571 wchar_t* username,
572 DWORD username_size,
573 wchar_t* domain,
574 DWORD domain_size) {
575 PSID psid;
576 if (!::ConvertStringSidToSidW(sid, &psid)) {
577 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
578 LOGFN(ERROR) << "ConvertStringSidToSidW sid=" << sid << " hr=" << putHR(hr);
579 return hr;
580 }
581
582 HRESULT hr = S_OK;
583 DWORD name_length = username ? username_size : 0;
584 wchar_t local_domain_buffer[kWindowsDomainBufferLength];
585 DWORD domain_length = base::size(local_domain_buffer);
586 SID_NAME_USE use;
587 if (!::LookupAccountSid(nullptr, psid, username, &name_length,
588 local_domain_buffer, &domain_length, &use)) {
589 hr = HRESULT_FROM_WIN32(::GetLastError());
590 if (hr != HRESULT_FROM_WIN32(ERROR_NONE_MAPPED)) {
591 if (username_size == 0 &&
592 hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) {
593 hr = S_OK;
594 }
595 }
596 }
597
598 if (domain_size) {
599 if (domain_size <= domain_length)
600 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
601 wcscpy_s(domain, domain_size, local_domain_buffer);
602 }
603
604 ::LocalFree(psid);
605 return hr;
606 }
607
IsUserDomainJoined(const base::string16 & sid)608 bool OSUserManager::IsUserDomainJoined(const base::string16& sid) {
609 wchar_t username[kWindowsUsernameBufferLength];
610 wchar_t domain[kWindowsDomainBufferLength];
611
612 HRESULT hr = FindUserBySID(sid.c_str(), username, base::size(username),
613 domain, base::size(domain));
614
615 if (FAILED(hr)) {
616 LOGFN(ERROR) << "IsUserDomainJoined sid=" << sid << " hr=" << putHR(hr);
617 return hr;
618 }
619
620 return !base::EqualsCaseInsensitiveASCII(
621 domain, OSUserManager::GetLocalDomain().c_str());
622 }
623
RemoveUser(const wchar_t * username,const wchar_t * password)624 HRESULT OSUserManager::RemoveUser(const wchar_t* username,
625 const wchar_t* password) {
626 DCHECK(username);
627 DCHECK(password);
628
629 // Get the user's profile directory.
630 base::win::ScopedHandle token;
631 wchar_t profiledir[MAX_PATH + 1];
632
633 base::string16 local_domain = OSUserManager::GetLocalDomain();
634
635 // Get the user's profile directory. Try a batch logon first, and if that
636 // fails then try an interactive logon.
637 HRESULT hr = CreateLogonToken(local_domain.c_str(), username, password,
638 /*interactive=*/false, &token);
639 if (FAILED(hr))
640 hr = CreateLogonToken(local_domain.c_str(), username, password,
641 /*interactive=*/true, &token);
642
643 if (SUCCEEDED(hr)) {
644 // Get the gaia user's profile directory so that it can be deleted.
645 DWORD length = base::size(profiledir) - 1;
646 if (!::GetUserProfileDirectory(token.Get(), profiledir, &length)) {
647 hr = HRESULT_FROM_WIN32(::GetLastError());
648 if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
649 LOGFN(ERROR) << "GetUserProfileDirectory hr=" << putHR(hr);
650 profiledir[0] = 0;
651 }
652 } else {
653 LOGFN(ERROR) << "CreateLogonToken hr=" << putHR(hr);
654 }
655
656 // Remove the OS user.
657 NET_API_STATUS nsts = ::NetUserDel(nullptr, username);
658 if (nsts != NERR_Success)
659 LOGFN(ERROR) << "NetUserDel nsts=" << nsts;
660
661 // Force delete the user's profile directory.
662 if (*profiledir && !base::DeletePathRecursively(base::FilePath(profiledir)))
663 LOGFN(ERROR) << "base::DeleteFile";
664
665 return S_OK;
666 }
667
ModifyUserAccessWithLogonHours(const wchar_t * domain,const wchar_t * username,bool allow)668 HRESULT OSUserManager::ModifyUserAccessWithLogonHours(const wchar_t* domain,
669 const wchar_t* username,
670 bool allow) {
671 BYTE buffer[21] = {0x0};
672 memset(buffer, allow ? 0xff : 0x0, sizeof(buffer));
673 USER_INFO_1020 user_info{UNITS_PER_WEEK, buffer};
674
675 NET_API_STATUS nsts = ::NetUserSetInfo(
676 domain, username, 1020, reinterpret_cast<BYTE*>(&user_info), nullptr);
677 if (nsts != NERR_Success) {
678 LOGFN(ERROR) << "NetUserSetInfo(set logon time) nsts=" << nsts;
679 return HRESULT_FROM_WIN32(nsts);
680 }
681
682 return S_OK;
683 }
684
685 } // namespace credential_provider
686