main
cs 519 lines 21.3 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.Firewall
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.Reflection;
8 using System.Security;
9 using System.Xml.Linq;
10 using WixToolset.Data;
11 using WixToolset.Data.WindowsInstaller;
12 using WixToolset.Extensibility;
13 using WixToolset.Extensibility.Data;
14 using WixToolset.Extensibility.Services;
15
16 /// <summary>
17 /// The decompiler for the WiX Toolset Firewall Extension.
18 /// </summary>
19 public sealed class FirewallDecompiler : BaseWindowsInstallerDecompilerExtension
20 {
21 public override IReadOnlyCollection<TableDefinition> TableDefinitions => FirewallTableDefinitions.All;
22
23 private IParseHelper ParseHelper { get; set; }
24
25 public override void PreDecompile(IWindowsInstallerDecompileContext context, IWindowsInstallerDecompilerHelper helper)
26 {
27 base.PreDecompile(context, helper);
28 this.ParseHelper = context.ServiceProvider.GetService<IParseHelper>();
29 }
30
31 /// <summary>
32 /// Called at the beginning of the decompilation of a database.
33 /// </summary>
34 /// <param name="tables">The collection of all tables.</param>
35 public override void PreDecompileTables(TableIndexedCollection tables)
36 {
37 }
38
39 /// <summary>
40 /// Decompiles an extension table.
41 /// </summary>
42 /// <param name="table">The table to decompile.</param>
43 public override bool TryDecompileTable(Table table)
44 {
45 switch (table.Name)
46 {
47 case "WixFirewallException":
48 case "Wix4FirewallException":
49 case "Wix5FirewallException":
50 this.DecompileWixFirewallExceptionTable(table);
51 break;
52 default:
53 return false;
54 }
55
56 return true;
57 }
58
59 /// <summary>
60 /// Finalize decompilation.
61 /// </summary>
62 /// <param name="tables">The collection of all tables.</param>
63 public override void PostDecompileTables(TableIndexedCollection tables)
64 {
65 this.FinalizeFirewallExceptionTable(tables);
66 }
67
68 /// <summary>
69 /// Decompile the WixFirewallException table.
70 /// </summary>
71 /// <param name="table">The table to decompile.</param>
72 private void DecompileWixFirewallExceptionTable(Table table)
73 {
74 foreach (var row in table.Rows)
75 {
76 var firewallException = new XElement(FirewallConstants.FirewallExceptionName,
77 new XAttribute("Id", row.FieldAsString(0)),
78 new XAttribute("Name", row.FieldAsString(1))
79 );
80
81 if (!row.IsColumnEmpty(2))
82 {
83 string[] addresses = ((string)row[2]).Split(',');
84 if (addresses.Length == 1)
85 {
86 switch(addresses[0])
87 {
88 case "*":
89 firewallException.Add(new XAttribute("Scope", "any"));
90 break;
91 case "LocalSubnet":
92 firewallException.Add(new XAttribute("Scope", "localSubnet"));
93 break;
94 case "dns":
95 firewallException.Add(new XAttribute("Scope", "DNS"));
96 break;
97 case "dhcp":
98 firewallException.Add(new XAttribute("Scope", "DHCP"));
99 break;
100 case "wins":
101 firewallException.Add(new XAttribute("Scope", "WINS"));
102 break;
103 case "DefaultGateway":
104 firewallException.Add(new XAttribute("Scope", "defaultGateway"));
105 break;
106 default:
107 if (this.ParseHelper.ContainsProperty(addresses[0]))
108 {
109 firewallException.Add(new XAttribute("Scope", addresses[0]));
110 }
111 else
112 {
113 FirewallDecompiler.AddRemoteAddress(firewallException, addresses[0]);
114 }
115 break;
116 }
117 }
118 else
119 {
120 foreach (var address in addresses)
121 {
122 FirewallDecompiler.AddRemoteAddress(firewallException, address);
123 }
124 }
125 }
126
127 if (!row.IsColumnEmpty(3))
128 {
129 firewallException.Add(new XAttribute("Port", row.FieldAsString(3)));
130 }
131
132 if (!row.IsColumnEmpty(4))
133 {
134 switch (row.FieldAsString(4))
135 {
136 case FirewallConstants.IntegerNotSetString:
137 break;
138 case "6":
139 firewallException.Add(new XAttribute("Protocol", "tcp"));
140 break;
141 case "17":
142 firewallException.Add(new XAttribute("Protocol", "udp"));
143 break;
144
145 default:
146 firewallException.Add(new XAttribute("Protocol", row.FieldAsString(4)));
147 break;
148 }
149 }
150
151 if (!row.IsColumnEmpty(5))
152 {
153 firewallException.Add(new XAttribute("Program", row.FieldAsString(5)));
154 }
155
156 if (!row.IsColumnEmpty(6))
157 {
158 var attr = row.FieldAsInteger(6);
159 if ((attr & 0x1) == 0x1)
160 {
161 AttributeIfNotNull("IgnoreFailure", true);
162 }
163
164 if ((attr & 0x2) == 0x2)
165 {
166 firewallException.Add(new XAttribute("OnUpdate", "doNothing"));
167 }
168 else if ((attr & 0x4) == 0x4)
169 {
170 firewallException.Add(new XAttribute("OnUpdate", "enableOnly"));
171 }
172 }
173
174 if (!row.IsColumnEmpty(7))
175 {
176 switch (row.FieldAsString(7))
177 {
178 case FirewallConstants.IntegerNotSetString:
179 break;
180 case "1":
181 firewallException.Add(new XAttribute("Profile", "domain"));
182 break;
183 case "2":
184 firewallException.Add(new XAttribute("Profile", "private"));
185 break;
186 case "4":
187 firewallException.Add(new XAttribute("Profile", "public"));
188 break;
189 case "2147483647":
190 firewallException.Add(new XAttribute("Profile", "all"));
191 break;
192
193 default:
194 firewallException.Add(new XAttribute("Profile", row.FieldAsString(7)));
195 break;
196 }
197 }
198
199 if (!row.IsColumnEmpty(9))
200 {
201 firewallException.Add(new XAttribute("Description", row.FieldAsString(9)));
202 }
203
204 if (!row.IsColumnEmpty(10))
205 {
206 switch (Convert.ToInt32(row[10]))
207 {
208 case FirewallConstants.NET_FW_RULE_DIR_IN:
209 break;
210 case FirewallConstants.NET_FW_RULE_DIR_OUT:
211 firewallException.Add(AttributeIfNotNull("Outbound", true));
212 break;
213 }
214 }
215
216 // Introduced in 5.0.0
217 if (row.Fields.Length > 11)
218 {
219 if (!row.IsColumnEmpty(11))
220 {
221 var action = row.FieldAsString(11);
222 switch (action)
223 {
224 case FirewallConstants.IntegerNotSetString:
225 break;
226 case "1":
227 firewallException.Add(new XAttribute("Action", "allow"));
228 break;
229 case "0":
230 firewallException.Add(new XAttribute("Action", "block"));
231 break;
232 default:
233 firewallException.Add(new XAttribute("Action", action));
234 break;
235 }
236 }
237
238 if (!row.IsColumnEmpty(12))
239 {
240 var edgeTraversal = row.FieldAsString(12);
241 switch (edgeTraversal)
242 {
243 case FirewallConstants.IntegerNotSetString:
244 break;
245 case "0":
246 firewallException.Add(new XAttribute("EdgeTraversal", "deny"));
247 break;
248 case "1":
249 firewallException.Add(new XAttribute("EdgeTraversal", "allow"));
250 break;
251 case "2":
252 firewallException.Add(new XAttribute("EdgeTraversal", "deferToApp"));
253 break;
254 case "3":
255 firewallException.Add(new XAttribute("EdgeTraversal", "deferToUser"));
256 break;
257 default:
258 firewallException.Add(new XAttribute("EdgeTraversal", edgeTraversal));
259 break;
260 }
261 }
262
263 if (!row.IsColumnEmpty(13))
264 {
265 var enabled = row.FieldAsString(13);
266 switch (enabled)
267 {
268 case FirewallConstants.IntegerNotSetString:
269 break;
270 case "1":
271 firewallException.Add(new XAttribute("Enabled", "yes"));
272 break;
273 case "0":
274 firewallException.Add(new XAttribute("Enabled", "no"));
275 break;
276 default:
277 firewallException.Add(new XAttribute("Enabled", enabled));
278 break;
279 }
280 }
281
282 if (!row.IsColumnEmpty(14))
283 {
284 firewallException.Add(new XAttribute("Grouping", row.FieldAsString(14)));
285 }
286
287 if (!row.IsColumnEmpty(15))
288 {
289 firewallException.Add(new XAttribute("IcmpTypesAndCodes", row.FieldAsString(15)));
290 }
291
292 if (!row.IsColumnEmpty(16))
293 {
294 string[] interfaces = row.FieldAsString(16).Split(new[] { FirewallConstants.FORBIDDEN_FIREWALL_CHAR }, StringSplitOptions.RemoveEmptyEntries);
295 if (interfaces.Length == 1)
296 {
297 firewallException.Add(new XAttribute("Interface", interfaces[0].ToCamelCase()));
298 }
299 else
300 {
301 foreach (var interfaceItem in interfaces)
302 {
303 FirewallDecompiler.AddInterface(firewallException, interfaceItem.ToCamelCase());
304 }
305 }
306 }
307
308 if (!row.IsColumnEmpty(17))
309 {
310 string[] interfaceTypes = row.FieldAsString(17).Split(',');
311 if (interfaceTypes.Length == 1)
312 {
313 firewallException.Add(new XAttribute("InterfaceType", interfaceTypes[0].ToCamelCase()));
314 }
315 else
316 {
317 foreach (var interfaceType in interfaceTypes)
318 {
319 FirewallDecompiler.AddInterfaceType(firewallException, interfaceType.ToCamelCase());
320 }
321 }
322 }
323
324 if (!row.IsColumnEmpty(18))
325 {
326 string[] addresses = row.FieldAsString(18).Split(',');
327 if (addresses.Length == 1)
328 {
329 switch (addresses[0])
330 {
331 case "*":
332 firewallException.Add(new XAttribute("LocalScope", "any"));
333 break;
334 case "LocalSubnet":
335 firewallException.Add(new XAttribute("LocalScope", "localSubnet"));
336 break;
337 case "dns":
338 firewallException.Add(new XAttribute("LocalScope", "DNS"));
339 break;
340 case "dhcp":
341 firewallException.Add(new XAttribute("LocalScope", "DHCP"));
342 break;
343 case "wins":
344 firewallException.Add(new XAttribute("LocalScope", "WINS"));
345 break;
346 case "DefaultGateway":
347 firewallException.Add(new XAttribute("LocalScope", "defaultGateway"));
348 break;
349 default:
350 if (this.ParseHelper.ContainsProperty(addresses[0]))
351 {
352 firewallException.Add(new XAttribute("LocalScope", addresses[0]));
353 }
354 else
355 {
356 FirewallDecompiler.AddLocalAddress(firewallException, addresses[0]);
357 }
358 break;
359 }
360 }
361 else
362 {
363 foreach (var address in addresses)
364 {
365 FirewallDecompiler.AddLocalAddress(firewallException, address);
366 }
367 }
368 }
369
370 if (!row.IsColumnEmpty(19))
371 {
372 firewallException.Add(new XAttribute("RemotePort", row.FieldAsString(19)));
373 }
374
375 if (!row.IsColumnEmpty(20))
376 {
377 firewallException.Add(new XAttribute("Service", row.FieldAsString(20)));
378 }
379
380 if (!row.IsColumnEmpty(21))
381 {
382 firewallException.Add(new XAttribute("LocalAppPackageId", row.FieldAsString(21)));
383 }
384
385 if (!row.IsColumnEmpty(22))
386 {
387 firewallException.Add(new XAttribute("LocalUserAuthorizedList", row.FieldAsString(22)));
388 }
389
390 if (!row.IsColumnEmpty(23))
391 {
392 firewallException.Add(new XAttribute("LocalUserOwner", row.FieldAsString(23)));
393 }
394
395 if (!row.IsColumnEmpty(24))
396 {
397 firewallException.Add(new XAttribute("RemoteMachineAuthorizedList", row.FieldAsString(24)));
398 }
399
400 if (!row.IsColumnEmpty(25))
401 {
402 firewallException.Add(new XAttribute("RemoteUserAuthorizedList", row.FieldAsString(25)));
403 }
404
405 if (!row.IsColumnEmpty(26))
406 {
407 var secureFlags = row.FieldAsString(26);
408 switch (secureFlags)
409 {
410 case FirewallConstants.IntegerNotSetString:
411 break;
412 case "0":
413 firewallException.Add(new XAttribute("IPSecSecureFlags", "none"));
414 break;
415 case "1":
416 firewallException.Add(new XAttribute("IPSecSecureFlags", "noEncapsulation"));
417 break;
418 case "2":
419 firewallException.Add(new XAttribute("IPSecSecureFlags", "withIntegrity"));
420 break;
421 case "3":
422 firewallException.Add(new XAttribute("IPSecSecureFlags", "negotiateEncryption"));
423 break;
424 case "4":
425 firewallException.Add(new XAttribute("IPSecSecureFlags", "encrypt"));
426 break;
427 default:
428 firewallException.Add(new XAttribute("IPSecSecureFlags", secureFlags));
429 break;
430 }
431 }
432 }
433
434 this.DecompilerHelper.IndexElement(row, firewallException);
435 }
436 }
437
438 private static void AddRemoteAddress(XElement firewallException, string address)
439 {
440 var remoteAddress = new XElement(FirewallConstants.RemoteAddressName,
441 new XAttribute("Value", address)
442 );
443
444 firewallException.Add(remoteAddress);
445 }
446
447 private static void AddInterfaceType(XElement firewallException, string type)
448 {
449 var interfaceType = new XElement(FirewallConstants.InterfaceTypeName,
450 new XAttribute("Value", type)
451 );
452
453 firewallException.Add(interfaceType);
454 }
455
456 private static void AddLocalAddress(XElement firewallException, string address)
457 {
458 var localAddress = new XElement(FirewallConstants.LocalAddressName,
459 new XAttribute("Value", address)
460 );
461
462 firewallException.Add(localAddress);
463 }
464
465 private static void AddInterface(XElement firewallException, string value)
466 {
467 var interfaceName = new XElement(FirewallConstants.InterfaceName,
468 new XAttribute("Name", value)
469 );
470
471 firewallException.Add(interfaceName);
472 }
473
474 private static XAttribute AttributeIfNotNull(string name, bool value)
475 {
476 return new XAttribute(name, value ? "yes" : "no");
477 }
478
479 /// <summary>
480 /// Finalize the FirewallException table.
481 /// </summary>
482 /// <param name="tables">Collection of all tables.</param>
483 private void FinalizeFirewallExceptionTable(TableIndexedCollection tables)
484 {
485 if (tables.TryGetTable("Wix5FirewallException", out var firewallExceptionTable))
486 {
487 foreach (var row in firewallExceptionTable.Rows)
488 {
489 var xmlConfig = this.DecompilerHelper.GetIndexedElement(row);
490
491 var componentId = row.FieldAsString(8);
492 if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
493 {
494 component.Add(xmlConfig);
495 }
496 else
497 {
498 this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, firewallExceptionTable.Name, row.GetPrimaryKey(), "Component_", componentId, "Component"));
499 }
500 }
501 }
502 }
503 }
504
505 internal static class StringExtensions
506 {
507 public static string ToCamelCase(this string str)
508 {
509 if (String.IsNullOrEmpty(str))
510 {
511 return str;
512 }
513
514 var camelCase = str[0].ToString().ToLowerInvariant() + str.Substring(1);
515
516 return camelCase;
517 }
518 }
519 }