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 #ifndef CHROME_CREDENTIAL_PROVIDER_GAIACP_GCP_UTILS_H_
6 #define CHROME_CREDENTIAL_PROVIDER_GAIACP_GCP_UTILS_H_
7 
8 #include <windows.h>
9 #include <memory>
10 #include <string>
11 
12 #include "base/callback.h"
13 #include "base/files/file_path.h"
14 #include "base/strings/string16.h"
15 #include "base/values.h"
16 #include "base/version.h"
17 #include "base/win/scoped_handle.h"
18 #include "base/win/windows_types.h"
19 #include "chrome/credential_provider/gaiacp/scoped_lsa_policy.h"
20 #include "chrome/credential_provider/gaiacp/win_http_url_fetcher.h"
21 #include "url/gurl.h"
22 
23 // These define are documented in
24 // https://msdn.microsoft.com/en-us/library/bb470234(v=vs.85).aspx not available
25 // in the user mode headers.
26 #define DIRECTORY_QUERY 0x00000001
27 #define DIRECTORY_TRAVERSE 0x00000002
28 #define DIRECTORY_CREATE_OBJECT 0x00000004
29 #define DIRECTORY_CREATE_SUBDIRECTORY 0x00000008
30 #define DIRECTORY_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0xF)
31 
32 namespace base {
33 
34 class CommandLine;
35 class FilePath;
36 
37 }  // namespace base
38 
39 namespace credential_provider {
40 
41 // Windows supports a maximum of 20 characters plus null in username.
42 constexpr int kWindowsUsernameBufferLength = 21;
43 
44 // Maximum domain length is 256 characters including null.
45 // https://support.microsoft.com/en-ca/help/909264/naming-conventions-in-active-directory-for-computers-domains-sites-and
46 constexpr int kWindowsDomainBufferLength = 256;
47 
48 // According to:
49 // https://stackoverflow.com/questions/1140528/what-is-the-maximum-length-of-a-sid-in-sddl-format
50 constexpr int kWindowsSidBufferLength = 184;
51 
52 // Max number of attempts to find a new username when a user already exists
53 // with the same username.
54 constexpr int kMaxUsernameAttempts = 10;
55 
56 // First index to append to a username when another user with the same name
57 // already exists.
58 constexpr int kInitialDuplicateUsernameIndex = 2;
59 
60 // Default extension used as a fallback if the picture_url returned from gaia
61 // does not have a file extension.
62 extern const wchar_t kDefaultProfilePictureFileExtension[];
63 
64 // Name of the sub-folder under which all files for GCPW are stored.
65 extern const base::FilePath::CharType kCredentialProviderFolder[];
66 
67 // Default URL for the GEM MDM API.
68 extern const wchar_t kDefaultMdmUrl[];
69 
70 // Maximum number of consecutive Upload device details failures for which we do
71 // enforce auth.
72 extern const int kMaxNumConsecutiveUploadDeviceFailures;
73 
74 // Maximum allowed time delta after which user policies should be refreshed
75 // again.
76 extern const base::TimeDelta kMaxTimeDeltaSinceLastUserPolicyRefresh;
77 
78 // Because of some strange dependency problems with windows header files,
79 // define STATUS_SUCCESS here instead of including ntstatus.h or SubAuth.h
80 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
81 
82 // A bitfield indicating which standard handles are to be created.
83 using StdHandlesToCreate = uint32_t;
84 
85 enum : uint32_t {
86   kStdOutput = 1 << 0,
87   kStdInput = 1 << 1,
88   kStdError = 1 << 2,
89   kAllStdHandles = kStdOutput | kStdInput | kStdError
90 };
91 
92 // Filled in by InitializeStdHandles to return the parent side of stdin/stdout/
93 // stderr pipes of the login UI process.
94 struct StdParentHandles {
95   StdParentHandles();
96   ~StdParentHandles();
97 
98   base::win::ScopedHandle hstdin_write;
99   base::win::ScopedHandle hstdout_read;
100   base::win::ScopedHandle hstderr_read;
101 };
102 
103 // Class used in tests to set registration data for testing.
104 class GoogleRegistrationDataForTesting {
105  public:
106   explicit GoogleRegistrationDataForTesting(base::string16 serial_number);
107   ~GoogleRegistrationDataForTesting();
108 };
109 
110 // Class used in tests to set gem device details for testing.
111 class GemDeviceDetailsForTesting {
112  public:
113   explicit GemDeviceDetailsForTesting(std::vector<std::string>& mac_addresses,
114                                       std::string os_version);
115   ~GemDeviceDetailsForTesting();
116 };
117 
118 // Class used in tests to set chrome path for testing.
119 class GoogleChromePathForTesting {
120  public:
121   explicit GoogleChromePathForTesting(base::FilePath chrome_path);
122   ~GoogleChromePathForTesting();
123 };
124 
125 // Process startup options that allows customization of stdin/stdout/stderr
126 // handles.
127 class ScopedStartupInfo {
128  public:
129   ScopedStartupInfo();
130   explicit ScopedStartupInfo(const wchar_t* desktop);
131   ~ScopedStartupInfo();
132 
133   // This function takes ownership of the handles.
134   HRESULT SetStdHandles(base::win::ScopedHandle* hstdin,
135                         base::win::ScopedHandle* hstdout,
136                         base::win::ScopedHandle* hstderr);
137 
GetInfo()138   LPSTARTUPINFOW GetInfo() { return &info_; }
139 
140   // Releases all resources held by this info.
141   void Shutdown();
142 
143  private:
144   STARTUPINFOW info_;
145   base::string16 desktop_;
146 };
147 
148 // Gets the brand specific path in which to install GCPW.
149 base::FilePath::StringType GetInstallParentDirectoryName();
150 
151 // Gets the directory where the GCP is installed
152 base::FilePath GetInstallDirectory();
153 
154 // Deletes versions of GCP found under |gcp_path| except for version
155 // |product_version|.
156 void DeleteVersionsExcept(const base::FilePath& gcp_path,
157                           const base::string16& product_version);
158 
159 // Waits for the process specified by |procinfo| to terminate.  The handles
160 // in |read_handles| can be used to read stdout/err from the process.  Upon
161 // return, |exit_code| contains one of the UIEC_xxx constants listed above,
162 // and |stdout_buffer| and |stderr_buffer| contain the output, if any.
163 // Both buffers must be at least |buffer_size| characters long.
164 HRESULT WaitForProcess(base::win::ScopedHandle::Handle process_handle,
165                        const StdParentHandles& parent_handles,
166                        DWORD* exit_code,
167                        char* output_buffer,
168                        int buffer_size);
169 
170 // Creates a restricted, batch or interactive login token for the given user.
171 HRESULT CreateLogonToken(const wchar_t* domain,
172                          const wchar_t* username,
173                          const wchar_t* password,
174                          bool interactive,
175                          base::win::ScopedHandle* token);
176 
177 HRESULT CreateJobForSignin(base::win::ScopedHandle* job);
178 
179 // Creates a pipe that can be used by a parent process to communicate with a
180 // child process.  If |child_reads| is false, then it is expected that the
181 // parent process will read from |reading| anything the child process writes
182 // to |writing|.  For example, this is used to read stdout/stderr of child.
183 //
184 // If |child_reads| is true, then it is expected that the child process will
185 // read from |reading| anything the parent process writes to |writing|.  For
186 // example, this is used to write to stdin of child.
187 //
188 // If |use_nul| is true, then the parent's handle is not used (can be passed
189 // as nullptr).  The child reads from or writes to the null device.
190 HRESULT CreatePipeForChildProcess(bool child_reads,
191                                   bool use_nul,
192                                   base::win::ScopedHandle* reading,
193                                   base::win::ScopedHandle* writing);
194 
195 // Initializes 3 pipes for communicating with a child process.  On return,
196 // |startupinfo| will be set with the handles needed by the child.  This is
197 // used when creating the child process.  |parent_handles| contains the
198 // corresponding handles to be used by the parent process.
199 //
200 // Communication direction is used to optimize handle creation.  If
201 // communication occurs in only one direction then some pipes will be directed
202 // to the nul device.
203 enum class CommDirection {
204   kParentToChildOnly,
205   kChildToParentOnly,
206   kBidirectional,
207 };
208 HRESULT InitializeStdHandles(CommDirection direction,
209                              StdHandlesToCreate to_create,
210                              ScopedStartupInfo* startupinfo,
211                              StdParentHandles* parent_handles);
212 
213 // Fills |path_to_dll| with the short path to the dll referenced by
214 // |dll_handle|. The short path is needed to correctly call rundll32.exe in
215 // cases where there might be quotes or spaces in the path.
216 HRESULT GetPathToDllFromHandle(HINSTANCE dll_handle,
217                                base::FilePath* path_to_dll);
218 
219 // This function gets a correctly formatted entry point argument to pass to
220 // rundll32.exe for a dll referenced by the handle |dll_handle| and an entry
221 // point function with the name |entrypoint|. |entrypoint_arg| will be filled
222 // with the argument value.
223 HRESULT GetEntryPointArgumentForRunDll(HINSTANCE dll_handle,
224                                        const wchar_t* entrypoint,
225                                        base::string16* entrypoint_arg);
226 
227 // This function is used to build the command line for rundll32 to call an
228 // exported entrypoint from the DLL given by |dll_handle|.
229 // Returns S_FALSE if a command line can successfully be built but if the
230 // path to the "dll" actually points to a non ".dll" file. This allows
231 // detection of calls to this function via a unit test which will be
232 // running under an ".exe" module.
233 HRESULT GetCommandLineForEntrypoint(HINSTANCE dll_handle,
234                                     const wchar_t* entrypoint,
235                                     base::CommandLine* command_line);
236 
237 // Looks up the name associated to the |sid| (if any). Returns an error on any
238 // failure or no name is associated with the |sid|.
239 HRESULT LookupLocalizedNameBySid(PSID sid, base::string16* localized_name);
240 
241 // Gets localalized name for builtin administrator account.
242 HRESULT GetLocalizedNameBuiltinAdministratorAccount(
243     base::string16* builtin_localized_admin_name);
244 
245 // Looks up the name associated to the well known |sid_type| (if any). Returns
246 // an error on any failure or no name is associated with the |sid_type|.
247 HRESULT LookupLocalizedNameForWellKnownSid(WELL_KNOWN_SID_TYPE sid_type,
248                                            base::string16* localized_name);
249 
250 // Handles the writing and deletion of a startup sentinel file used to ensure
251 // that the GCPW does not crash continuously on startup and render the
252 // winlogon process unusable.
253 bool WriteToStartupSentinel();
254 void DeleteStartupSentinel();
255 void DeleteStartupSentinelForVersion(const base::string16& version);
256 
257 // Gets a string resource from the DLL with the given id.
258 base::string16 GetStringResource(int base_message_id);
259 
260 // Gets a string resource from the DLL with the given id after replacing the
261 // placeholders with the provided substitutions.
262 base::string16 GetStringResource(int base_message_id,
263                                  const std::vector<base::string16>& subst);
264 
265 // Gets the language selected by the base::win::i18n::LanguageSelector.
266 base::string16 GetSelectedLanguage();
267 
268 // Securely clear a base::Value that may be a dictionary value that may
269 // have a password field.
270 void SecurelyClearDictionaryValue(base::Optional<base::Value>* value);
271 void SecurelyClearDictionaryValueWithKey(base::Optional<base::Value>* value,
272                                          const std::string& password_key);
273 
274 // Securely clear base:string16 and std::string.
275 void SecurelyClearString(base::string16& str);
276 void SecurelyClearString(std::string& str);
277 
278 // Securely clear a given |buffer| with size |length|.
279 void SecurelyClearBuffer(void* buffer, size_t length);
280 
281 // Helpers to get strings from base::Values that are expected to be
282 // DictionaryValues.
283 
284 base::string16 GetDictString(const base::Value& dict, const char* name);
285 base::string16 GetDictString(const std::unique_ptr<base::Value>& dict,
286                              const char* name);
287 // Perform a recursive search on a nested dictionary object. Note that the
288 // names provided in the input should be in order. Below is an example : Lets
289 // say the json object is {"key1": {"key2": {"key3": "value1"}}, "key4":
290 // "value2"}. Then to search for the key "key3", this method should be called
291 // by providing the |path| as {"key1", "key2", "key3"}.
292 std::string SearchForKeyInStringDictUTF8(
293     const std::string& json_string,
294     const std::initializer_list<base::StringPiece>& path);
295 
296 // Perform a recursive search on a nested dictionary object. Note that the
297 // names provided in the input should be in order. Below is an example : Lets
298 // say the json object is
299 // {"key1": {"key2": {"value": "value1", "value": "value2"}}}.
300 // Then to search for the key "key2" and list_key as "value", then this method
301 // should be called by providing |list_key| as "value", |path| as
302 // ["key1", "key2"] and the result returned would be ["value1", "value2"].
303 HRESULT SearchForListInStringDictUTF8(
304     const std::string& list_key,
305     const std::string& json_string,
306     const std::initializer_list<base::StringPiece>& path,
307     std::vector<std::string>* output);
308 std::string GetDictStringUTF8(const base::Value& dict, const char* name);
309 std::string GetDictStringUTF8(const std::unique_ptr<base::Value>& dict,
310                               const char* name);
311 
312 // Returns the major build version of Windows by reading the registry.
313 // See:
314 // https://stackoverflow.com/questions/31072543/reliable-way-to-get-windows-version-from-registry
315 base::string16 GetWindowsVersion();
316 
317 // Returns the minimum supported version of Chrome for GCPW.
318 base::Version GetMinimumSupportedChromeVersion();
319 
320 class OSUserManager;
321 class OSProcessManager;
322 
323 // This structure is used in tests to set fake objects in the credential
324 // provider dll.  See the function SetFakesForTesting() for details.
325 struct FakesForTesting {
326   FakesForTesting();
327   ~FakesForTesting();
328 
329   ScopedLsaPolicy::CreatorCallback scoped_lsa_policy_creator;
330   OSUserManager* os_user_manager_for_testing = nullptr;
331   OSProcessManager* os_process_manager_for_testing = nullptr;
332   WinHttpUrlFetcher::CreatorCallback fake_win_http_url_fetcher_creator;
333 };
334 
335 // DLL entrypoint signature for settings testing fakes.  This is used by
336 // the setup tests to install fakes into the dynamically loaded gaia1_0 DLL
337 // static data.  This way the production DLL does not need to include binary
338 // code used only for testing.
339 typedef void CALLBACK (*SetFakesForTestingFn)(const FakesForTesting* fakes);
340 
341 // Initializes the members of a Windows STRING struct (UNICODE_STRING or
342 // LSA_STRING) to point to the string pointed to by |string|.
343 template <class WindowsStringT,
344           class WindowsStringCharT = decltype(WindowsStringT().Buffer[0])>
InitWindowsStringWithString(const WindowsStringCharT * string,WindowsStringT * windows_string)345 void InitWindowsStringWithString(const WindowsStringCharT* string,
346                                  WindowsStringT* windows_string) {
347   constexpr size_t buffer_char_size = sizeof(WindowsStringCharT);
348   windows_string->Buffer = const_cast<WindowsStringCharT*>(string);
349   windows_string->Length = static_cast<USHORT>(
350       std::char_traits<WindowsStringCharT>::length((windows_string->Buffer)) *
351       buffer_char_size);
352   windows_string->MaximumLength = windows_string->Length + buffer_char_size;
353 }
354 
355 // Extracts the provided keys from the given dictionary. Returns true if all
356 // keys are found. If any of the key isn't found, returns false.
357 bool ExtractKeysFromDict(
358     const base::Value& dict,
359     const std::vector<std::pair<std::string, std::string*>>& needed_outputs);
360 
361 // Gets the bios serial number of the windows device.
362 base::string16 GetSerialNumber();
363 
364 // Gets the mac addresses of the windows device.
365 std::vector<std::string> GetMacAddresses();
366 
367 // Gets the OS version installed on the device. The format is
368 // "major.minor.build".
369 void GetOsVersion(std::string* version);
370 
371 // Gets the obfuscated device_id that is a combination of multiple device
372 // identifiers.
373 HRESULT GenerateDeviceId(std::string* device_id);
374 
375 // Overrides the gaia_url and gcpw_endpoint_path that is used to load GLS.
376 HRESULT SetGaiaEndpointCommandLineIfNeeded(const wchar_t* override_registry_key,
377                                            const std::string& default_endpoint,
378                                            bool provide_deviceid,
379                                            bool show_tos,
380                                            base::CommandLine* command_line);
381 
382 // Returns the file path to installed chrome.exe.
383 base::FilePath GetChromePath();
384 
385 // Returns the file path to system installed chrome.exe.
386 base::FilePath GetSystemChromePath();
387 
388 // Generates gcpw dm token for the given |sid|. If any of the lsa operations
389 // fail, function returns a result other than S_OK.
390 HRESULT GenerateGCPWDmToken(const base::string16& sid);
391 
392 // Reads the gcpw dm token from lsa store for the given |sid| and writes it back
393 // in |token| output parameter.  If any of the lsa operations fail, function
394 // returns a result other than S_OK.
395 HRESULT GetGCPWDmToken(const base::string16& sid, base::string16* token);
396 
397 // Gets the gcpw service URL.
398 GURL GetGcpwServiceUrl();
399 
400 // Converts the |url| in the form of http://xxxxx.googleapis.com/...
401 // to a form that points to a development URL as specified with |dev|
402 // environment. Final url will be in the form
403 // https://{dev}-xxxxx.sandbox.googleapis.com/...
404 base::string16 GetDevelopmentUrl(const base::string16& url,
405                                  const base::string16& dev);
406 
407 }  // namespace credential_provider
408 
409 #endif  // CHROME_CREDENTIAL_PROVIDER_GAIACP_GCP_UTILS_H_
410