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