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