OLD | NEW |
| (Empty) |
1 // Copyright 2007-2009 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 // | |
16 // This is the implementation of the API for verifying and executing | |
17 // an executable under high integrity using an Msi Patch. | |
18 // | |
19 // This class assumes the following: | |
20 // 1) its needed Msi has already been installed, | |
21 // 2) its needed Msp is in the same directory as this module, | |
22 // 3) the name of the Msp file, | |
23 // 4) the name of the property passed as CustomActionData to the custom action, | |
24 // 5) the guid of the patch, and | |
25 // 6) the guid of the Msi install which will be patched. | |
26 | |
27 #define _WIN32_MSI 300 | |
28 | |
29 #include "omaha/recovery/repair_exe/mspexecutableelevator.h" | |
30 #include <atlpath.h> | |
31 #include <msi.h> | |
32 #include "omaha/base/debug.h" | |
33 #include "omaha/base/safe_format.h" | |
34 #include "omaha/base/string.h" | |
35 | |
36 namespace omaha { | |
37 | |
38 namespace msp_executable_elevator { | |
39 | |
40 // Used to return information back to the process that called | |
41 // ExecuteGoogleSignedExe. | |
42 struct SharedMemoryInfo { | |
43 HANDLE process; | |
44 HRESULT launch_result; | |
45 }; | |
46 | |
47 // Used to store the name of the shared memory. The name is retrieved from | |
48 // the MSP command line when parsing the command line. The code assumes that | |
49 // only one thread per process will call ParseMSPCommandLine followed by | |
50 // SetResultOfExecute (which is a safe assumption if this functionality is only | |
51 // used for the purpose for which it was originally written). | |
52 // Assumes that the MSP is in the same directory as the current process. | |
53 static TCHAR parsed_shared_memory_name[200]; | |
54 | |
55 HRESULT ExecuteGoogleSignedExe(const TCHAR* exe, | |
56 const TCHAR* args, | |
57 const TCHAR* kProductGuid, | |
58 const TCHAR* kPatchGuid, | |
59 const TCHAR* kPatchName, | |
60 HANDLE* process) { | |
61 ASSERT1(exe); | |
62 ASSERT1(args); | |
63 ASSERT1(process); | |
64 ASSERT1(kProductGuid); | |
65 ASSERT1(kPatchGuid); | |
66 ASSERT1(kPatchName); | |
67 | |
68 // Create shared memory in which to receive result of attempt to launch | |
69 // process and a handle to the launched process. | |
70 HRESULT hr = E_FAIL; | |
71 GUID random_guid = {0}; | |
72 TCHAR shared_memory_name[200] = {0}; | |
73 if (SUCCEEDED(::CoCreateGuid(&random_guid)) && | |
74 0 < ::StringFromGUID2(random_guid, | |
75 shared_memory_name, | |
76 ARRAYSIZE(shared_memory_name))) { | |
77 HANDLE file_mapping = ::CreateFileMapping(INVALID_HANDLE_VALUE, | |
78 NULL, | |
79 PAGE_READWRITE, | |
80 0, | |
81 sizeof(file_mapping), | |
82 shared_memory_name); | |
83 if (file_mapping) { | |
84 SharedMemoryInfo* shared_info = reinterpret_cast<SharedMemoryInfo*> | |
85 (::MapViewOfFileEx(file_mapping, | |
86 FILE_MAP_ALL_ACCESS, | |
87 0, | |
88 0, | |
89 sizeof(*shared_info), | |
90 0)); | |
91 if (shared_info) { | |
92 shared_info->launch_result = E_FAIL; | |
93 shared_info->process = NULL; | |
94 // Create command line to pass to patch. Parameters are the name of the | |
95 // shared memory just created, the current process id, the executable | |
96 // to launch, and the executable's arguments. | |
97 CString command_line; | |
98 SafeCStringFormat(&command_line, | |
99 _T("EXECUTABLECOMMANDLINE=\"%s %u \"\"%s\"\" %s\" ") | |
100 _T("REINSTALL=ALL"), | |
101 shared_memory_name, | |
102 GetCurrentProcessId(), | |
103 exe, | |
104 args); | |
105 // Generate path to patch using path to current module. | |
106 TCHAR module_name[MAX_PATH] = {0}; | |
107 DWORD len = ::GetModuleFileName(_AtlBaseModule.GetModuleInstance(), | |
108 module_name, | |
109 ARRAYSIZE(module_name)); | |
110 module_name[ARRAYSIZE(module_name) - 1] = '\0'; | |
111 if (0 < len && len < ARRAYSIZE(module_name) && | |
112 0 < command_line.GetLength()) { | |
113 CPath path = module_name; | |
114 if (path.RemoveFileSpec() && path.Append(kPatchName)) { | |
115 path.Canonicalize(); | |
116 // Set install level to none so that user does not see | |
117 // an Msi window and so that a restore point is not created. | |
118 ::MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); | |
119 UINT res = ::MsiApplyPatch(path, | |
120 NULL, | |
121 INSTALLTYPE_DEFAULT, | |
122 command_line); | |
123 // MsiApplyPatch will not return until the passed executable has | |
124 // been launched (or not) and the shared memory has been updated. | |
125 *process = shared_info->process; | |
126 hr = HRESULT_FROM_WIN32(res); | |
127 if (SUCCEEDED(hr)) | |
128 hr = shared_info->launch_result; | |
129 } | |
130 } | |
131 ::MsiRemovePatches(kPatchGuid, | |
132 kProductGuid, | |
133 INSTALLTYPE_SINGLE_INSTANCE, | |
134 NULL); | |
135 VERIFY1(::UnmapViewOfFile(shared_info)); | |
136 } | |
137 VERIFY1(::CloseHandle(file_mapping)); | |
138 } | |
139 } | |
140 return hr; | |
141 } | |
142 | |
143 bool ParseMSPCommandLine(TCHAR* command_line, | |
144 TCHAR** executable_result, | |
145 TCHAR** arguments_result, | |
146 DWORD* calling_process_id) { | |
147 ASSERT1(command_line); | |
148 ASSERT1(executable_result); | |
149 ASSERT1(arguments_result); | |
150 ASSERT1(calling_process_id); | |
151 // Parse command line. First extract name of shared memory into which | |
152 // the process handle of launched executable will be written. Then extract | |
153 // the process id of calling process. This process id will be used to create | |
154 // for the calling process a handle to the launched executable. Finally, | |
155 // extract the path to executable so that we can verify the executable. | |
156 TCHAR* shared_memory_name = NULL; | |
157 TCHAR* process_id_param = NULL; | |
158 TCHAR* arguments = NULL; | |
159 if (SplitCommandLineInPlace(command_line, &shared_memory_name, &arguments) && | |
160 shared_memory_name && arguments && | |
161 SplitCommandLineInPlace(arguments, &process_id_param, &arguments) && | |
162 process_id_param && arguments && | |
163 SplitCommandLineInPlace(arguments, executable_result, &arguments) && | |
164 *executable_result && arguments) { | |
165 *calling_process_id = static_cast<DWORD>(_wtoi(process_id_param)); | |
166 *arguments_result = arguments; | |
167 _tcsncpy(parsed_shared_memory_name, | |
168 shared_memory_name, | |
169 ARRAYSIZE(parsed_shared_memory_name)); | |
170 parsed_shared_memory_name[ARRAYSIZE(parsed_shared_memory_name) - 1] = | |
171 _T('\0'); | |
172 return true; | |
173 } | |
174 return false; | |
175 } | |
176 | |
177 // Copy process handle and result to shared memory. | |
178 // Process can be NULL. | |
179 bool SetResultOfExecute(HANDLE process, HRESULT result) { | |
180 bool success = false; | |
181 if (_T('\0') != *parsed_shared_memory_name) { | |
182 HANDLE file_mapping = ::OpenFileMapping(FILE_MAP_WRITE, | |
183 FALSE, | |
184 parsed_shared_memory_name); | |
185 if (file_mapping) { | |
186 SharedMemoryInfo* shared_info = reinterpret_cast<SharedMemoryInfo*> | |
187 (::MapViewOfFileEx(file_mapping, | |
188 FILE_MAP_WRITE, | |
189 0, | |
190 0, | |
191 sizeof(SharedMemoryInfo), | |
192 0)); | |
193 if (shared_info) { | |
194 shared_info->process = process; | |
195 shared_info->launch_result = result; | |
196 VERIFY1(::UnmapViewOfFile(shared_info)); | |
197 success = true; | |
198 } else { | |
199 ASSERT(false, (_T("::MapViewOfFileEx failed."))); | |
200 } | |
201 | |
202 VERIFY1(::CloseHandle(file_mapping)); | |
203 } else { | |
204 ASSERT(false, (_T("::OpenFileMapping failed."))); | |
205 } | |
206 } else { | |
207 ASSERT(false, (_T("parsed_shared_memory_name is empty."))); | |
208 } | |
209 return success; | |
210 } | |
211 | |
212 } // namespace msp_executable_elevator | |
213 | |
214 } // namespace omaha | |
215 | |
OLD | NEW |