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