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