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