install.cpp 15 KB

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