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 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 { 247 qga_debug_begin; 248 249 HRESULT hr; 250 251 DllUnregisterServer(); 252 chk(QGAProviderFind(QGAProviderRemove, NULL)); 253 out: 254 qga_debug_end; 255 return hr; 256 } 257 258 /* Register this module to COM+ Applications Catalog */ 259 STDAPI COMRegister(void) 260 { 261 qga_debug_begin; 262 263 HRESULT hr; 264 COMInitializer initializer; 265 COMPointer<IUnknown> pUnknown; 266 COMPointer<ICOMAdminCatalog2> pCatalog; 267 COMPointer<ICatalogCollection> pApps, pRoles, pUsersInRole; 268 COMPointer<ICatalogObject> pObj; 269 long n; 270 _bstr_t name; 271 _variant_t key; 272 CHAR dllPath[MAX_PATH], tlbPath[MAX_PATH]; 273 bool unregisterOnFailure = false; 274 int count = 0; 275 DWORD bufferLen = BUFFER_SIZE; 276 wchar_t buffer[BUFFER_SIZE]; 277 const wchar_t *administratorsGroupSID = L"S-1-5-32-544"; 278 const wchar_t *systemUserSID = L"S-1-5-18"; 279 280 if (!g_hinstDll) { 281 errmsg(E_FAIL, "Failed to initialize DLL"); 282 qga_debug_end; 283 return E_FAIL; 284 } 285 286 chk(QGAProviderFind(QGAProviderCount, (void *)&count)); 287 if (count) { 288 errmsg(E_ABORT, "QGA VSS Provider is already installed"); 289 qga_debug_end; 290 return E_ABORT; 291 } 292 293 chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER, 294 IID_IUnknown, (void **)pUnknown.replace())); 295 chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2, 296 (void **)pCatalog.replace())); 297 298 /* Install COM+ Component */ 299 300 chk(pCatalog->GetCollection(_bstr_t(L"Applications"), 301 (IDispatch **)pApps.replace())); 302 chk(pApps->Populate()); 303 chk(pApps->Add((IDispatch **)&pObj)); 304 chk(put_Value(pObj, L"Name", QGA_PROVIDER_LNAME)); 305 chk(put_Value(pObj, L"Description", QGA_PROVIDER_LNAME)); 306 chk(put_Value(pObj, L"ApplicationAccessChecksEnabled", true)); 307 chk(put_Value(pObj, L"Authentication", short(6))); 308 chk(put_Value(pObj, L"AuthenticationCapability", short(2))); 309 chk(put_Value(pObj, L"ImpersonationLevel", short(2))); 310 chk(pApps->SaveChanges(&n)); 311 312 /* The app should be deleted if something fails after SaveChanges */ 313 unregisterOnFailure = true; 314 315 chk(pObj->get_Key(&key)); 316 317 if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) { 318 hr = HRESULT_FROM_WIN32(GetLastError()); 319 errmsg(hr, "GetModuleFileName failed"); 320 goto out; 321 } 322 n = strlen(dllPath); 323 if (n < 3) { 324 hr = E_FAIL; 325 errmsg(hr, "Failed to lookup dll"); 326 goto out; 327 } 328 strcpy(tlbPath, dllPath); 329 strcpy(tlbPath+n-3, "tlb"); 330 qga_debug("Registering " QGA_PROVIDER_NAME ": %s %s", 331 dllPath, tlbPath); 332 if (!PathFileExists(tlbPath)) { 333 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 334 errmsg(hr, "Failed to lookup tlb"); 335 goto out; 336 } 337 338 chk(pCatalog->CreateServiceForApplication( 339 _bstr_t(QGA_PROVIDER_LNAME), _bstr_t(QGA_PROVIDER_LNAME), 340 _bstr_t(L"SERVICE_DEMAND_START"), _bstr_t(L"SERVICE_ERROR_NORMAL"), 341 _bstr_t(L""), _bstr_t(L".\\localsystem"), _bstr_t(L""), FALSE)); 342 chk(pCatalog->InstallComponent(_bstr_t(QGA_PROVIDER_LNAME), 343 _bstr_t(dllPath), _bstr_t(tlbPath), 344 _bstr_t(""))); 345 346 /* Setup roles of the applicaion */ 347 348 chk(getNameByStringSID(administratorsGroupSID, buffer, &bufferLen)); 349 chk(pApps->GetCollection(_bstr_t(L"Roles"), key, 350 (IDispatch **)pRoles.replace())); 351 chk(pRoles->Populate()); 352 chk(pRoles->Add((IDispatch **)pObj.replace())); 353 chk(put_Value(pObj, L"Name", buffer)); 354 chk(put_Value(pObj, L"Description", L"Administrators group")); 355 chk(pRoles->SaveChanges(&n)); 356 chk(pObj->get_Key(&key)); 357 358 /* Setup users in the role */ 359 360 chk(pRoles->GetCollection(_bstr_t(L"UsersInRole"), key, 361 (IDispatch **)pUsersInRole.replace())); 362 chk(pUsersInRole->Populate()); 363 364 chk(pUsersInRole->Add((IDispatch **)pObj.replace())); 365 chk(GetAdminName(&name)); 366 chk(put_Value(pObj, L"User", _bstr_t(".\\") + name)); 367 368 bufferLen = BUFFER_SIZE; 369 chk(getNameByStringSID(systemUserSID, buffer, &bufferLen)); 370 chk(pUsersInRole->Add((IDispatch **)pObj.replace())); 371 chk(put_Value(pObj, L"User", buffer)); 372 chk(pUsersInRole->SaveChanges(&n)); 373 374 out: 375 if (unregisterOnFailure && FAILED(hr)) { 376 COMUnregister(); 377 } 378 379 qga_debug_end; 380 return hr; 381 } 382 383 STDAPI_(void) CALLBACK DLLCOMRegister(HWND, HINSTANCE, LPSTR, int) 384 { 385 COMRegister(); 386 } 387 388 STDAPI_(void) CALLBACK DLLCOMUnregister(HWND, HINSTANCE, LPSTR, int) 389 { 390 COMUnregister(); 391 } 392 393 static BOOL CreateRegistryKey(LPCTSTR key, LPCTSTR value, LPCTSTR data) 394 { 395 qga_debug_begin; 396 397 HKEY hKey; 398 LONG ret; 399 DWORD size; 400 401 ret = RegCreateKeyEx(HKEY_CLASSES_ROOT, key, 0, NULL, 402 REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL); 403 if (ret != ERROR_SUCCESS) { 404 goto out; 405 } 406 407 if (data != NULL) { 408 size = strlen(data) + 1; 409 } else { 410 size = 0; 411 } 412 413 ret = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, size); 414 RegCloseKey(hKey); 415 416 out: 417 qga_debug_end; 418 if (ret != ERROR_SUCCESS) { 419 /* As we cannot printf within DllRegisterServer(), show a dialog. */ 420 errmsg_dialog(ret, "Cannot add registry", key); 421 return FALSE; 422 } 423 return TRUE; 424 } 425 426 /* Register this dll as a VSS provider */ 427 STDAPI DllRegisterServer(void) 428 { 429 qga_debug_begin; 430 431 COMInitializer initializer; 432 COMPointer<IVssAdmin> pVssAdmin; 433 HRESULT hr = E_FAIL; 434 char dllPath[MAX_PATH]; 435 char key[256]; 436 437 if (!g_hinstDll) { 438 errmsg_dialog(hr, "Module instance is not available"); 439 goto out; 440 } 441 442 /* Add this module to registery */ 443 444 sprintf(key, "CLSID\\%s", g_szClsid); 445 if (!CreateRegistryKey(key, NULL, g_szClsid)) { 446 goto out; 447 } 448 449 if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) { 450 errmsg_dialog(GetLastError(), "GetModuleFileName failed"); 451 goto out; 452 } 453 454 sprintf(key, "CLSID\\%s\\InprocServer32", g_szClsid); 455 if (!CreateRegistryKey(key, NULL, dllPath)) { 456 goto out; 457 } 458 459 if (!CreateRegistryKey(key, "ThreadingModel", "Apartment")) { 460 goto out; 461 } 462 463 sprintf(key, "CLSID\\%s\\ProgID", g_szClsid); 464 if (!CreateRegistryKey(key, NULL, g_szProgid)) { 465 goto out; 466 } 467 468 if (!CreateRegistryKey(g_szProgid, NULL, QGA_PROVIDER_NAME)) { 469 goto out; 470 } 471 472 sprintf(key, "%s\\CLSID", g_szProgid); 473 if (!CreateRegistryKey(key, NULL, g_szClsid)) { 474 goto out; 475 } 476 477 hr = CoCreateInstance(CLSID_VSSCoordinator, NULL, CLSCTX_ALL, 478 IID_IVssAdmin, (void **)pVssAdmin.replace()); 479 if (FAILED(hr)) { 480 errmsg_dialog(hr, "CoCreateInstance(VSSCoordinator) failed"); 481 goto out; 482 } 483 484 hr = pVssAdmin->RegisterProvider(g_gProviderId, CLSID_QGAVSSProvider, 485 const_cast<WCHAR*>(QGA_PROVIDER_LNAME), 486 VSS_PROV_SOFTWARE, 487 const_cast<WCHAR*>(QGA_PROVIDER_VERSION), 488 g_gProviderVersion); 489 if (hr == (long int) VSS_E_PROVIDER_ALREADY_REGISTERED) { 490 DllUnregisterServer(); 491 hr = pVssAdmin->RegisterProvider(g_gProviderId, CLSID_QGAVSSProvider, 492 const_cast<WCHAR * > 493 (QGA_PROVIDER_LNAME), 494 VSS_PROV_SOFTWARE, 495 const_cast<WCHAR * > 496 (QGA_PROVIDER_VERSION), 497 g_gProviderVersion); 498 } 499 500 if (FAILED(hr)) { 501 errmsg_dialog(hr, "RegisterProvider failed"); 502 } 503 504 out: 505 if (FAILED(hr)) { 506 DllUnregisterServer(); 507 } 508 509 qga_debug_end; 510 return hr; 511 } 512 513 /* Unregister this VSS hardware provider from the system */ 514 STDAPI DllUnregisterServer(void) 515 { 516 qga_debug_begin; 517 518 TCHAR key[256]; 519 COMInitializer initializer; 520 COMPointer<IVssAdmin> pVssAdmin; 521 522 HRESULT hr = CoCreateInstance(CLSID_VSSCoordinator, 523 NULL, CLSCTX_ALL, IID_IVssAdmin, 524 (void **)pVssAdmin.replace()); 525 if (SUCCEEDED(hr)) { 526 hr = pVssAdmin->UnregisterProvider(g_gProviderId); 527 } else { 528 errmsg(hr, "CoCreateInstance(VSSCoordinator) failed"); 529 } 530 531 sprintf(key, "CLSID\\%s", g_szClsid); 532 SHDeleteKey(HKEY_CLASSES_ROOT, key); 533 SHDeleteKey(HKEY_CLASSES_ROOT, g_szProgid); 534 535 qga_debug_end; 536 return S_OK; /* Uninstall should never fail */ 537 } 538 539 540 /* Support function to convert ASCII string into BSTR (used in _bstr_t) */ 541 namespace _com_util 542 { 543 BSTR WINAPI ConvertStringToBSTR(const char *ascii) { 544 int len = strlen(ascii); 545 BSTR bstr = SysAllocStringLen(NULL, len); 546 547 if (!bstr) { 548 return NULL; 549 } 550 551 if (mbstowcs(bstr, ascii, len) == (size_t)-1) { 552 qga_debug("Failed to convert string '%s' into BSTR", ascii); 553 bstr[0] = 0; 554 } 555 return bstr; 556 } 557 } 558 559 /* Stop QGA VSS provider service using Winsvc API */ 560 STDAPI StopService(void) 561 { 562 qga_debug_begin; 563 564 HRESULT hr = S_OK; 565 SC_HANDLE manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); 566 SC_HANDLE service = NULL; 567 568 if (!manager) { 569 errmsg(E_FAIL, "Failed to open service manager"); 570 hr = E_FAIL; 571 goto out; 572 } 573 service = OpenService(manager, QGA_PROVIDER_NAME, SC_MANAGER_ALL_ACCESS); 574 575 if (!service) { 576 errmsg(E_FAIL, "Failed to open service"); 577 hr = E_FAIL; 578 goto out; 579 } 580 if (!(ControlService(service, SERVICE_CONTROL_STOP, NULL))) { 581 errmsg(E_FAIL, "Failed to stop service"); 582 hr = E_FAIL; 583 } 584 585 out: 586 CloseServiceHandle(service); 587 CloseServiceHandle(manager); 588 qga_debug_end; 589 return hr; 590 } 591