| 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 |