Firewall CIM calls from C#


I’ve learned how to do the CIM calls from the .NET C# code. It wasn’t quite straightforward, so I want to share this knowledge.

First of all, what are the CIM calls and why would you want to use them? The CIM is the new way of doing the RPC for the WMI calls. In case if you don’t know, WMI is the Windows Management Interface, the way to manage various programs, including the internals of Windows itself. The classic WMI implementation uses the OLE for the RPC protocol. CIM is the new lighter-weight RPC protocol. The small versions of Windows, such as NanoServer, don’t support OLE but they do support CIM. The full server and client versions of Windows support both OLE and CIM.

If you use PowerShell, the cmdlet Get-WmiObject and friends use the OLE interface while Get-CimInstance and friends use the CIM interface. When you build a WMI provider, there is no need to think about OLE and CIM: the WMI library takes care of the implementation. The method names are unchanged, they still say COM in the names, but if COM is not available the library implements the access through CIM. The only major difference is that the initialization  CoInitializeEx() must use the COINIT_MULTITHREADED mode, the COINIT_APARTMENTTHREADED is not implemented with CIM.

My immediate problem was to control the firewall. The classic way to do it is through the COM classes, or in C# through the NetFwTypeLib that encapsulates these COM classes.  None of which works on NanoServer, not to mention that NanoServer runs CoreCLR instead of the full .NET, and NetFwTypeLib is not available on CoreCLR. But the CIM interface for the firewall is there, and is used by PowerShell to implement its cmdlets. The use of CIM is so prevalent in PowerShell that it doesn’t even have the code for the firewall cmdlets. If you look in C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\NetSecurity\, you’ll find only the declarations that say that the PowerShell cmdlets translate directly to the operations on the WMI/CIM objects.

So, how to use the same CIM interface from the CoreCLR C#?

First of all, the CIM APIs are documented at https://msdn.microsoft.com/en-us/library/microsoft.management.infrastructure.cimsession_members%28v=vs.85%29.aspx.

Then, to build the code, you’ll need the assembly Microsoft.Management.Infrastructure. This is a bit of a bummer at the moment, since currently it’s not officially published to NuGet.org. I’ve used an internal source. There is a published package with this name on NuGet, and maybe it even works but I haven’t tried because it’s not official. Hopefully it will get replaced by an official package. You can take the DLL from NanoServer and make your own NuGet package out of it, and my guess is that that’s what someone did.

Finally, having the build configured, you can write the code. The basic querying looks like this:

using Microsoft.Management.Infrastructure;
        const string FirewallNamespace = "root/standardcimv2";
            CimSession s = CimSession.Create(null);
            string query = String.Format("select * from MSFT_NetFirewallRule where InstanceID='{0}'", ruleName);

            var rules = s.QueryInstances(FirewallNamespace, "WQL", query);

            logger.Log(LogLevel.Info, "Firewall query for '{0}'returned (", ruleName);
            bool result = false;
            foreach (var r in rules)
            {
                result = true; // at least one rule was found

                string name = r.CimInstanceProperties["InstanceId"].Value.ToString();
                UInt16 enabled = (UInt16)r.CimInstanceProperties["Enabled"].Value;

                logger.Log(LogLevel.Info, "  Rule {0} enabled={1}", name, enabled);
                r.Dispose();
            }
            logger.Log(LogLevel.Info, ")");

            s.Dispose();

You can find out more about the available properties by looking at them with the PowerShell cmdlets. For example, the above fragment is an equivalent of

$i = Get-CimInstance -Namespace "root/standardcimv2" -Query "select * from MSFT_NetFirewallRulewhere InstanceID='$ruleName'"

And then you can use the usual PowerShell reflection cmdlets like “gm” to find out what’s inside (and looking inside the PowerShell modules also helps, for instance it shows that the value to enable the rule is 1 and to disable it is 2). You can change the values and write them back. In PowerShell just set the properties of $i and then run

$i | Set-CimInstance

In C# it’s done similarly:

                // 1 means "enabled", 2 means "disabled"
                if (enabled != 1) 
                {
                    logger.Log(LogLevel.Info, "  -- Enabling the Rule {0}", name);
                    r.CimInstanceProperties["Enabled"].Value = (UInt16)1;
                    s.ModifyInstance(r);
                }

But it’s not all quite that easy. The firewall rules are not contained in a single WMI pseudo-table, they are split into multiple connected tables. So when you look at the contents of $i above, you’ll notice that not all the fields from New-NetFirewallRule are present there. The rest are placed into the other associated pseudo-tables, pseudo-jointed by the implicit keys. In PowerShell you can get these associated parts with Get-CimAssociatedInstance, for example to get the information about the network profiles where the rule applies:

$pi = Get-CimAssociatedInstance -InputObject $i -ResultClassName "MSFT_NetFirewallProfile"

Without -ResultClassName it would return the mix of all associated parts of all classes. You can get the names of the class names of all the available parts with

Get-CimAssociatedInstance -InputObject $i |% { $_.CimClass.CimClassName }

If you want to modify these values, do the same thing: set the new value in the part object and then send back the updated part object with Set-CimInstance. In C# it looks like this:

                // also make sure that the rule is enabled in all profiles
                string aclass = "MSFT_NetFirewallProfile"; 
                logger.Log(LogLevel.Info, "  Checking the firewall entry for '{0}' element {1}.", ruleName, aclass);
                var res = s.EnumerateAssociatedInstances(FirewallNamespace, r, null, aclass, null, null);
                foreach (var o in res)
                {
                    // there are normally 3 profile elements associated: Domain, Private, Public
                    string pname = o.CimInstanceProperties["Name"].Value.ToString();
                    UInt16 penabled = (UInt16)o.CimInstanceProperties["Enabled"].Value;
                    logger.Log(LogLevel.Info, "    Profile {0} enabled={1}", pname, penabled);

                    if (penabled != 1)
                    {
                        logger.Log(LogLevel.Info, "    -- Enabling the Profile {0}", pname);
                        o.CimInstanceProperties["Enabled"].Value = (UInt16)1;
                        s.ModifyInstance(o);
                    }
                    o.Dispose();
                }

And finally the creation. The creation is tricky. First you need to create the base object. This will also create the associated parts with the default values. Then you read back these parts and update the values in them:

            CimSession s = CimSession.Create(null);

            CimInstance rule = new CimInstance("MSFT_NetFirewallRule");
            rule.CimInstanceProperties.Add(CimProperty.Create("ElementName", ruleName, CimType.String, CimFlags.Property | CimFlags.Key));
            rule.CimInstanceProperties.Add(CimProperty.Create("InstanceID", ruleName, CimType.String, CimFlags.Property | CimFlags.Key));
            rule.CimInstanceProperties.Add(CimProperty.Create("Description", ruleName, CimType.String, CimFlags.Property));
            rule.CimInstanceProperties.Add(CimProperty.Create("Direction", (UInt16)1, CimType.UInt16, CimFlags.Property)); // 1 = Inbound
            rule.CimInstanceProperties.Add(CimProperty.Create("Action", (UInt16)2, CimType.UInt16, CimFlags.Property)); // 2 = Allow
            rule.CimInstanceProperties.Add(CimProperty.Create("Enabled", (UInt16)1, CimType.UInt16, CimFlags.Property)); // 1 = Enabled
            rule.CimInstanceProperties.Add(CimProperty.Create("RuleGroup", Group, CimType.String, CimFlags.Property));

            rule = s.CreateInstance(FirewallNamespace, rule);

            // the extra properties have to be set in the associated objects that get created with the main object
            {
                string aclass = "MSFT_NetProtocolPortFilter"; 
                logger.Log(LogLevel.Info, "Setting the firewall entry for '{0}' element {1}.", ruleName, aclass);
                var res = s.EnumerateAssociatedInstances(FirewallNamespace, rule, null, aclass, null, null);
                foreach (var o in res)
                {
                    found = true;
                    o.CimInstanceProperties["Protocol"].Value = "TCP";
                    o.CimInstanceProperties["LocalPort"].Value = new string[] { port.ToString() };
                    s.ModifyInstance(o);
                    o.Dispose();
                }
            }

            rule.Dispose();
            s.Dispose();

This finally works. Some typical pitfalls to look for include:

  • The messages in the exceptions of CreateInstance() are not very detailed. For example, if you mistype a field name, it won’t tell you which field is wrong. You can get the better diagnostics by doing the same in PowerShell with New-CimInstance.
  • PowerShell provides a lot of property aliases and “script properties”. You’ll need to find the actual underlying properties for the C# code, either with the introspection cmdlets or by looking into the definitions of the PowerShell modules.
  • You need to know the names of the classes (AKA names of pseudo-tables) for the associated parts, the types of the fields, and the enumerated values. Again, use either the PowerShell introspection or the PowerShell module definitions.

 

Comments (0)

Skip to main content