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