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