FirewallRules.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. // https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ics/c-adding-an-application-rule-edge-traversal
  2. /********************************************************************
  3. Copyright (C) Microsoft. All Rights Reserved.
  4. Abstract:
  5. This C++ file includes sample code that adds a firewall rule with
  6. EdgeTraversalOptions (one of the EdgeTraversalOptions values).
  7. ********************************************************************/
  8. #include "pch.h"
  9. #include <windows.h>
  10. #include <stdio.h>
  11. #include <netfw.h>
  12. #include <strsafe.h>
  13. #pragma comment(lib, "ole32.lib")
  14. #pragma comment(lib, "oleaut32.lib")
  15. #define STRING_BUFFER_SIZE 500
  16. // Forward declarations
  17. HRESULT WFCOMInitialize(INetFwPolicy2** ppNetFwPolicy2);
  18. void WFCOMCleanup(INetFwPolicy2* pNetFwPolicy2);
  19. HRESULT RemoveFirewallRule(
  20. __in INetFwPolicy2* pNetFwPolicy2,
  21. __in LPWSTR exeName);
  22. HRESULT AddFirewallRuleWithEdgeTraversal(__in INetFwPolicy2* pNetFwPolicy2,
  23. __in bool in,
  24. __in LPWSTR exeName,
  25. __in LPWSTR exeFile);
  26. bool AddFirewallRule(bool add, LPWSTR exeName, LPWSTR exeFile)
  27. {
  28. bool result = false;
  29. HRESULT hrComInit = S_OK;
  30. HRESULT hr = S_OK;
  31. INetFwPolicy2* pNetFwPolicy2 = NULL;
  32. // Initialize COM.
  33. hrComInit = CoInitializeEx(
  34. 0,
  35. COINIT_APARTMENTTHREADED
  36. );
  37. // Ignore RPC_E_CHANGED_MODE; this just means that COM has already been
  38. // initialized with a different mode. Since we don't care what the mode is,
  39. // we'll just use the existing mode.
  40. if (hrComInit != RPC_E_CHANGED_MODE)
  41. {
  42. if (FAILED(hrComInit))
  43. {
  44. WcaLog(LOGMSG_STANDARD, "CoInitializeEx failed: 0x%08lx\n", hrComInit);
  45. goto Cleanup;
  46. }
  47. }
  48. // Retrieve INetFwPolicy2
  49. hr = WFCOMInitialize(&pNetFwPolicy2);
  50. if (FAILED(hr))
  51. {
  52. goto Cleanup;
  53. }
  54. if (add) {
  55. // Add firewall rule with EdgeTraversalOption=DeferApp (Windows7+) if available
  56. // else add with Edge=True (Vista and Server 2008).
  57. hr = AddFirewallRuleWithEdgeTraversal(pNetFwPolicy2, true, exeName, exeFile);
  58. hr = AddFirewallRuleWithEdgeTraversal(pNetFwPolicy2, false, exeName, exeFile);
  59. }
  60. else {
  61. hr = RemoveFirewallRule(pNetFwPolicy2, exeName);
  62. }
  63. result = SUCCEEDED(hr);
  64. Cleanup:
  65. // Release INetFwPolicy2
  66. WFCOMCleanup(pNetFwPolicy2);
  67. // Uninitialize COM.
  68. if (SUCCEEDED(hrComInit))
  69. {
  70. CoUninitialize();
  71. }
  72. return result;
  73. }
  74. BSTR MakeRuleName(__in LPWSTR exeName)
  75. {
  76. WCHAR pwszTemp[STRING_BUFFER_SIZE] = L"";
  77. HRESULT hr = StringCchPrintfW(pwszTemp, STRING_BUFFER_SIZE, L"%ls Service", exeName);
  78. if (FAILED(hr))
  79. {
  80. WcaLog(LOGMSG_STANDARD, "Failed to compose a resource identifier string: 0x%08lx\n", hr);
  81. return NULL;
  82. }
  83. return SysAllocString(pwszTemp);
  84. }
  85. HRESULT RemoveFirewallRule(
  86. __in INetFwPolicy2* pNetFwPolicy2,
  87. __in LPWSTR exeName)
  88. {
  89. HRESULT hr = S_OK;
  90. INetFwRules* pNetFwRules = NULL;
  91. WCHAR pwszTemp[STRING_BUFFER_SIZE] = L"";
  92. BSTR RuleName = NULL;
  93. RuleName = MakeRuleName(exeName);
  94. if (NULL == RuleName)
  95. {
  96. WcaLog(LOGMSG_STANDARD, "\nERROR: Insufficient memory\n");
  97. goto Cleanup;
  98. }
  99. hr = pNetFwPolicy2->get_Rules(&pNetFwRules);
  100. if (FAILED(hr))
  101. {
  102. WcaLog(LOGMSG_STANDARD, "Failed to retrieve firewall rules collection : 0x%08lx\n", hr);
  103. goto Cleanup;
  104. }
  105. // We need to "Remove()" twice, because both "in" and "out" rules are added?
  106. // There's no remarks for this case https://learn.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-inetfwrules-remove
  107. hr = pNetFwRules->Remove(RuleName);
  108. hr = pNetFwRules->Remove(RuleName);
  109. if (FAILED(hr)) {
  110. WcaLog(LOGMSG_STANDARD, "Failed to remove firewall rule \"%ls\" : 0x%08lx\n", exeName, hr);
  111. }
  112. else {
  113. WcaLog(LOGMSG_STANDARD, "Firewall rule \"%ls\" is removed\n", exeName);
  114. }
  115. Cleanup:
  116. SysFreeString(RuleName);
  117. if (pNetFwRules != NULL)
  118. {
  119. pNetFwRules->Release();
  120. }
  121. return hr;
  122. }
  123. // Add firewall rule with EdgeTraversalOption=DeferApp (Windows7+) if available
  124. // else add with Edge=True (Vista and Server 2008).
  125. HRESULT AddFirewallRuleWithEdgeTraversal(
  126. __in INetFwPolicy2* pNetFwPolicy2,
  127. __in bool in,
  128. __in LPWSTR exeName,
  129. __in LPWSTR exeFile)
  130. {
  131. HRESULT hr = S_OK;
  132. INetFwRules* pNetFwRules = NULL;
  133. INetFwRule* pNetFwRule = NULL;
  134. INetFwRule2* pNetFwRule2 = NULL;
  135. WCHAR pwszTemp[STRING_BUFFER_SIZE] = L"";
  136. BSTR RuleName = NULL;
  137. BSTR RuleGroupName = NULL;
  138. BSTR RuleDescription = NULL;
  139. BSTR RuleAppPath = NULL;
  140. long CurrentProfilesBitMask = 0;
  141. // For localization purposes, the rule name, description, and group can be
  142. // provided as indirect strings. These indirect strings can be defined in an rc file.
  143. // Examples of the indirect string definitions in the rc file -
  144. // 127 "EdgeTraversalOptions Sample Application"
  145. // 128 "Allow inbound TCP traffic to application EdgeTraversalOptions.exe"
  146. // 129 "Allow EdgeTraversalOptions.exe to receive inbound traffic for TCP protocol
  147. // from remote machines located within your network as well as from
  148. // the Internet (i.e from outside of your Edge device like Firewall or NAT"
  149. // Examples of using indirect strings -
  150. // hr = StringCchPrintfW(pwszTemp, STRING_BUFFER_SIZE, L"@EdgeTraversalOptions.exe,-128");
  151. RuleName = MakeRuleName(exeName);
  152. if (NULL == RuleName)
  153. {
  154. WcaLog(LOGMSG_STANDARD, "\nERROR: Insufficient memory\n");
  155. goto Cleanup;
  156. }
  157. // Examples of using indirect strings -
  158. // hr = StringCchPrintfW(pwszTemp, STRING_BUFFER_SIZE, L"@EdgeTraversalOptions.exe,-127");
  159. hr = StringCchPrintfW(pwszTemp, STRING_BUFFER_SIZE, exeName);
  160. if (FAILED(hr))
  161. {
  162. WcaLog(LOGMSG_STANDARD, "Failed to compose a resource identifier string: 0x%08lx\n", hr);
  163. goto Cleanup;
  164. }
  165. RuleGroupName = SysAllocString(pwszTemp); // Used for grouping together multiple rules
  166. if (NULL == RuleGroupName)
  167. {
  168. WcaLog(LOGMSG_STANDARD, "\nERROR: Insufficient memory\n");
  169. goto Cleanup;
  170. }
  171. // Examples of using indirect strings -
  172. // hr = StringCchPrintfW(pwszTemp, STRING_BUFFER_SIZE, L"@EdgeTraversalOptions.exe,-129");
  173. hr = StringCchPrintfW(pwszTemp, STRING_BUFFER_SIZE, L"Allow %ls to receive \
  174. inbound traffic from remote machines located within your network as well as \
  175. from the Internet", exeName);
  176. if (FAILED(hr))
  177. {
  178. WcaLog(LOGMSG_STANDARD, "Failed to compose a resource identifier string: 0x%08lx\n", hr);
  179. goto Cleanup;
  180. }
  181. RuleDescription = SysAllocString(pwszTemp);
  182. if (NULL == RuleDescription)
  183. {
  184. WcaLog(LOGMSG_STANDARD, "\nERROR: Insufficient memory\n");
  185. goto Cleanup;
  186. }
  187. RuleAppPath = SysAllocString(exeFile);
  188. if (NULL == RuleAppPath)
  189. {
  190. WcaLog(LOGMSG_STANDARD, "\nERROR: Insufficient memory\n");
  191. goto Cleanup;
  192. }
  193. hr = pNetFwPolicy2->get_Rules(&pNetFwRules);
  194. if (FAILED(hr))
  195. {
  196. WcaLog(LOGMSG_STANDARD, "Failed to retrieve firewall rules collection : 0x%08lx\n", hr);
  197. goto Cleanup;
  198. }
  199. hr = CoCreateInstance(
  200. __uuidof(NetFwRule), //CLSID of the class whose object is to be created
  201. NULL,
  202. CLSCTX_INPROC_SERVER,
  203. __uuidof(INetFwRule), // Identifier of the Interface used for communicating with the object
  204. (void**)&pNetFwRule);
  205. if (FAILED(hr))
  206. {
  207. WcaLog(LOGMSG_STANDARD, "CoCreateInstance for INetFwRule failed: 0x%08lx\n", hr);
  208. goto Cleanup;
  209. }
  210. hr = pNetFwRule->put_Name(RuleName);
  211. if (FAILED(hr))
  212. {
  213. WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_Name failed with error: 0x %x.\n", hr);
  214. goto Cleanup;
  215. }
  216. hr = pNetFwRule->put_Grouping(RuleGroupName);
  217. if (FAILED(hr))
  218. {
  219. WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_Grouping failed with error: 0x %x.\n", hr);
  220. goto Cleanup;
  221. }
  222. hr = pNetFwRule->put_Description(RuleDescription);
  223. if (FAILED(hr))
  224. {
  225. WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_Description failed with error: 0x %x.\n", hr);
  226. goto Cleanup;
  227. }
  228. // If you want the rule to avoid public, you can refer to
  229. // https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ics/c-adding-an-outbound-rule
  230. CurrentProfilesBitMask = NET_FW_PROFILE2_ALL;
  231. hr = pNetFwRule->put_Direction(in ? NET_FW_RULE_DIR_IN : NET_FW_RULE_DIR_OUT);
  232. if (FAILED(hr))
  233. {
  234. WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_Direction failed with error: 0x %x.\n", hr);
  235. goto Cleanup;
  236. }
  237. hr = pNetFwRule->put_Action(NET_FW_ACTION_ALLOW);
  238. if (FAILED(hr))
  239. {
  240. WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_Action failed with error: 0x %x.\n", hr);
  241. goto Cleanup;
  242. }
  243. hr = pNetFwRule->put_ApplicationName(RuleAppPath);
  244. if (FAILED(hr))
  245. {
  246. WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_ApplicationName failed with error: 0x %x.\n", hr);
  247. goto Cleanup;
  248. }
  249. //hr = pNetFwRule->put_Protocol(6); // TCP
  250. //if (FAILED(hr))
  251. //{
  252. // WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_Protocol failed with error: 0x %x.\n", hr);
  253. // goto Cleanup;
  254. //}
  255. hr = pNetFwRule->put_Profiles(CurrentProfilesBitMask);
  256. if (FAILED(hr))
  257. {
  258. WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_Profiles failed with error: 0x %x.\n", hr);
  259. goto Cleanup;
  260. }
  261. hr = pNetFwRule->put_Enabled(VARIANT_TRUE);
  262. if (FAILED(hr))
  263. {
  264. WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_Enabled failed with error: 0x %x.\n", hr);
  265. goto Cleanup;
  266. }
  267. if (in) {
  268. // Check if INetFwRule2 interface is available (i.e Windows7+)
  269. // If supported, then use EdgeTraversalOptions
  270. // Else use the EdgeTraversal boolean flag.
  271. if (SUCCEEDED(pNetFwRule->QueryInterface(__uuidof(INetFwRule2), (void**)&pNetFwRule2)))
  272. {
  273. hr = pNetFwRule2->put_EdgeTraversalOptions(NET_FW_EDGE_TRAVERSAL_TYPE_DEFER_TO_APP);
  274. if (FAILED(hr))
  275. {
  276. WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_EdgeTraversalOptions failed with error: 0x %x.\n", hr);
  277. goto Cleanup;
  278. }
  279. }
  280. else
  281. {
  282. hr = pNetFwRule->put_EdgeTraversal(VARIANT_TRUE);
  283. if (FAILED(hr))
  284. {
  285. WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_EdgeTraversal failed with error: 0x %x.\n", hr);
  286. goto Cleanup;
  287. }
  288. }
  289. }
  290. hr = pNetFwRules->Add(pNetFwRule);
  291. if (FAILED(hr))
  292. {
  293. WcaLog(LOGMSG_STANDARD, "Failed to add firewall rule to the firewall rules collection : 0x%08lx\n", hr);
  294. goto Cleanup;
  295. }
  296. WcaLog(LOGMSG_STANDARD, "Successfully added firewall rule !\n");
  297. Cleanup:
  298. SysFreeString(RuleName);
  299. SysFreeString(RuleGroupName);
  300. SysFreeString(RuleDescription);
  301. SysFreeString(RuleAppPath);
  302. if (pNetFwRule2 != NULL)
  303. {
  304. pNetFwRule2->Release();
  305. }
  306. if (pNetFwRule != NULL)
  307. {
  308. pNetFwRule->Release();
  309. }
  310. if (pNetFwRules != NULL)
  311. {
  312. pNetFwRules->Release();
  313. }
  314. return hr;
  315. }
  316. // Instantiate INetFwPolicy2
  317. HRESULT WFCOMInitialize(INetFwPolicy2** ppNetFwPolicy2)
  318. {
  319. HRESULT hr = S_OK;
  320. hr = CoCreateInstance(
  321. __uuidof(NetFwPolicy2),
  322. NULL,
  323. CLSCTX_INPROC_SERVER,
  324. __uuidof(INetFwPolicy2),
  325. (void**)ppNetFwPolicy2);
  326. if (FAILED(hr))
  327. {
  328. WcaLog(LOGMSG_STANDARD, "CoCreateInstance for INetFwPolicy2 failed: 0x%08lx\n", hr);
  329. goto Cleanup;
  330. }
  331. Cleanup:
  332. return hr;
  333. }
  334. // Release INetFwPolicy2
  335. void WFCOMCleanup(INetFwPolicy2* pNetFwPolicy2)
  336. {
  337. // Release the INetFwPolicy2 object (Vista+)
  338. if (pNetFwPolicy2 != NULL)
  339. {
  340. pNetFwPolicy2->Release();
  341. }
  342. }