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 "vss-debug.h" 17 #ifdef HAVE_VSS_SDK 18 #include <vscoordint.h> 19 #else 20 #include <vsadmin.h> 21 #endif 22 #include "install.h" 23 #include <wbemidl.h> 24 #include <comdef.h> 25 #include <comutil.h> 26 #include <sddl.h> 27 #include <winsvc.h> 28 29 #define BUFFER_SIZE 1024 30 31 extern HINSTANCE g_hinstDll; 32 33 const GUID CLSID_COMAdminCatalog = { 0xF618C514, 0xDFB8, 0x11d1, 34 {0xA2, 0xCF, 0x00, 0x80, 0x5F, 0xC7, 0x92, 0x35} }; 35 const GUID IID_ICOMAdminCatalog2 = { 0x790C6E0B, 0x9194, 0x4cc9, 36 {0x94, 0x26, 0xA4, 0x8A, 0x63, 0x18, 0x56, 0x96} }; 37 const GUID CLSID_WbemLocator = { 0x4590f811, 0x1d3a, 0x11d0, 38 {0x89, 0x1f, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} }; 39 const GUID IID_IWbemLocator = { 0xdc12a687, 0x737f, 0x11cf, 40 {0x88, 0x4d, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} }; 41 42 static void errmsg(DWORD err, const char *text) 43 { 44 /* 45 * `text' contains function call statement when errmsg is called via chk(). 46 * To make error message more readable, we cut off the text after '('. 47 * If text doesn't contains '(', negative precision is given, which is 48 * treated as though it were missing. 49 */ 50 char *msg = NULL; 51 const char *nul = strchr(text, '('); 52 int len = nul ? nul - text : -1; 53 54 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 55 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 56 NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 57 (char *)&msg, 0, NULL); 58 qga_debug("%.*s. (Error: %lx) %s", len, text, err, msg); 59 LocalFree(msg); 60 } 61 62 static void errmsg_dialog(DWORD err, const char *text, const char *opt = "") 63 { 64 char *msg, buf[512]; 65 66 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 67 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 68 NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 69 (char *)&msg, 0, NULL); 70 snprintf(buf, sizeof(buf), "%s%s. (Error: %lx) %s", text, opt, err, msg); 71 MessageBox(NULL, buf, "Error from " QGA_PROVIDER_NAME, MB_OK|MB_ICONERROR); 72 LocalFree(msg); 73 } 74 75 #define _chk(hr, status, msg, err_label) \ 76 do { \ 77 hr = (status); \ 78 if (FAILED(hr)) { \ 79 errmsg(hr, msg); \ 80 goto err_label; \ 81 } \ 82 } while (0) 83 84 #define chk(status) _chk(hr, status, "Failed to " #status, out) 85 86 #if !defined(__MINGW64_VERSION_MAJOR) || !defined(__MINGW64_VERSION_MINOR) || \ 87 __MINGW64_VERSION_MAJOR * 100 + __MINGW64_VERSION_MINOR < 301 88 void __stdcall _com_issue_error(HRESULT hr) 89 { 90 errmsg(hr, "Unexpected error in COM"); 91 } 92 #endif 93 94 template<class T> 95 HRESULT put_Value(ICatalogObject *pObj, LPCWSTR name, T val) 96 { 97 return pObj->put_Value(_bstr_t(name), _variant_t(val)); 98 } 99 100 /* Lookup Administrators group name from winmgmt */ 101 static HRESULT GetAdminName(_bstr_t *name) 102 { 103 qga_debug_begin; 104 105 HRESULT hr; 106 COMPointer<IWbemLocator> pLoc; 107 COMPointer<IWbemServices> pSvc; 108 COMPointer<IEnumWbemClassObject> pEnum; 109 COMPointer<IWbemClassObject> pWobj; 110 ULONG returned; 111 _variant_t var; 112 113 chk(CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, 114 IID_IWbemLocator, (LPVOID *)pLoc.replace())); 115 chk(pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, NULL, 116 0, 0, 0, pSvc.replace())); 117 chk(CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, 118 NULL, RPC_C_AUTHN_LEVEL_CALL, 119 RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE)); 120 chk(pSvc->ExecQuery(_bstr_t(L"WQL"), 121 _bstr_t(L"select * from Win32_Account where " 122 "SID='S-1-5-32-544' and localAccount=TRUE"), 123 WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, 124 NULL, pEnum.replace())); 125 if (!pEnum) { 126 hr = E_FAIL; 127 errmsg(hr, "Failed to query for Administrators"); 128 goto out; 129 } 130 chk(pEnum->Next(WBEM_INFINITE, 1, pWobj.replace(), &returned)); 131 if (returned == 0) { 132 hr = E_FAIL; 133 errmsg(hr, "No Administrators found"); 134 goto out; 135 } 136 137 chk(pWobj->Get(_bstr_t(L"Name"), 0, &var, 0, 0)); 138 try { 139 *name = var; 140 } catch(...) { 141 hr = E_FAIL; 142 errmsg(hr, "Failed to get name of Administrators"); 143 goto out; 144 } 145 146 out: 147 qga_debug_end; 148 return hr; 149 } 150 151 /* Acquire group or user name by SID */ 152 static HRESULT getNameByStringSID( 153 const wchar_t *sid, LPWSTR buffer, LPDWORD bufferLen) 154 { 155 qga_debug_begin; 156 157 HRESULT hr = S_OK; 158 PSID psid = NULL; 159 SID_NAME_USE groupType; 160 DWORD domainNameLen = BUFFER_SIZE; 161 wchar_t domainName[BUFFER_SIZE]; 162 163 if (!ConvertStringSidToSidW(sid, &psid)) { 164 hr = HRESULT_FROM_WIN32(GetLastError()); 165 goto out; 166 } 167 if (!LookupAccountSidW(NULL, psid, buffer, bufferLen, 168 domainName, &domainNameLen, &groupType)) { 169 hr = HRESULT_FROM_WIN32(GetLastError()); 170 /* Fall through and free psid */ 171 } 172 173 LocalFree(psid); 174 175 out: 176 qga_debug_end; 177 return hr; 178 } 179 180 /* Find and iterate QGA VSS provider in COM+ Application Catalog */ 181 static HRESULT QGAProviderFind( 182 HRESULT (*found)(ICatalogCollection *, int, void *), void *arg) 183 { 184 qga_debug_begin; 185 186 HRESULT hr; 187 COMInitializer initializer; 188 COMPointer<IUnknown> pUnknown; 189 COMPointer<ICOMAdminCatalog2> pCatalog; 190 COMPointer<ICatalogCollection> pColl; 191 COMPointer<ICatalogObject> pObj; 192 _variant_t var; 193 long i, n; 194 195 chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER, 196 IID_IUnknown, (void **)pUnknown.replace())); 197 chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2, 198 (void **)pCatalog.replace())); 199 chk(pCatalog->GetCollection(_bstr_t(L"Applications"), 200 (IDispatch **)pColl.replace())); 201 chk(pColl->Populate()); 202 203 chk(pColl->get_Count(&n)); 204 for (i = n - 1; i >= 0; i--) { 205 chk(pColl->get_Item(i, (IDispatch **)pObj.replace())); 206 chk(pObj->get_Value(_bstr_t(L"Name"), &var)); 207 if (var == _variant_t(QGA_PROVIDER_LNAME)) { 208 if (FAILED(found(pColl, i, arg))) { 209 goto out; 210 } 211 } 212 } 213 chk(pColl->SaveChanges(&n)); 214 215 out: 216 qga_debug_end; 217 return hr; 218 } 219 220 /* Count QGA VSS provider in COM+ Application Catalog */ 221 static HRESULT QGAProviderCount(ICatalogCollection *coll, int i, void *arg) 222 { 223 qga_debug_begin; 224 225 (*(int *)arg)++; 226 227 qga_debug_end; 228 return S_OK; 229 } 230 231 /* Remove QGA VSS provider from COM+ Application Catalog Collection */ 232 static HRESULT QGAProviderRemove(ICatalogCollection *coll, int i, void *arg) 233 { 234 qga_debug_begin; 235 HRESULT hr; 236 237 qga_debug("Removing COM+ Application: %s", QGA_PROVIDER_NAME); 238 chk(coll->Remove(i)); 239 out: 240 qga_debug_end; 241 return hr; 242 } 243 244 /* Unregister this module from COM+ Applications Catalog */ 245 STDAPI COMUnregister(void); 246 STDAPI COMUnregister(void) 247 { 248 qga_debug_begin; 249 250 HRESULT hr; 251 252 DllUnregisterServer(); 253 chk(QGAProviderFind(QGAProviderRemove, NULL)); 254 out: 255 qga_debug_end; 256 return hr; 257 } 258 259 /* Register this module to COM+ Applications Catalog */ 260 STDAPI COMRegister(void); 261 STDAPI COMRegister(void) 262 { 263 qga_debug_begin; 264 265 HRESULT hr; 266 COMInitializer initializer; 267 COMPointer<IUnknown> pUnknown; 268 COMPointer<ICOMAdminCatalog2> pCatalog; 269 COMPointer<ICatalogCollection> pApps, pRoles, pUsersInRole; 270 COMPointer<ICatalogObject> pObj; 271 long n; 272 _bstr_t name; 273 _variant_t key; 274 CHAR dllPath[MAX_PATH], tlbPath[MAX_PATH]; 275 bool unregisterOnFailure = false; 276 int count = 0; 277 DWORD bufferLen = BUFFER_SIZE; 278 wchar_t buffer[BUFFER_SIZE]; 279 const wchar_t *administratorsGroupSID = L"S-1-5-32-544"; 280 const wchar_t *systemUserSID = L"S-1-5-18"; 281 282 if (!g_hinstDll) { 283 errmsg(E_FAIL, "Failed to initialize DLL"); 284 qga_debug_end; 285 return E_FAIL; 286 } 287 288 chk(QGAProviderFind(QGAProviderCount, (void *)&count)); 289 if (count) { 290 qga_debug("QGA VSS Provider is already installed. Attempting to unregister first."); 291 hr = COMUnregister(); 292 if (FAILED(hr)) { 293 errmsg(hr, "Failed to unregister existing QGA VSS Provider. Aborting installation."); 294 qga_debug_end; 295 return E_ABORT; 296 } 297 } 298 299 chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER, 300 IID_IUnknown, (void **)pUnknown.replace())); 301 chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2, 302 (void **)pCatalog.replace())); 303 304 /* Install COM+ Component */ 305 306 chk(pCatalog->GetCollection(_bstr_t(L"Applications"), 307 (IDispatch **)pApps.replace())); 308 chk(pApps->Populate()); 309 chk(pApps->Add((IDispatch **)&pObj)); 310 chk(put_Value(pObj, L"Name", QGA_PROVIDER_LNAME)); 311 chk(put_Value(pObj, L"Description", QGA_PROVIDER_LNAME)); 312 chk(put_Value(pObj, L"ApplicationAccessChecksEnabled", true)); 313 chk(put_Value(pObj, L"Authentication", short(6))); 314 chk(put_Value(pObj, L"AuthenticationCapability", short(2))); 315 chk(put_Value(pObj, L"ImpersonationLevel", short(2))); 316 chk(pApps->SaveChanges(&n)); 317 318 /* The app should be deleted if something fails after SaveChanges */ 319 unregisterOnFailure = true; 320 321 chk(pObj->get_Key(&key)); 322 323 if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) { 324 hr = HRESULT_FROM_WIN32(GetLastError()); 325 errmsg(hr, "GetModuleFileName failed"); 326 goto out; 327 } 328 n = strlen(dllPath); 329 if (n < 3) { 330 hr = E_FAIL; 331 errmsg(hr, "Failed to lookup dll"); 332 goto out; 333 } 334 strcpy(tlbPath, dllPath); 335 strcpy(tlbPath+n-3, "tlb"); 336 qga_debug("Registering " QGA_PROVIDER_NAME ": %s %s", 337 dllPath, tlbPath); 338 if (!PathFileExists(tlbPath)) { 339 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 340 errmsg(hr, "Failed to lookup tlb"); 341 goto out; 342 } 343 344 chk(pCatalog->CreateServiceForApplication( 345 _bstr_t(QGA_PROVIDER_LNAME), _bstr_t(QGA_PROVIDER_LNAME), 346 _bstr_t(L"SERVICE_DEMAND_START"), _bstr_t(L"SERVICE_ERROR_NORMAL"), 347 _bstr_t(L""), _bstr_t(L".\\localsystem"), _bstr_t(L""), FALSE)); 348 chk(pCatalog->InstallComponent(_bstr_t(QGA_PROVIDER_LNAME), 349 _bstr_t(dllPath), _bstr_t(tlbPath), 350 _bstr_t(""))); 351 352 /* Setup roles of the application */ 353 354 chk(getNameByStringSID(administratorsGroupSID, buffer, &bufferLen)); 355 chk(pApps->GetCollection(_bstr_t(L"Roles"), key, 356 (IDispatch **)pRoles.replace())); 357 chk(pRoles->Populate()); 358 chk(pRoles->Add((IDispatch **)pObj.replace())); 359 chk(put_Value(pObj, L"Name", buffer)); 360 chk(put_Value(pObj, L"Description", L"Administrators group")); 361 chk(pRoles->SaveChanges(&n)); 362 chk(pObj->get_Key(&key)); 363 364 /* Setup users in the role */ 365 366 chk(pRoles->GetCollection(_bstr_t(L"UsersInRole"), key, 367 (IDispatch **)pUsersInRole.replace())); 368 chk(pUsersInRole->Populate()); 369 370 chk(pUsersInRole->Add((IDispatch **)pObj.replace())); 371 chk(GetAdminName(&name)); 372 chk(put_Value(pObj, L"User", _bstr_t(".\\") + name)); 373 374 bufferLen = BUFFER_SIZE; 375 chk(getNameByStringSID(systemUserSID, buffer, &bufferLen)); 376 chk(pUsersInRole->Add((IDispatch **)pObj.replace())); 377 chk(put_Value(pObj, L"User", buffer)); 378 chk(pUsersInRole->SaveChanges(&n)); 379 380 out: 381 if (unregisterOnFailure && FAILED(hr)) { 382 COMUnregister(); 383 } 384 385 qga_debug_end; 386 return hr; 387 } 388 389 STDAPI_(void) CALLBACK DLLCOMRegister(HWND, HINSTANCE, LPSTR, int); 390 STDAPI_(void) CALLBACK DLLCOMRegister(HWND, HINSTANCE, LPSTR, int) 391 { 392 HRESULT hr = COMRegister(); 393 if (FAILED(hr)) { 394 exit(hr); 395 } 396 } 397 398 STDAPI_(void) CALLBACK DLLCOMUnregister(HWND, HINSTANCE, LPSTR, int); 399 STDAPI_(void) CALLBACK DLLCOMUnregister(HWND, HINSTANCE, LPSTR, int) 400 { 401 COMUnregister(); 402 } 403 404 static BOOL CreateRegistryKey(LPCTSTR key, LPCTSTR value, LPCTSTR data) 405 { 406 qga_debug_begin; 407 408 HKEY hKey; 409 LONG ret; 410 DWORD size; 411 412 ret = RegCreateKeyEx(HKEY_CLASSES_ROOT, key, 0, NULL, 413 REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL); 414 if (ret != ERROR_SUCCESS) { 415 goto out; 416 } 417 418 if (data != NULL) { 419 size = strlen(data) + 1; 420 } else { 421 size = 0; 422 } 423 424 ret = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, size); 425 RegCloseKey(hKey); 426 427 out: 428 qga_debug_end; 429 if (ret != ERROR_SUCCESS) { 430 /* As we cannot printf within DllRegisterServer(), show a dialog. */ 431 errmsg_dialog(ret, "Cannot add registry", key); 432 return FALSE; 433 } 434 return TRUE; 435 } 436 437 /* Register this dll as a VSS provider */ 438 STDAPI DllRegisterServer(void) 439 { 440 qga_debug_begin; 441 442 COMInitializer initializer; 443 COMPointer<IVssAdmin> pVssAdmin; 444 HRESULT hr = E_FAIL; 445 char dllPath[MAX_PATH]; 446 char key[256]; 447 448 if (!g_hinstDll) { 449 errmsg_dialog(hr, "Module instance is not available"); 450 goto out; 451 } 452 453 /* Add this module to registry */ 454 455 sprintf(key, "CLSID\\%s", g_szClsid); 456 if (!CreateRegistryKey(key, NULL, g_szClsid)) { 457 goto out; 458 } 459 460 if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) { 461 errmsg_dialog(GetLastError(), "GetModuleFileName failed"); 462 goto out; 463 } 464 465 sprintf(key, "CLSID\\%s\\InprocServer32", g_szClsid); 466 if (!CreateRegistryKey(key, NULL, dllPath)) { 467 goto out; 468 } 469 470 if (!CreateRegistryKey(key, "ThreadingModel", "Apartment")) { 471 goto out; 472 } 473 474 sprintf(key, "CLSID\\%s\\ProgID", g_szClsid); 475 if (!CreateRegistryKey(key, NULL, g_szProgid)) { 476 goto out; 477 } 478 479 if (!CreateRegistryKey(g_szProgid, NULL, QGA_PROVIDER_NAME)) { 480 goto out; 481 } 482 483 sprintf(key, "%s\\CLSID", g_szProgid); 484 if (!CreateRegistryKey(key, NULL, g_szClsid)) { 485 goto out; 486 } 487 488 hr = CoCreateInstance(CLSID_VSSCoordinator, NULL, CLSCTX_ALL, 489 IID_IVssAdmin, (void **)pVssAdmin.replace()); 490 if (FAILED(hr)) { 491 errmsg_dialog(hr, "CoCreateInstance(VSSCoordinator) failed"); 492 goto out; 493 } 494 495 hr = pVssAdmin->RegisterProvider(g_gProviderId, CLSID_QGAVSSProvider, 496 const_cast<WCHAR*>(QGA_PROVIDER_LNAME), 497 VSS_PROV_SOFTWARE, 498 const_cast<WCHAR*>(QGA_PROVIDER_VERSION), 499 g_gProviderVersion); 500 if (hr == (long int) VSS_E_PROVIDER_ALREADY_REGISTERED) { 501 DllUnregisterServer(); 502 hr = pVssAdmin->RegisterProvider(g_gProviderId, CLSID_QGAVSSProvider, 503 const_cast<WCHAR * > 504 (QGA_PROVIDER_LNAME), 505 VSS_PROV_SOFTWARE, 506 const_cast<WCHAR * > 507 (QGA_PROVIDER_VERSION), 508 g_gProviderVersion); 509 } 510 511 if (FAILED(hr)) { 512 errmsg_dialog(hr, "RegisterProvider failed"); 513 } 514 515 out: 516 if (FAILED(hr)) { 517 DllUnregisterServer(); 518 } 519 520 qga_debug_end; 521 return hr; 522 } 523 524 /* Unregister this VSS hardware provider from the system */ 525 STDAPI DllUnregisterServer(void) 526 { 527 qga_debug_begin; 528 529 TCHAR key[256]; 530 COMInitializer initializer; 531 COMPointer<IVssAdmin> pVssAdmin; 532 533 HRESULT hr = CoCreateInstance(CLSID_VSSCoordinator, 534 NULL, CLSCTX_ALL, IID_IVssAdmin, 535 (void **)pVssAdmin.replace()); 536 if (SUCCEEDED(hr)) { 537 hr = pVssAdmin->UnregisterProvider(g_gProviderId); 538 } else { 539 errmsg(hr, "CoCreateInstance(VSSCoordinator) failed"); 540 } 541 542 sprintf(key, "CLSID\\%s", g_szClsid); 543 SHDeleteKey(HKEY_CLASSES_ROOT, key); 544 SHDeleteKey(HKEY_CLASSES_ROOT, g_szProgid); 545 546 qga_debug_end; 547 return S_OK; /* Uninstall should never fail */ 548 } 549 550 551 /* Support function to convert ASCII string into BSTR (used in _bstr_t) */ 552 namespace _com_util 553 { 554 BSTR WINAPI ConvertStringToBSTR(const char *ascii) { 555 int len = strlen(ascii); 556 BSTR bstr = SysAllocStringLen(NULL, len); 557 558 if (!bstr) { 559 return NULL; 560 } 561 562 if (mbstowcs(bstr, ascii, len) == (size_t)-1) { 563 qga_debug("Failed to convert string '%s' into BSTR", ascii); 564 bstr[0] = 0; 565 } 566 return bstr; 567 } 568 } 569 570 /* Stop QGA VSS provider service using Winsvc API */ 571 STDAPI StopService(void) 572 { 573 qga_debug_begin; 574 575 HRESULT hr = S_OK; 576 SC_HANDLE manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); 577 SC_HANDLE service = NULL; 578 579 if (!manager) { 580 errmsg(E_FAIL, "Failed to open service manager"); 581 hr = E_FAIL; 582 goto out; 583 } 584 service = OpenService(manager, QGA_PROVIDER_NAME, SC_MANAGER_ALL_ACCESS); 585 586 if (!service) { 587 errmsg(E_FAIL, "Failed to open service"); 588 hr = E_FAIL; 589 goto out; 590 } 591 if (!(ControlService(service, SERVICE_CONTROL_STOP, NULL))) { 592 errmsg(E_FAIL, "Failed to stop service"); 593 hr = E_FAIL; 594 } 595 596 out: 597 CloseServiceHandle(service); 598 CloseServiceHandle(manager); 599 qga_debug_end; 600 return hr; 601 } 602