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 HRESULT hr; 104 COMPointer<IWbemLocator> pLoc; 105 COMPointer<IWbemServices> pSvc; 106 COMPointer<IEnumWbemClassObject> pEnum; 107 COMPointer<IWbemClassObject> pWobj; 108 ULONG returned; 109 _variant_t var; 110 111 chk(CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, 112 IID_IWbemLocator, (LPVOID *)pLoc.replace())); 113 chk(pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, NULL, 114 0, 0, 0, pSvc.replace())); 115 chk(CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, 116 NULL, RPC_C_AUTHN_LEVEL_CALL, 117 RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE)); 118 chk(pSvc->ExecQuery(_bstr_t(L"WQL"), 119 _bstr_t(L"select * from Win32_Account where " 120 "SID='S-1-5-32-544' and localAccount=TRUE"), 121 WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, 122 NULL, pEnum.replace())); 123 if (!pEnum) { 124 hr = E_FAIL; 125 errmsg(hr, "Failed to query for Administrators"); 126 goto out; 127 } 128 chk(pEnum->Next(WBEM_INFINITE, 1, pWobj.replace(), &returned)); 129 if (returned == 0) { 130 hr = E_FAIL; 131 errmsg(hr, "No Administrators found"); 132 goto out; 133 } 134 135 chk(pWobj->Get(_bstr_t(L"Name"), 0, &var, 0, 0)); 136 try { 137 *name = var; 138 } catch(...) { 139 hr = E_FAIL; 140 errmsg(hr, "Failed to get name of Administrators"); 141 goto out; 142 } 143 144 out: 145 return hr; 146 } 147 148 /* Acquire group or user name by SID */ 149 static HRESULT getNameByStringSID( 150 const wchar_t *sid, LPWSTR buffer, LPDWORD bufferLen) 151 { 152 HRESULT hr = S_OK; 153 PSID psid = NULL; 154 SID_NAME_USE groupType; 155 DWORD domainNameLen = BUFFER_SIZE; 156 wchar_t domainName[BUFFER_SIZE]; 157 158 if (!ConvertStringSidToSidW(sid, &psid)) { 159 hr = HRESULT_FROM_WIN32(GetLastError()); 160 goto out; 161 } 162 if (!LookupAccountSidW(NULL, psid, buffer, bufferLen, 163 domainName, &domainNameLen, &groupType)) { 164 hr = HRESULT_FROM_WIN32(GetLastError()); 165 /* Fall through and free psid */ 166 } 167 168 LocalFree(psid); 169 170 out: 171 return hr; 172 } 173 174 /* Find and iterate QGA VSS provider in COM+ Application Catalog */ 175 static HRESULT QGAProviderFind( 176 HRESULT (*found)(ICatalogCollection *, int, void *), void *arg) 177 { 178 HRESULT hr; 179 COMInitializer initializer; 180 COMPointer<IUnknown> pUnknown; 181 COMPointer<ICOMAdminCatalog2> pCatalog; 182 COMPointer<ICatalogCollection> pColl; 183 COMPointer<ICatalogObject> pObj; 184 _variant_t var; 185 long i, n; 186 187 chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER, 188 IID_IUnknown, (void **)pUnknown.replace())); 189 chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2, 190 (void **)pCatalog.replace())); 191 chk(pCatalog->GetCollection(_bstr_t(L"Applications"), 192 (IDispatch **)pColl.replace())); 193 chk(pColl->Populate()); 194 195 chk(pColl->get_Count(&n)); 196 for (i = n - 1; i >= 0; i--) { 197 chk(pColl->get_Item(i, (IDispatch **)pObj.replace())); 198 chk(pObj->get_Value(_bstr_t(L"Name"), &var)); 199 if (var == _variant_t(QGA_PROVIDER_LNAME)) { 200 if (FAILED(found(pColl, i, arg))) { 201 goto out; 202 } 203 } 204 } 205 chk(pColl->SaveChanges(&n)); 206 207 out: 208 return hr; 209 } 210 211 /* Count QGA VSS provider in COM+ Application Catalog */ 212 static HRESULT QGAProviderCount(ICatalogCollection *coll, int i, void *arg) 213 { 214 (*(int *)arg)++; 215 return S_OK; 216 } 217 218 /* Remove QGA VSS provider from COM+ Application Catalog Collection */ 219 static HRESULT QGAProviderRemove(ICatalogCollection *coll, int i, void *arg) 220 { 221 HRESULT hr; 222 223 qga_debug("Removing COM+ Application: %s", QGA_PROVIDER_NAME); 224 chk(coll->Remove(i)); 225 out: 226 return hr; 227 } 228 229 /* Unregister this module from COM+ Applications Catalog */ 230 STDAPI COMUnregister(void) 231 { 232 HRESULT hr; 233 234 DllUnregisterServer(); 235 chk(QGAProviderFind(QGAProviderRemove, NULL)); 236 out: 237 return hr; 238 } 239 240 /* Register this module to COM+ Applications Catalog */ 241 STDAPI COMRegister(void) 242 { 243 HRESULT hr; 244 COMInitializer initializer; 245 COMPointer<IUnknown> pUnknown; 246 COMPointer<ICOMAdminCatalog2> pCatalog; 247 COMPointer<ICatalogCollection> pApps, pRoles, pUsersInRole; 248 COMPointer<ICatalogObject> pObj; 249 long n; 250 _bstr_t name; 251 _variant_t key; 252 CHAR dllPath[MAX_PATH], tlbPath[MAX_PATH]; 253 bool unregisterOnFailure = false; 254 int count = 0; 255 DWORD bufferLen = BUFFER_SIZE; 256 wchar_t buffer[BUFFER_SIZE]; 257 const wchar_t *administratorsGroupSID = L"S-1-5-32-544"; 258 const wchar_t *systemUserSID = L"S-1-5-18"; 259 260 if (!g_hinstDll) { 261 errmsg(E_FAIL, "Failed to initialize DLL"); 262 return E_FAIL; 263 } 264 265 chk(QGAProviderFind(QGAProviderCount, (void *)&count)); 266 if (count) { 267 errmsg(E_ABORT, "QGA VSS Provider is already installed"); 268 return E_ABORT; 269 } 270 271 chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER, 272 IID_IUnknown, (void **)pUnknown.replace())); 273 chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2, 274 (void **)pCatalog.replace())); 275 276 /* Install COM+ Component */ 277 278 chk(pCatalog->GetCollection(_bstr_t(L"Applications"), 279 (IDispatch **)pApps.replace())); 280 chk(pApps->Populate()); 281 chk(pApps->Add((IDispatch **)&pObj)); 282 chk(put_Value(pObj, L"Name", QGA_PROVIDER_LNAME)); 283 chk(put_Value(pObj, L"Description", QGA_PROVIDER_LNAME)); 284 chk(put_Value(pObj, L"ApplicationAccessChecksEnabled", true)); 285 chk(put_Value(pObj, L"Authentication", short(6))); 286 chk(put_Value(pObj, L"AuthenticationCapability", short(2))); 287 chk(put_Value(pObj, L"ImpersonationLevel", short(2))); 288 chk(pApps->SaveChanges(&n)); 289 290 /* The app should be deleted if something fails after SaveChanges */ 291 unregisterOnFailure = true; 292 293 chk(pObj->get_Key(&key)); 294 295 if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) { 296 hr = HRESULT_FROM_WIN32(GetLastError()); 297 errmsg(hr, "GetModuleFileName failed"); 298 goto out; 299 } 300 n = strlen(dllPath); 301 if (n < 3) { 302 hr = E_FAIL; 303 errmsg(hr, "Failed to lookup dll"); 304 goto out; 305 } 306 strcpy(tlbPath, dllPath); 307 strcpy(tlbPath+n-3, "tlb"); 308 qga_debug("Registering " QGA_PROVIDER_NAME ": %s %s", 309 dllPath, 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 qga_debug("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 = S_OK; 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