1 /* 2 * QEMU Guest Agent win32 VSS Provider installer 3 * 4 * Copyright Hitachi Data Systems Corp. 2013 5 * 6 * Authors: 7 * Tomoki Sekiyama <tomoki.sekiyama@hds.com> 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2 or later. 10 * See the COPYING file in the top-level directory. 11 */ 12 13 #include "qemu/osdep.h" 14 15 #include "vss-common.h" 16 #include <inc/win2003/vscoordint.h> 17 #include "install.h" 18 #include <wbemidl.h> 19 #include <comdef.h> 20 #include <comutil.h> 21 22 extern HINSTANCE g_hinstDll; 23 24 const GUID CLSID_COMAdminCatalog = { 0xF618C514, 0xDFB8, 0x11d1, 25 {0xA2, 0xCF, 0x00, 0x80, 0x5F, 0xC7, 0x92, 0x35} }; 26 const GUID IID_ICOMAdminCatalog2 = { 0x790C6E0B, 0x9194, 0x4cc9, 27 {0x94, 0x26, 0xA4, 0x8A, 0x63, 0x18, 0x56, 0x96} }; 28 const GUID CLSID_WbemLocator = { 0x4590f811, 0x1d3a, 0x11d0, 29 {0x89, 0x1f, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} }; 30 const GUID IID_IWbemLocator = { 0xdc12a687, 0x737f, 0x11cf, 31 {0x88, 0x4d, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} }; 32 33 void errmsg(DWORD err, const char *text) 34 { 35 /* 36 * `text' contains function call statement when errmsg is called via chk(). 37 * To make error message more readable, we cut off the text after '('. 38 * If text doesn't contains '(', negative precision is given, which is 39 * treated as though it were missing. 40 */ 41 char *msg = NULL, *nul = strchr(text, '('); 42 int len = nul ? nul - text : -1; 43 44 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 45 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 46 NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 47 (char *)&msg, 0, NULL); 48 fprintf(stderr, "%.*s. (Error: %lx) %s\n", len, text, err, msg); 49 LocalFree(msg); 50 } 51 52 static void errmsg_dialog(DWORD err, const char *text, const char *opt = "") 53 { 54 char *msg, buf[512]; 55 56 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 57 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 58 NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 59 (char *)&msg, 0, NULL); 60 snprintf(buf, sizeof(buf), "%s%s. (Error: %lx) %s", text, opt, err, msg); 61 MessageBox(NULL, buf, "Error from " QGA_PROVIDER_NAME, MB_OK|MB_ICONERROR); 62 LocalFree(msg); 63 } 64 65 #define _chk(hr, status, msg, err_label) \ 66 do { \ 67 hr = (status); \ 68 if (FAILED(hr)) { \ 69 errmsg(hr, msg); \ 70 goto err_label; \ 71 } \ 72 } while (0) 73 74 #define chk(status) _chk(hr, status, "Failed to " #status, out) 75 76 #if !defined(__MINGW64_VERSION_MAJOR) || !defined(__MINGW64_VERSION_MINOR) || \ 77 __MINGW64_VERSION_MAJOR * 100 + __MINGW64_VERSION_MINOR < 301 78 void __stdcall _com_issue_error(HRESULT hr) 79 { 80 errmsg(hr, "Unexpected error in COM"); 81 } 82 #endif 83 84 template<class T> 85 HRESULT put_Value(ICatalogObject *pObj, LPCWSTR name, T val) 86 { 87 return pObj->put_Value(_bstr_t(name), _variant_t(val)); 88 } 89 90 /* Lookup Administrators group name from winmgmt */ 91 static HRESULT GetAdminName(_bstr_t *name) 92 { 93 HRESULT hr; 94 COMPointer<IWbemLocator> pLoc; 95 COMPointer<IWbemServices> pSvc; 96 COMPointer<IEnumWbemClassObject> pEnum; 97 COMPointer<IWbemClassObject> pWobj; 98 ULONG returned; 99 _variant_t var; 100 101 chk(CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, 102 IID_IWbemLocator, (LPVOID *)pLoc.replace())); 103 chk(pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, NULL, 104 0, 0, 0, pSvc.replace())); 105 chk(CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, 106 NULL, RPC_C_AUTHN_LEVEL_CALL, 107 RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE)); 108 chk(pSvc->ExecQuery(_bstr_t(L"WQL"), 109 _bstr_t(L"select * from Win32_Account where " 110 "SID='S-1-5-32-544' and localAccount=TRUE"), 111 WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, 112 NULL, pEnum.replace())); 113 if (!pEnum) { 114 hr = E_FAIL; 115 errmsg(hr, "Failed to query for Administrators"); 116 goto out; 117 } 118 chk(pEnum->Next(WBEM_INFINITE, 1, pWobj.replace(), &returned)); 119 if (returned == 0) { 120 hr = E_FAIL; 121 errmsg(hr, "No Administrators found"); 122 goto out; 123 } 124 125 chk(pWobj->Get(_bstr_t(L"Name"), 0, &var, 0, 0)); 126 try { 127 *name = var; 128 } catch(...) { 129 hr = E_FAIL; 130 errmsg(hr, "Failed to get name of Administrators"); 131 goto out; 132 } 133 134 out: 135 return hr; 136 } 137 138 /* Find and iterate QGA VSS provider in COM+ Application Catalog */ 139 static HRESULT QGAProviderFind( 140 HRESULT (*found)(ICatalogCollection *, int, void *), void *arg) 141 { 142 HRESULT hr; 143 COMInitializer initializer; 144 COMPointer<IUnknown> pUnknown; 145 COMPointer<ICOMAdminCatalog2> pCatalog; 146 COMPointer<ICatalogCollection> pColl; 147 COMPointer<ICatalogObject> pObj; 148 _variant_t var; 149 long i, n; 150 151 chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER, 152 IID_IUnknown, (void **)pUnknown.replace())); 153 chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2, 154 (void **)pCatalog.replace())); 155 chk(pCatalog->GetCollection(_bstr_t(L"Applications"), 156 (IDispatch **)pColl.replace())); 157 chk(pColl->Populate()); 158 159 chk(pColl->get_Count(&n)); 160 for (i = n - 1; i >= 0; i--) { 161 chk(pColl->get_Item(i, (IDispatch **)pObj.replace())); 162 chk(pObj->get_Value(_bstr_t(L"Name"), &var)); 163 if (var == _variant_t(QGA_PROVIDER_LNAME)) { 164 if (FAILED(found(pColl, i, arg))) { 165 goto out; 166 } 167 } 168 } 169 chk(pColl->SaveChanges(&n)); 170 171 out: 172 return hr; 173 } 174 175 /* Count QGA VSS provider in COM+ Application Catalog */ 176 static HRESULT QGAProviderCount(ICatalogCollection *coll, int i, void *arg) 177 { 178 (*(int *)arg)++; 179 return S_OK; 180 } 181 182 /* Remove QGA VSS provider from COM+ Application Catalog Collection */ 183 static HRESULT QGAProviderRemove(ICatalogCollection *coll, int i, void *arg) 184 { 185 HRESULT hr; 186 187 fprintf(stderr, "Removing COM+ Application: %s\n", QGA_PROVIDER_NAME); 188 chk(coll->Remove(i)); 189 out: 190 return hr; 191 } 192 193 /* Unregister this module from COM+ Applications Catalog */ 194 STDAPI COMUnregister(void) 195 { 196 HRESULT hr; 197 198 DllUnregisterServer(); 199 chk(QGAProviderFind(QGAProviderRemove, NULL)); 200 out: 201 return hr; 202 } 203 204 /* Register this module to COM+ Applications Catalog */ 205 STDAPI COMRegister(void) 206 { 207 HRESULT hr; 208 COMInitializer initializer; 209 COMPointer<IUnknown> pUnknown; 210 COMPointer<ICOMAdminCatalog2> pCatalog; 211 COMPointer<ICatalogCollection> pApps, pRoles, pUsersInRole; 212 COMPointer<ICatalogObject> pObj; 213 long n; 214 _bstr_t name; 215 _variant_t key; 216 CHAR dllPath[MAX_PATH], tlbPath[MAX_PATH]; 217 bool unregisterOnFailure = false; 218 int count = 0; 219 220 if (!g_hinstDll) { 221 errmsg(E_FAIL, "Failed to initialize DLL"); 222 return E_FAIL; 223 } 224 225 chk(QGAProviderFind(QGAProviderCount, (void *)&count)); 226 if (count) { 227 errmsg(E_ABORT, "QGA VSS Provider is already installed"); 228 return E_ABORT; 229 } 230 231 chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER, 232 IID_IUnknown, (void **)pUnknown.replace())); 233 chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2, 234 (void **)pCatalog.replace())); 235 236 /* Install COM+ Component */ 237 238 chk(pCatalog->GetCollection(_bstr_t(L"Applications"), 239 (IDispatch **)pApps.replace())); 240 chk(pApps->Populate()); 241 chk(pApps->Add((IDispatch **)&pObj)); 242 chk(put_Value(pObj, L"Name", QGA_PROVIDER_LNAME)); 243 chk(put_Value(pObj, L"Description", QGA_PROVIDER_LNAME)); 244 chk(put_Value(pObj, L"ApplicationAccessChecksEnabled", true)); 245 chk(put_Value(pObj, L"Authentication", short(6))); 246 chk(put_Value(pObj, L"AuthenticationCapability", short(2))); 247 chk(put_Value(pObj, L"ImpersonationLevel", short(2))); 248 chk(pApps->SaveChanges(&n)); 249 250 /* The app should be deleted if something fails after SaveChanges */ 251 unregisterOnFailure = true; 252 253 chk(pObj->get_Key(&key)); 254 255 if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) { 256 hr = HRESULT_FROM_WIN32(GetLastError()); 257 errmsg(hr, "GetModuleFileName failed"); 258 goto out; 259 } 260 n = strlen(dllPath); 261 if (n < 3) { 262 hr = E_FAIL; 263 errmsg(hr, "Failed to lookup dll"); 264 goto out; 265 } 266 strcpy(tlbPath, dllPath); 267 strcpy(tlbPath+n-3, "tlb"); 268 fprintf(stderr, "Registering " QGA_PROVIDER_NAME ":\n"); 269 fprintf(stderr, " %s\n", dllPath); 270 fprintf(stderr, " %s\n", tlbPath); 271 if (!PathFileExists(tlbPath)) { 272 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 273 errmsg(hr, "Failed to lookup tlb"); 274 goto out; 275 } 276 277 chk(pCatalog->CreateServiceForApplication( 278 _bstr_t(QGA_PROVIDER_LNAME), _bstr_t(QGA_PROVIDER_LNAME), 279 _bstr_t(L"SERVICE_DEMAND_START"), _bstr_t(L"SERVICE_ERROR_NORMAL"), 280 _bstr_t(L""), _bstr_t(L".\\localsystem"), _bstr_t(L""), FALSE)); 281 chk(pCatalog->InstallComponent(_bstr_t(QGA_PROVIDER_LNAME), 282 _bstr_t(dllPath), _bstr_t(tlbPath), 283 _bstr_t(""))); 284 285 /* Setup roles of the applicaion */ 286 287 chk(pApps->GetCollection(_bstr_t(L"Roles"), key, 288 (IDispatch **)pRoles.replace())); 289 chk(pRoles->Populate()); 290 chk(pRoles->Add((IDispatch **)pObj.replace())); 291 chk(put_Value(pObj, L"Name", L"Administrators")); 292 chk(put_Value(pObj, L"Description", L"Administrators group")); 293 chk(pRoles->SaveChanges(&n)); 294 chk(pObj->get_Key(&key)); 295 296 /* Setup users in the role */ 297 298 chk(pRoles->GetCollection(_bstr_t(L"UsersInRole"), key, 299 (IDispatch **)pUsersInRole.replace())); 300 chk(pUsersInRole->Populate()); 301 302 chk(pUsersInRole->Add((IDispatch **)pObj.replace())); 303 chk(GetAdminName(&name)); 304 chk(put_Value(pObj, L"User", _bstr_t(".\\") + name)); 305 306 chk(pUsersInRole->Add((IDispatch **)pObj.replace())); 307 chk(put_Value(pObj, L"User", L"SYSTEM")); 308 chk(pUsersInRole->SaveChanges(&n)); 309 310 out: 311 if (unregisterOnFailure && FAILED(hr)) { 312 COMUnregister(); 313 } 314 315 return hr; 316 } 317 318 319 static BOOL CreateRegistryKey(LPCTSTR key, LPCTSTR value, LPCTSTR data) 320 { 321 HKEY hKey; 322 LONG ret; 323 DWORD size; 324 325 ret = RegCreateKeyEx(HKEY_CLASSES_ROOT, key, 0, NULL, 326 REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL); 327 if (ret != ERROR_SUCCESS) { 328 goto out; 329 } 330 331 if (data != NULL) { 332 size = strlen(data) + 1; 333 } else { 334 size = 0; 335 } 336 337 ret = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, size); 338 RegCloseKey(hKey); 339 340 out: 341 if (ret != ERROR_SUCCESS) { 342 /* As we cannot printf within DllRegisterServer(), show a dialog. */ 343 errmsg_dialog(ret, "Cannot add registry", key); 344 return FALSE; 345 } 346 return TRUE; 347 } 348 349 /* Register this dll as a VSS provider */ 350 STDAPI DllRegisterServer(void) 351 { 352 COMInitializer initializer; 353 COMPointer<IVssAdmin> pVssAdmin; 354 HRESULT hr = E_FAIL; 355 char dllPath[MAX_PATH]; 356 char key[256]; 357 358 if (!g_hinstDll) { 359 errmsg_dialog(hr, "Module instance is not available"); 360 goto out; 361 } 362 363 /* Add this module to registery */ 364 365 sprintf(key, "CLSID\\%s", g_szClsid); 366 if (!CreateRegistryKey(key, NULL, g_szClsid)) { 367 goto out; 368 } 369 370 if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) { 371 errmsg_dialog(GetLastError(), "GetModuleFileName failed"); 372 goto out; 373 } 374 375 sprintf(key, "CLSID\\%s\\InprocServer32", g_szClsid); 376 if (!CreateRegistryKey(key, NULL, dllPath)) { 377 goto out; 378 } 379 380 if (!CreateRegistryKey(key, "ThreadingModel", "Apartment")) { 381 goto out; 382 } 383 384 sprintf(key, "CLSID\\%s\\ProgID", g_szClsid); 385 if (!CreateRegistryKey(key, NULL, g_szProgid)) { 386 goto out; 387 } 388 389 if (!CreateRegistryKey(g_szProgid, NULL, QGA_PROVIDER_NAME)) { 390 goto out; 391 } 392 393 sprintf(key, "%s\\CLSID", g_szProgid); 394 if (!CreateRegistryKey(key, NULL, g_szClsid)) { 395 goto out; 396 } 397 398 hr = CoCreateInstance(CLSID_VSSCoordinator, NULL, CLSCTX_ALL, 399 IID_IVssAdmin, (void **)pVssAdmin.replace()); 400 if (FAILED(hr)) { 401 errmsg_dialog(hr, "CoCreateInstance(VSSCoordinator) failed"); 402 goto out; 403 } 404 405 hr = pVssAdmin->RegisterProvider(g_gProviderId, CLSID_QGAVSSProvider, 406 const_cast<WCHAR*>(QGA_PROVIDER_LNAME), 407 VSS_PROV_SOFTWARE, 408 const_cast<WCHAR*>(QGA_PROVIDER_VERSION), 409 g_gProviderVersion); 410 if (FAILED(hr)) { 411 errmsg_dialog(hr, "RegisterProvider failed"); 412 } 413 414 out: 415 if (FAILED(hr)) { 416 DllUnregisterServer(); 417 } 418 419 return hr; 420 } 421 422 /* Unregister this VSS hardware provider from the system */ 423 STDAPI DllUnregisterServer(void) 424 { 425 TCHAR key[256]; 426 COMInitializer initializer; 427 COMPointer<IVssAdmin> pVssAdmin; 428 429 HRESULT hr = CoCreateInstance(CLSID_VSSCoordinator, 430 NULL, CLSCTX_ALL, IID_IVssAdmin, 431 (void **)pVssAdmin.replace()); 432 if (SUCCEEDED(hr)) { 433 hr = pVssAdmin->UnregisterProvider(g_gProviderId); 434 } else { 435 errmsg(hr, "CoCreateInstance(VSSCoordinator) failed"); 436 } 437 438 sprintf(key, "CLSID\\%s", g_szClsid); 439 SHDeleteKey(HKEY_CLASSES_ROOT, key); 440 SHDeleteKey(HKEY_CLASSES_ROOT, g_szProgid); 441 442 return S_OK; /* Uninstall should never fail */ 443 } 444 445 446 /* Support function to convert ASCII string into BSTR (used in _bstr_t) */ 447 namespace _com_util 448 { 449 BSTR WINAPI ConvertStringToBSTR(const char *ascii) { 450 int len = strlen(ascii); 451 BSTR bstr = SysAllocStringLen(NULL, len); 452 453 if (!bstr) { 454 return NULL; 455 } 456 457 if (mbstowcs(bstr, ascii, len) == (size_t)-1) { 458 fprintf(stderr, "Failed to convert string '%s' into BSTR", ascii); 459 bstr[0] = 0; 460 } 461 return bstr; 462 } 463 } 464 465 /* Stop QGA VSS provider service from COM+ Application Admin Catalog */ 466 467 STDAPI StopService(void) 468 { 469 HRESULT hr; 470 COMInitializer initializer; 471 COMPointer<IUnknown> pUnknown; 472 COMPointer<ICOMAdminCatalog2> pCatalog; 473 474 int count = 0; 475 476 chk(QGAProviderFind(QGAProviderCount, (void *)&count)); 477 if (count) { 478 chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER, 479 IID_IUnknown, (void **)pUnknown.replace())); 480 chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2, 481 (void **)pCatalog.replace())); 482 chk(pCatalog->ShutdownApplication(_bstr_t(QGA_PROVIDER_LNAME))); 483 } 484 485 out: 486 return hr; 487 } 488