xref: /qemu/qga/vss-win32/install.cpp (revision 700784bfc65496551e15ccebff5ac30b6d13f492)
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  static 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      qga_debug_begin;
104  
105      HRESULT hr;
106      COMPointer<IWbemLocator> pLoc;
107      COMPointer<IWbemServices> pSvc;
108      COMPointer<IEnumWbemClassObject> pEnum;
109      COMPointer<IWbemClassObject> pWobj;
110      ULONG returned;
111      _variant_t var;
112  
113      chk(CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER,
114                           IID_IWbemLocator, (LPVOID *)pLoc.replace()));
115      chk(pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, NULL,
116                              0, 0, 0, pSvc.replace()));
117      chk(CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
118                            NULL, RPC_C_AUTHN_LEVEL_CALL,
119                            RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE));
120      chk(pSvc->ExecQuery(_bstr_t(L"WQL"),
121                          _bstr_t(L"select * from Win32_Account where "
122                                  "SID='S-1-5-32-544' and localAccount=TRUE"),
123                          WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY,
124                          NULL, pEnum.replace()));
125      if (!pEnum) {
126          hr = E_FAIL;
127          errmsg(hr, "Failed to query for Administrators");
128          goto out;
129      }
130      chk(pEnum->Next(WBEM_INFINITE, 1, pWobj.replace(), &returned));
131      if (returned == 0) {
132          hr = E_FAIL;
133          errmsg(hr, "No Administrators found");
134          goto out;
135      }
136  
137      chk(pWobj->Get(_bstr_t(L"Name"), 0, &var, 0, 0));
138      try {
139          *name = var;
140      } catch(...) {
141          hr = E_FAIL;
142          errmsg(hr, "Failed to get name of Administrators");
143          goto out;
144      }
145  
146  out:
147      qga_debug_end;
148      return hr;
149  }
150  
151  /* Acquire group or user name by SID */
152  static HRESULT getNameByStringSID(
153      const wchar_t *sid, LPWSTR buffer, LPDWORD bufferLen)
154  {
155      qga_debug_begin;
156  
157      HRESULT hr = S_OK;
158      PSID psid = NULL;
159      SID_NAME_USE groupType;
160      DWORD domainNameLen = BUFFER_SIZE;
161      wchar_t domainName[BUFFER_SIZE];
162  
163      if (!ConvertStringSidToSidW(sid, &psid)) {
164          hr = HRESULT_FROM_WIN32(GetLastError());
165          goto out;
166      }
167      if (!LookupAccountSidW(NULL, psid, buffer, bufferLen,
168                             domainName, &domainNameLen, &groupType)) {
169          hr = HRESULT_FROM_WIN32(GetLastError());
170          /* Fall through and free psid */
171      }
172  
173      LocalFree(psid);
174  
175  out:
176      qga_debug_end;
177      return hr;
178  }
179  
180  /* Find and iterate QGA VSS provider in COM+ Application Catalog */
181  static HRESULT QGAProviderFind(
182      HRESULT (*found)(ICatalogCollection *, int, void *), void *arg)
183  {
184      qga_debug_begin;
185  
186      HRESULT hr;
187      COMInitializer initializer;
188      COMPointer<IUnknown> pUnknown;
189      COMPointer<ICOMAdminCatalog2> pCatalog;
190      COMPointer<ICatalogCollection> pColl;
191      COMPointer<ICatalogObject> pObj;
192      _variant_t var;
193      long i, n;
194  
195      chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
196                           IID_IUnknown, (void **)pUnknown.replace()));
197      chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2,
198                                   (void **)pCatalog.replace()));
199      chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
200                                  (IDispatch **)pColl.replace()));
201      chk(pColl->Populate());
202  
203      chk(pColl->get_Count(&n));
204      for (i = n - 1; i >= 0; i--) {
205          chk(pColl->get_Item(i, (IDispatch **)pObj.replace()));
206          chk(pObj->get_Value(_bstr_t(L"Name"), &var));
207          if (var == _variant_t(QGA_PROVIDER_LNAME)) {
208              if (FAILED(found(pColl, i, arg))) {
209                  goto out;
210              }
211          }
212      }
213      chk(pColl->SaveChanges(&n));
214  
215  out:
216      qga_debug_end;
217      return hr;
218  }
219  
220  /* Count QGA VSS provider in COM+ Application Catalog */
221  static HRESULT QGAProviderCount(ICatalogCollection *coll, int i, void *arg)
222  {
223      qga_debug_begin;
224  
225      (*(int *)arg)++;
226  
227      qga_debug_end;
228      return S_OK;
229  }
230  
231  /* Remove QGA VSS provider from COM+ Application Catalog Collection */
232  static HRESULT QGAProviderRemove(ICatalogCollection *coll, int i, void *arg)
233  {
234      qga_debug_begin;
235      HRESULT hr;
236  
237      qga_debug("Removing COM+ Application: %s", QGA_PROVIDER_NAME);
238      chk(coll->Remove(i));
239  out:
240      qga_debug_end;
241      return hr;
242  }
243  
244  /* Unregister this module from COM+ Applications Catalog */
245  STDAPI COMUnregister(void);
246  STDAPI COMUnregister(void)
247  {
248      qga_debug_begin;
249  
250      HRESULT hr;
251  
252      DllUnregisterServer();
253      chk(QGAProviderFind(QGAProviderRemove, NULL));
254  out:
255      qga_debug_end;
256      return hr;
257  }
258  
259  /* Register this module to COM+ Applications Catalog */
260  STDAPI COMRegister(void);
261  STDAPI COMRegister(void)
262  {
263      qga_debug_begin;
264  
265      HRESULT hr;
266      COMInitializer initializer;
267      COMPointer<IUnknown> pUnknown;
268      COMPointer<ICOMAdminCatalog2> pCatalog;
269      COMPointer<ICatalogCollection> pApps, pRoles, pUsersInRole;
270      COMPointer<ICatalogObject> pObj;
271      long n;
272      _bstr_t name;
273      _variant_t key;
274      CHAR dllPath[MAX_PATH], tlbPath[MAX_PATH];
275      bool unregisterOnFailure = false;
276      int count = 0;
277      DWORD bufferLen = BUFFER_SIZE;
278      wchar_t buffer[BUFFER_SIZE];
279      const wchar_t *administratorsGroupSID = L"S-1-5-32-544";
280      const wchar_t *systemUserSID = L"S-1-5-18";
281  
282      if (!g_hinstDll) {
283          errmsg(E_FAIL, "Failed to initialize DLL");
284          qga_debug_end;
285          return E_FAIL;
286      }
287  
288      chk(QGAProviderFind(QGAProviderCount, (void *)&count));
289      if (count) {
290          errmsg(E_ABORT, "QGA VSS Provider is already installed");
291          qga_debug_end;
292          return E_ABORT;
293      }
294  
295      chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
296                           IID_IUnknown, (void **)pUnknown.replace()));
297      chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2,
298                                   (void **)pCatalog.replace()));
299  
300      /* Install COM+ Component */
301  
302      chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
303                                  (IDispatch **)pApps.replace()));
304      chk(pApps->Populate());
305      chk(pApps->Add((IDispatch **)&pObj));
306      chk(put_Value(pObj, L"Name",        QGA_PROVIDER_LNAME));
307      chk(put_Value(pObj, L"Description", QGA_PROVIDER_LNAME));
308      chk(put_Value(pObj, L"ApplicationAccessChecksEnabled", true));
309      chk(put_Value(pObj, L"Authentication",                 short(6)));
310      chk(put_Value(pObj, L"AuthenticationCapability",       short(2)));
311      chk(put_Value(pObj, L"ImpersonationLevel",             short(2)));
312      chk(pApps->SaveChanges(&n));
313  
314      /* The app should be deleted if something fails after SaveChanges */
315      unregisterOnFailure = true;
316  
317      chk(pObj->get_Key(&key));
318  
319      if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) {
320          hr = HRESULT_FROM_WIN32(GetLastError());
321          errmsg(hr, "GetModuleFileName failed");
322          goto out;
323      }
324      n = strlen(dllPath);
325      if (n < 3) {
326          hr = E_FAIL;
327          errmsg(hr, "Failed to lookup dll");
328          goto out;
329      }
330      strcpy(tlbPath, dllPath);
331      strcpy(tlbPath+n-3, "tlb");
332      qga_debug("Registering " QGA_PROVIDER_NAME ": %s %s",
333                dllPath, tlbPath);
334      if (!PathFileExists(tlbPath)) {
335          hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
336          errmsg(hr, "Failed to lookup tlb");
337          goto out;
338      }
339  
340      chk(pCatalog->CreateServiceForApplication(
341              _bstr_t(QGA_PROVIDER_LNAME), _bstr_t(QGA_PROVIDER_LNAME),
342              _bstr_t(L"SERVICE_DEMAND_START"), _bstr_t(L"SERVICE_ERROR_NORMAL"),
343              _bstr_t(L""), _bstr_t(L".\\localsystem"), _bstr_t(L""), FALSE));
344      chk(pCatalog->InstallComponent(_bstr_t(QGA_PROVIDER_LNAME),
345                                     _bstr_t(dllPath), _bstr_t(tlbPath),
346                                     _bstr_t("")));
347  
348      /* Setup roles of the application */
349  
350      chk(getNameByStringSID(administratorsGroupSID, buffer, &bufferLen));
351      chk(pApps->GetCollection(_bstr_t(L"Roles"), key,
352                               (IDispatch **)pRoles.replace()));
353      chk(pRoles->Populate());
354      chk(pRoles->Add((IDispatch **)pObj.replace()));
355      chk(put_Value(pObj, L"Name", buffer));
356      chk(put_Value(pObj, L"Description", L"Administrators group"));
357      chk(pRoles->SaveChanges(&n));
358      chk(pObj->get_Key(&key));
359  
360      /* Setup users in the role */
361  
362      chk(pRoles->GetCollection(_bstr_t(L"UsersInRole"), key,
363                                (IDispatch **)pUsersInRole.replace()));
364      chk(pUsersInRole->Populate());
365  
366      chk(pUsersInRole->Add((IDispatch **)pObj.replace()));
367      chk(GetAdminName(&name));
368      chk(put_Value(pObj, L"User", _bstr_t(".\\") + name));
369  
370      bufferLen = BUFFER_SIZE;
371      chk(getNameByStringSID(systemUserSID, buffer, &bufferLen));
372      chk(pUsersInRole->Add((IDispatch **)pObj.replace()));
373      chk(put_Value(pObj, L"User", buffer));
374      chk(pUsersInRole->SaveChanges(&n));
375  
376  out:
377      if (unregisterOnFailure && FAILED(hr)) {
378          COMUnregister();
379      }
380  
381      qga_debug_end;
382      return hr;
383  }
384  
385  STDAPI_(void) CALLBACK DLLCOMRegister(HWND, HINSTANCE, LPSTR, int);
386  STDAPI_(void) CALLBACK DLLCOMRegister(HWND, HINSTANCE, LPSTR, int)
387  {
388      COMRegister();
389  }
390  
391  STDAPI_(void) CALLBACK DLLCOMUnregister(HWND, HINSTANCE, LPSTR, int);
392  STDAPI_(void) CALLBACK DLLCOMUnregister(HWND, HINSTANCE, LPSTR, int)
393  {
394      COMUnregister();
395  }
396  
397  static BOOL CreateRegistryKey(LPCTSTR key, LPCTSTR value, LPCTSTR data)
398  {
399      qga_debug_begin;
400  
401      HKEY  hKey;
402      LONG  ret;
403      DWORD size;
404  
405      ret = RegCreateKeyEx(HKEY_CLASSES_ROOT, key, 0, NULL,
406          REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL);
407      if (ret != ERROR_SUCCESS) {
408          goto out;
409      }
410  
411      if (data != NULL) {
412          size = strlen(data) + 1;
413      } else {
414          size = 0;
415      }
416  
417      ret = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, size);
418      RegCloseKey(hKey);
419  
420  out:
421      qga_debug_end;
422      if (ret != ERROR_SUCCESS) {
423          /* As we cannot printf within DllRegisterServer(), show a dialog. */
424          errmsg_dialog(ret, "Cannot add registry", key);
425          return FALSE;
426      }
427      return TRUE;
428  }
429  
430  /* Register this dll as a VSS provider */
431  STDAPI DllRegisterServer(void)
432  {
433      qga_debug_begin;
434  
435      COMInitializer initializer;
436      COMPointer<IVssAdmin> pVssAdmin;
437      HRESULT hr = E_FAIL;
438      char dllPath[MAX_PATH];
439      char key[256];
440  
441      if (!g_hinstDll) {
442          errmsg_dialog(hr, "Module instance is not available");
443          goto out;
444      }
445  
446      /* Add this module to registry */
447  
448      sprintf(key, "CLSID\\%s", g_szClsid);
449      if (!CreateRegistryKey(key, NULL, g_szClsid)) {
450          goto out;
451      }
452  
453      if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) {
454          errmsg_dialog(GetLastError(), "GetModuleFileName failed");
455          goto out;
456      }
457  
458      sprintf(key, "CLSID\\%s\\InprocServer32", g_szClsid);
459      if (!CreateRegistryKey(key, NULL, dllPath)) {
460          goto out;
461      }
462  
463      if (!CreateRegistryKey(key, "ThreadingModel", "Apartment")) {
464          goto out;
465      }
466  
467      sprintf(key, "CLSID\\%s\\ProgID", g_szClsid);
468      if (!CreateRegistryKey(key, NULL, g_szProgid)) {
469          goto out;
470      }
471  
472      if (!CreateRegistryKey(g_szProgid, NULL, QGA_PROVIDER_NAME)) {
473          goto out;
474      }
475  
476      sprintf(key, "%s\\CLSID", g_szProgid);
477      if (!CreateRegistryKey(key, NULL, g_szClsid)) {
478          goto out;
479      }
480  
481      hr = CoCreateInstance(CLSID_VSSCoordinator, NULL, CLSCTX_ALL,
482                            IID_IVssAdmin, (void **)pVssAdmin.replace());
483      if (FAILED(hr)) {
484          errmsg_dialog(hr, "CoCreateInstance(VSSCoordinator) failed");
485          goto out;
486      }
487  
488      hr = pVssAdmin->RegisterProvider(g_gProviderId, CLSID_QGAVSSProvider,
489                                       const_cast<WCHAR*>(QGA_PROVIDER_LNAME),
490                                       VSS_PROV_SOFTWARE,
491                                       const_cast<WCHAR*>(QGA_PROVIDER_VERSION),
492                                       g_gProviderVersion);
493      if (hr == (long int) VSS_E_PROVIDER_ALREADY_REGISTERED) {
494          DllUnregisterServer();
495          hr = pVssAdmin->RegisterProvider(g_gProviderId, CLSID_QGAVSSProvider,
496                                           const_cast<WCHAR * >
497                                           (QGA_PROVIDER_LNAME),
498                                           VSS_PROV_SOFTWARE,
499                                           const_cast<WCHAR * >
500                                           (QGA_PROVIDER_VERSION),
501                                           g_gProviderVersion);
502      }
503  
504      if (FAILED(hr)) {
505          errmsg_dialog(hr, "RegisterProvider failed");
506      }
507  
508  out:
509      if (FAILED(hr)) {
510          DllUnregisterServer();
511      }
512  
513      qga_debug_end;
514      return hr;
515  }
516  
517  /* Unregister this VSS hardware provider from the system */
518  STDAPI DllUnregisterServer(void)
519  {
520      qga_debug_begin;
521  
522      TCHAR key[256];
523      COMInitializer initializer;
524      COMPointer<IVssAdmin> pVssAdmin;
525  
526      HRESULT hr = CoCreateInstance(CLSID_VSSCoordinator,
527                                    NULL, CLSCTX_ALL, IID_IVssAdmin,
528                                    (void **)pVssAdmin.replace());
529      if (SUCCEEDED(hr)) {
530          hr = pVssAdmin->UnregisterProvider(g_gProviderId);
531      } else {
532          errmsg(hr, "CoCreateInstance(VSSCoordinator) failed");
533      }
534  
535      sprintf(key, "CLSID\\%s", g_szClsid);
536      SHDeleteKey(HKEY_CLASSES_ROOT, key);
537      SHDeleteKey(HKEY_CLASSES_ROOT, g_szProgid);
538  
539      qga_debug_end;
540      return S_OK; /* Uninstall should never fail */
541  }
542  
543  
544  /* Support function to convert ASCII string into BSTR (used in _bstr_t) */
545  namespace _com_util
546  {
547      BSTR WINAPI ConvertStringToBSTR(const char *ascii) {
548          int len = strlen(ascii);
549          BSTR bstr = SysAllocStringLen(NULL, len);
550  
551          if (!bstr) {
552              return NULL;
553          }
554  
555          if (mbstowcs(bstr, ascii, len) == (size_t)-1) {
556              qga_debug("Failed to convert string '%s' into BSTR", ascii);
557              bstr[0] = 0;
558          }
559          return bstr;
560      }
561  }
562  
563  /* Stop QGA VSS provider service using Winsvc API  */
564  STDAPI StopService(void)
565  {
566      qga_debug_begin;
567  
568      HRESULT hr = S_OK;
569      SC_HANDLE manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
570      SC_HANDLE service = NULL;
571  
572      if (!manager) {
573          errmsg(E_FAIL, "Failed to open service manager");
574          hr = E_FAIL;
575          goto out;
576      }
577      service = OpenService(manager, QGA_PROVIDER_NAME, SC_MANAGER_ALL_ACCESS);
578  
579      if (!service) {
580          errmsg(E_FAIL, "Failed to open service");
581          hr =  E_FAIL;
582          goto out;
583      }
584      if (!(ControlService(service, SERVICE_CONTROL_STOP, NULL))) {
585          errmsg(E_FAIL, "Failed to stop service");
586          hr = E_FAIL;
587      }
588  
589  out:
590      CloseServiceHandle(service);
591      CloseServiceHandle(manager);
592      qga_debug_end;
593      return hr;
594  }
595