1 // Copyright 2016 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/install_static/policy_path_parser.h"
6 
7 #include <assert.h>
8 #include <shlobj.h>
9 #include <stddef.h>
10 #include <stdlib.h>
11 #include <wtsapi32.h>
12 
13 #include <memory>
14 
15 namespace {
16 
17 constexpr WCHAR kMachineNamePolicyVarName[] = L"${machine_name}";
18 constexpr WCHAR kUserNamePolicyVarName[] = L"${user_name}";
19 constexpr WCHAR kWinDocumentsFolderVarName[] = L"${documents}";
20 constexpr WCHAR kWinLocalAppDataFolderVarName[] = L"${local_app_data}";
21 constexpr WCHAR kWinRoamingAppDataFolderVarName[] = L"${roaming_app_data}";
22 constexpr WCHAR kWinProfileFolderVarName[] = L"${profile}";
23 constexpr WCHAR kWinProgramDataFolderVarName[] = L"${global_app_data}";
24 constexpr WCHAR kWinProgramFilesFolderVarName[] = L"${program_files}";
25 constexpr WCHAR kWinWindowsFolderVarName[] = L"${windows}";
26 constexpr WCHAR kWinClientName[] = L"${client_name}";
27 constexpr WCHAR kWinSessionName[] = L"${session_name}";
28 
29 struct WinFolderNamesToCSIDLMapping {
30   const WCHAR* name;
31   int id;
32 };
33 
34 // Mapping from variable names to Windows CSIDL ids.
35 constexpr WinFolderNamesToCSIDLMapping kWinFolderMapping[] = {
36     {kWinWindowsFolderVarName, CSIDL_WINDOWS},
37     {kWinProgramFilesFolderVarName, CSIDL_PROGRAM_FILES},
38     {kWinProgramDataFolderVarName, CSIDL_COMMON_APPDATA},
39     {kWinProfileFolderVarName, CSIDL_PROFILE},
40     {kWinLocalAppDataFolderVarName, CSIDL_LOCAL_APPDATA},
41     {kWinRoamingAppDataFolderVarName, CSIDL_APPDATA},
42     {kWinDocumentsFolderVarName, CSIDL_PERSONAL}};
43 
44 template <class FunctionType>
45 struct ScopedFunctionHelper {
ScopedFunctionHelper__anonfd0a601b0111::ScopedFunctionHelper46   ScopedFunctionHelper(const wchar_t* library_name, const char* function_name) {
47     library_ = LoadLibrary(library_name);
48     assert(library_);
49     if (library_) {
50       // Strip off any leading :: that may have come from stringifying the
51       // function's name.
52       if (function_name[0] == ':' && function_name[1] == ':' &&
53           function_name[2] && function_name[2] != ':') {
54         function_name += 2;
55       }
56       function_ = reinterpret_cast<FunctionType*>(
57           GetProcAddress(library_, function_name));
58       assert(function_);
59     }
60   }
61 
~ScopedFunctionHelper__anonfd0a601b0111::ScopedFunctionHelper62   ~ScopedFunctionHelper() {
63     if (library_)
64       FreeLibrary(library_);
65   }
66 
67   template <class... Args>
operator ()__anonfd0a601b0111::ScopedFunctionHelper68   auto operator()(Args... a) {
69     return function_(a...);
70   }
71 
72  private:
73   HMODULE library_;
74   FunctionType* function_;
75 };
76 
77 #define SCOPED_LOAD_FUNCTION(library, function) \
78   ScopedFunctionHelper<decltype(function)>(library, #function)
79 
80 }  // namespace
81 
82 namespace install_static {
83 
84 // Replaces all variable occurrences in the policy string with the respective
85 // system settings values.
86 // Note that this uses GetProcAddress to load DLLs that cannot be loaded before
87 // the blacklist in the DllMain of chrome_elf has been applied. This function
88 // should only be used after DllMain() has run.
ExpandPathVariables(const std::wstring & untranslated_string)89 std::wstring ExpandPathVariables(const std::wstring& untranslated_string) {
90   std::wstring result(untranslated_string);
91   if (result.length() == 0)
92     return result;
93   // Sanitize quotes in case of any around the whole string.
94   if (result.length() > 1 &&
95       ((result.front() == L'"' && result.back() == L'"') ||
96        (result.front() == L'\'' && result.back() == L'\''))) {
97     // Strip first and last char which should be matching quotes now.
98     result.pop_back();
99     result.erase(0, 1);
100   }
101   auto sh_get_special_folder_path =
102       SCOPED_LOAD_FUNCTION(L"shell32.dll", ::SHGetSpecialFolderPathW);
103   // First translate all path variables we recognize.
104   for (size_t i = 0; i < _countof(kWinFolderMapping); ++i) {
105     size_t position = result.find(kWinFolderMapping[i].name);
106     if (position != std::wstring::npos) {
107       size_t variable_length = wcslen(kWinFolderMapping[i].name);
108       WCHAR path[MAX_PATH];
109       if (!sh_get_special_folder_path(nullptr, path, kWinFolderMapping[i].id,
110                                       false)) {
111         path[0] = 0;
112       }
113       std::wstring path_string(path);
114       // Remove a trailing slash if there is any but also only if the rest of
115       // the string contains one right after to avoid ending in a drive only
116       // value situation. This usually won't happen but if the value of this
117       // special folder is the root of a drive it will be presented as D:\.
118       if (!path_string.empty() && path_string.back() == L'\\' &&
119           result.length() > position + variable_length &&
120           result[position + variable_length] == L'\\') {
121         path_string.pop_back();
122       }
123       result.replace(position, variable_length, path_string);
124     }
125   }
126   // Next translate other windows specific variables.
127   auto get_user_name = SCOPED_LOAD_FUNCTION(L"advapi32.dll", ::GetUserNameW);
128   size_t position = result.find(kUserNamePolicyVarName);
129   if (position != std::wstring::npos) {
130     DWORD return_length = 0;
131     get_user_name(nullptr, &return_length);
132     if (return_length != 0) {
133       std::unique_ptr<WCHAR[]> username(new WCHAR[return_length]);
134       get_user_name(username.get(), &return_length);
135       std::wstring username_string(username.get());
136       result.replace(position, wcslen(kUserNamePolicyVarName), username_string);
137     }
138   }
139   position = result.find(kMachineNamePolicyVarName);
140   if (position != std::wstring::npos) {
141     DWORD return_length = 0;
142     ::GetComputerNameEx(ComputerNamePhysicalDnsHostname, nullptr,
143                         &return_length);
144     if (return_length != 0) {
145       std::unique_ptr<WCHAR[]> machinename(new WCHAR[return_length]);
146       ::GetComputerNameEx(ComputerNamePhysicalDnsHostname, machinename.get(),
147                           &return_length);
148       std::wstring machinename_string(machinename.get());
149       result.replace(position, wcslen(kMachineNamePolicyVarName),
150                      machinename_string);
151     }
152   }
153   auto wts_query_session_information =
154       SCOPED_LOAD_FUNCTION(L"wtsapi32.dll", ::WTSQuerySessionInformationW);
155   auto wts_free_memory = SCOPED_LOAD_FUNCTION(L"wtsapi32.dll", ::WTSFreeMemory);
156   position = result.find(kWinClientName);
157   if (position != std::wstring::npos) {
158     LPWSTR buffer = nullptr;
159     DWORD buffer_length = 0;
160     if (wts_query_session_information(WTS_CURRENT_SERVER, WTS_CURRENT_SESSION,
161                                       WTSClientName, &buffer, &buffer_length)) {
162       std::wstring clientname_string(buffer);
163       result.replace(position, wcslen(kWinClientName), clientname_string);
164       wts_free_memory(buffer);
165     }
166   }
167   position = result.find(kWinSessionName);
168   if (position != std::wstring::npos) {
169     LPWSTR buffer = nullptr;
170     DWORD buffer_length = 0;
171     if (wts_query_session_information(WTS_CURRENT_SERVER, WTS_CURRENT_SESSION,
172                                       WTSWinStationName, &buffer,
173                                       &buffer_length)) {
174       std::wstring sessionname_string(buffer);
175       result.replace(position, wcslen(kWinSessionName), sessionname_string);
176       wts_free_memory(buffer);
177     }
178   }
179   // TODO(pastarmovj): Consider reorganizing this code once there are even more
180   // variables to be supported. The search for the var and its replacement can
181   // be extracted as common functionality.
182 
183   return result;
184 }
185 
186 }  // namespace install_static
187