Example #1
0
        private static FirewallResponse OnFirewallCheck(FirewallRequest request)
        {
            // Only filter chrome.
            var filtering = request.BinaryAbsolutePath.IndexOf("chrome", StringComparison.OrdinalIgnoreCase) != -1;

            if (filtering)
            {
                switch (request.RemotePort)
                {
                case 80:
                case 443:
                {
                    // Let's allow chrome to access TCP 80 and 443, but block all other ports.
                    Console.WriteLine("Filtering application {0} destined for {1}", request.BinaryAbsolutePath, request.RemotePort);
                    return(new FirewallResponse(FirewallAction.FilterApplication));
                }

                default:
                {
                    // Let's allow chrome to access TCP 80 and 443, but block all other ports. This is where we're
                    // blocking any non-80/443 bound transmission.
                    Console.WriteLine("Blocking internet for application {0} destined for {1}", request.BinaryAbsolutePath, request.RemotePort);
                    return(new FirewallResponse(FirewallAction.BlockInternetForApplication));
                }
                }
            }

            // For all other applications, just let them access the internet without filtering.
            Console.WriteLine("Not filtering application {0} destined for {1}", request.BinaryAbsolutePath, request.RemotePort);
            return(new FirewallResponse(FirewallAction.DontFilterApplication));
        }
Example #2
0
        private static FirewallResponse OnFirewallCheck(FirewallRequest request)
        {
            // Only filter chrome.
            var filtering = request.BinaryAbsolutePath.IndexOf("chrome", StringComparison.OrdinalIgnoreCase) != -1;

            if (filtering)
            {
                if (request.RemotePort == s_standardHttpPortNetworkOrder || request.RemotePort == s_standardHttpsPortNetworkOrder)
                {
                    // Let's allow chrome to access TCP 80 and 443, but block all other ports.
                    Console.WriteLine("Filtering application {0} destined for {1}", request.BinaryAbsolutePath, (ushort)IPAddress.HostToNetworkOrder((short)request.RemotePort));
                    return(new FirewallResponse(CitadelCore.Net.Proxy.FirewallAction.FilterApplication));
                }
                else
                {
                    // Let's allow chrome to access TCP 80 and 443, but block all other
                    // ports. This is where we're blocking any non-80/443 bound transmission.
                    Console.WriteLine("Blocking internet for application {0} destined for {1}", request.BinaryAbsolutePath, (ushort)IPAddress.HostToNetworkOrder((short)request.RemotePort));
                    return(new FirewallResponse(CitadelCore.Net.Proxy.FirewallAction.BlockInternetForApplication));
                }
            }

            // For all other applications, just let them access the internet without filtering.
            Console.WriteLine("Not filtering application {0} destined for {1}", request.BinaryAbsolutePath, (ushort)IPAddress.HostToNetworkOrder((short)request.RemotePort));
            return(new FirewallResponse(CitadelCore.Net.Proxy.FirewallAction.DontFilterApplication));
        }
        private static FirewallResponse OnFirewallCheck(FirewallRequest request)
        {
            // Only filter chrome.
            //var filtering = request.BinaryAbsolutePath.IndexOf("chrome", StringComparison.OrdinalIgnoreCase) != -1;
            var filtering = true;

            if (filtering)
            {
                if (
                    request.RemotePort == s_standardHttpPortNetworkOrder ||
                    request.RemotePort == s_standardHttpsPortNetworkOrder ||
                    request.RemotePort == s_altHttpPortNetworkOrder ||
                    request.RemotePort == s_altHttpsPortNetworkOrder
                    )
                {
                    // Let's allow chrome to access TCP 80 and 443, but block all other ports.
                    //Console.WriteLine("Filtering application {0} destined for {1}", request.BinaryAbsolutePath, (ushort)IPAddress.HostToNetworkOrder((short)request.RemotePort));
                    return(new FirewallResponse(CitadelCore.Net.Proxy.FirewallAction.FilterApplication));
                }
                else
                {
                    // Let's allow chrome to access TCP 80 and 443, but ignore all other
                    // ports. We want to allow non 80/443 requests to go through because
                    // this example now demonstrates the replay API, which will cause
                    // a bunch of browser tabs to open whenever you visit my website.
                    //
                    // If we filtered the replays back through the proxy, who knows
                    // what would happen! Actually that's not true, you'd invoke an infinite
                    // loopback, spawn a ton of browser tabs and then call me a bad programmer.
                    //Console.WriteLine("Ignoring internet for application {0} destined for {1}", request.BinaryAbsolutePath, (ushort)IPAddress.HostToNetworkOrder((short)request.RemotePort));
                    return(new FirewallResponse(CitadelCore.Net.Proxy.FirewallAction.DontFilterApplication));
                }
            }

            // For all other applications, just let them access the internet without filtering.
            //Console.WriteLine("Not filtering application {0} destined for {1}", request.BinaryAbsolutePath, (ushort)IPAddress.HostToNetworkOrder((short)request.RemotePort));
            return(new FirewallResponse(CitadelCore.Net.Proxy.FirewallAction.DontFilterApplication));
        }
        /// <summary>
        /// Handles the process of inspecting a new TCP connection, seeking the user's decision on
        /// what to do with the connection, and then applying that decision in code in such a way as
        /// to cause the packet filtering loop to apply the user's decision.
        /// </summary>
        /// <param name="connInfo">
        /// The state of the appropriate TCP table at the time of the new connectio.
        /// </param>
        /// <param name="tcpHeader">
        /// The TCP header from the first packet in the new connection/flow.
        /// </param>
        /// <param name="isIpv6">
        /// Whether or not this is from an IPV6 connection.
        /// </param>
        private unsafe void HandleNewTcpConnection(ITcpConnectionInfo connInfo, TcpHeader *tcpHeader, bool isIpv6)
        {
            if (tcpHeader == null)
            {
            }

            if (connInfo != null && connInfo.OwnerPid == m_thisPid)
            {
                // This is our process.
                switch (isIpv6)
                {
                case true:
                {
                    Volatile.Write(ref m_v6ShouldFilter[tcpHeader->SrcPort], (int)FirewallAction.DontFilterApplication);
                }
                break;

                case false:
                {
                    Volatile.Write(ref m_v4ShouldFilter[tcpHeader->SrcPort], (int)FirewallAction.DontFilterApplication);
                }
                break;
                }
            }
            else
            {
                FirewallResponse response = null;
                if (connInfo == null || connInfo.OwnerPid == 4 || connInfo.OwnerPid == 0)
                {
                    var firewallRequest = new FirewallRequest("SYSTEM", tcpHeader->SrcPort, tcpHeader->DstPort, connInfo == null ? 0L : connInfo.OwnerPid);
                    response = ConfirmDenyFirewallAccess?.Invoke(firewallRequest);
                }
                else
                {
                    if (IsInternalIp(connInfo.RemoteAddress))
                    {
                        response = new FirewallResponse(FirewallAction.DontFilterApplication);

                        m_logger.Info("Don't filter internal IPs.");
                    }
                    else
                    {
                        // No need to null check here, because the above IF catches whenever connInfo
                        // is null.
                        var procPath        = connInfo.OwnerProcessPath.Length > 0 ? connInfo.OwnerProcessPath : "SYSTEM";
                        var firewallRequest = new FirewallRequest(procPath, tcpHeader->SrcPort, tcpHeader->DstPort, connInfo.OwnerPid);
                        response = ConfirmDenyFirewallAccess?.Invoke(firewallRequest);
                    }
                }


                if (response == null)
                {
                    // The user couldn't be bothered to give us an answer, so just go ahead and
                    // let the packet through.

                    switch (isIpv6)
                    {
                    case true:
                    {
                        Volatile.Write(ref m_v6ShouldFilter[tcpHeader->SrcPort], (byte)FirewallAction.DontFilterApplication);

                        Volatile.Write(ref m_v6EncryptionHints[tcpHeader->SrcPort], (tcpHeader->DstPort == m_httpsStandardPort || tcpHeader->DstPort == m_httpsAltPort));
                    }
                    break;

                    case false:
                    {
                        Volatile.Write(ref m_v4ShouldFilter[tcpHeader->SrcPort], (byte)FirewallAction.DontFilterApplication);

                        Volatile.Write(ref m_v4EncryptionHints[tcpHeader->SrcPort], (tcpHeader->DstPort == m_httpsStandardPort || tcpHeader->DstPort == m_httpsAltPort));
                    }
                    break;
                    }
                }
                else
                {
                    switch (isIpv6)
                    {
                    case true:
                    {
                        Volatile.Write(ref m_v6ShouldFilter[tcpHeader->SrcPort], (byte)response.Action);

                        Volatile.Write(ref m_v6EncryptionHints[tcpHeader->SrcPort], response.EncryptedHint ?? (tcpHeader->DstPort == m_httpsStandardPort || tcpHeader->DstPort == m_httpsAltPort));
                    }
                    break;

                    case false:
                    {
                        Volatile.Write(ref m_v4ShouldFilter[tcpHeader->SrcPort], (byte)response.Action);

                        Volatile.Write(ref m_v4EncryptionHints[tcpHeader->SrcPort], response.EncryptedHint ?? (tcpHeader->DstPort == m_httpsStandardPort || tcpHeader->DstPort == m_httpsAltPort));
                    }
                    break;
                    }
                }
            }
        }
        /// <summary>
        /// Called whenever the Engine want's to check if the application at the supplied absolute
        /// path should have its traffic forced through itself or not.
        /// </summary>
        /// <param name="appAbsolutePath">
        /// The absolute path to an application that the filter is inquiring about.
        /// </param>
        /// <returns>
        /// True if the application at the specified absolute path should have its traffic forced
        /// through the filtering engine, false otherwise.
        /// </returns>
        public FirewallResponse OnAppFirewallCheck(FirewallRequest request)
        {
            if (!IsStandardHttpPort(request.RemotePort))
            {
                return(new FirewallResponse(FirewallAction.DontFilterApplication, null));
            }

            if (appListCheck == null && m_provider.PolicyConfiguration != null)
            {
                appListCheck = new AppListCheck(m_provider.PolicyConfiguration);
            }

            // XXX TODO - The engine shouldn't even tell us about SYSTEM processes and just silently
            // let them through.
            if (request.BinaryAbsolutePath.OIEquals("SYSTEM"))
            {
                return(new FirewallResponse(FirewallAction.DontFilterApplication));
            }

            // Lets completely avoid piping anything from the operating system in the filter, with
            // the sole exception of Microsoft edge.
            if ((request.BinaryAbsolutePath.IndexOf("MicrosoftEdge", StringComparison.OrdinalIgnoreCase) == -1) && request.BinaryAbsolutePath.IndexOf(@"\Windows\", StringComparison.OrdinalIgnoreCase) != -1)
            {
                lock (s_foreverWhitelistedApplications)
                {
                    if (s_foreverWhitelistedApplications.Contains(request.BinaryAbsolutePath))
                    {
                        return(new FirewallResponse(FirewallAction.DontFilterApplication));
                    }
                }

                // Here we'll simply check if the binary is signed. If so, we'll validate the
                // certificate. If the cert is good, let's just go and bypass this binary altogether.
                // However, note that this does not verify that the signed binary is actually valid
                // for the certificate. That is, it doesn't ensure file integrity. Also, note that
                // even if we went all the way as to use WinVerifyTrust() from wintrust.dll to
                // completely verify integrity etc, this can still be bypassed by adding a self
                // signed signing authority to the windows trusted certs.
                //
                // So, all we can do is kick the can further down the road. This should be sufficient
                // to prevent the lay person from dropping a browser into the Windows folder.
                //
                // Leaving above notes just for the sake of knowledge. We can kick the can pretty
                // darn far down the road by asking Windows Resource Protection if the file really
                // belongs to the OS. Viruses are known to call SfcIsFileProtected in order to avoid
                // getting caught messing with these files so if viruses avoid them, I think we've
                // booted the can so far down the road that we need not worry about being exploited
                // here. The OS would need to be funamentally compromised and that wouldn't be our fault.
                //
                // The only other way we could get exploited here by getting our hook to sfc.dll
                // hijacked. There are countermeasures of course but not right now.

                // If the result is greater than zero, then this is a protected operating system file
                // according to the operating system.
                if (SFC.SfcIsFileProtected(IntPtr.Zero, request.BinaryAbsolutePath) > 0)
                {
                    lock (s_foreverWhitelistedApplications)
                    {
                        s_foreverWhitelistedApplications.Add(request.BinaryAbsolutePath);
                    }

                    return(new FirewallResponse(FirewallAction.DontFilterApplication));
                }
            }

            try
            {
                m_provider.PolicyConfiguration.PolicyLock.EnterReadLock();

                if (m_provider.PolicyConfiguration.BlacklistedApplications.Count == 0 && m_provider.PolicyConfiguration.WhitelistedApplications.Count == 0)
                {
                    // Just filter anything accessing port 80 and 443.
                    m_logger.Debug("1Filtering application: {0}", request.BinaryAbsolutePath);
                    return(new FirewallResponse(FirewallAction.FilterApplication));
                }

                var appName = Path.GetFileName(request.BinaryAbsolutePath);

                if (m_provider.PolicyConfiguration.WhitelistedApplications.Count > 0)
                {
                    bool inList = appListCheck.IsAppInWhitelist(request.BinaryAbsolutePath, appName);

                    if (inList)
                    {
                        return(new FirewallResponse(FirewallAction.DontFilterApplication));
                    }
                    else
                    {
                        // Whitelist is in effect, and this app is not whitelisted, so force it through.
                        m_logger.Debug("2Filtering application: {0}", request.BinaryAbsolutePath);
                        return(new FirewallResponse(FirewallAction.FilterApplication));
                    }
                }

                if (m_provider.PolicyConfiguration.BlacklistedApplications.Count > 0)
                {
                    bool inList = appListCheck.IsAppInBlacklist(request.BinaryAbsolutePath, appName);

                    if (inList)
                    {
                        m_logger.Debug("3Filtering application: {0}", request.BinaryAbsolutePath);
                        return(new FirewallResponse(FirewallAction.FilterApplication));
                    }

                    return(new FirewallResponse(FirewallAction.DontFilterApplication));
                }

                // This app was not hit by either an enforced whitelist or blacklist. So, by default
                // we will filter everything. We should never get here, but just in case.

                m_logger.Debug("4Filtering application: {0}", request.BinaryAbsolutePath);
                return(new FirewallResponse(FirewallAction.FilterApplication));
            }
            catch (Exception e)
            {
                m_logger.Error("Error in {0}", nameof(OnAppFirewallCheck));
                LoggerUtil.RecursivelyLogException(m_logger, e);
                return(new FirewallResponse(FirewallAction.DontFilterApplication));
            }
            finally
            {
                m_provider?.PolicyConfiguration?.PolicyLock?.ExitReadLock();
            }
        }
        /// <summary>
        /// Handles the process of inspecting a new TCP connection, seeking the user's decision on
        /// what to do with the connection, and then applying that decision in code in such a way as
        /// to cause the packet filtering loop to apply the user's decision.
        /// </summary>
        /// <param name="connInfo">
        /// The state of the appropriate TCP table at the time of the new connectio.
        /// </param>
        /// <param name="tcpHeader">
        /// The TCP header from the first packet in the new connection/flow.
        /// </param>
        /// <param name="isIpv6">
        /// Whether or not this is from an IPV6 connection.
        /// </param>
        private void HandleNewTcpConnection(ITcpConnectionInfo connInfo, WINDIVERT_TCPHDR *tcpHeader, bool isIpv6)
        {
            if (tcpHeader != null)
            {
                Console.WriteLine(nameof(HandleNewTcpConnection));

                if (connInfo != null && connInfo.OwnerPid == m_thisPid)
                {
                    LoggerProxy.Default.Info(string.Format("Connection from local:{0} -> remote:{1} outbound is our process.", tcpHeader->SrcPort, tcpHeader->DstPort));

                    // This is our process.
                    switch (isIpv6)
                    {
                    case true:
                    {
                        Volatile.Write(ref m_v6ShouldFilter[tcpHeader->SrcPort], (int)FirewallAction.DontFilterApplication);
                    }
                    break;

                    case false:
                    {
                        Volatile.Write(ref m_v4ShouldFilter[tcpHeader->SrcPort], (int)FirewallAction.DontFilterApplication);
                    }
                    break;
                    }
                }
                else
                {
                    FirewallResponse response = null;
                    if (connInfo == null || connInfo.OwnerPid == 4 || connInfo.OwnerPid == 0)
                    {
                        var firewallRequest = new FirewallRequest("SYSTEM", tcpHeader->SrcPort, tcpHeader->DstPort);
                        response = ConfirmDenyFirewallAccess?.Invoke(firewallRequest);
                    }
                    else
                    {
                        // No need to null check here, because the above IF catches whenever connInfo
                        // is null.
                        var procPath        = connInfo.OwnerProcessPath.Length > 0 ? connInfo.OwnerProcessPath : "SYSTEM";
                        var firewallRequest = new FirewallRequest(procPath, tcpHeader->SrcPort, tcpHeader->DstPort);
                        response = ConfirmDenyFirewallAccess?.Invoke(firewallRequest);
                    }

                    if (response == null)
                    {
                        LoggerProxy.Default.Info("NO RESPONSE");
                        // The user couldn't be bothered to give us an answer, so just go ahead and
                        // let the packet through.

                        switch (isIpv6)
                        {
                        case true:
                        {
                            Volatile.Write(ref m_v6ShouldFilter[tcpHeader->SrcPort], (int)FirewallAction.DontFilterApplication);

                            Volatile.Write(ref m_v6EncryptionHints[tcpHeader->SrcPort], (tcpHeader->DstPort == s_httpsStandardPort || tcpHeader->DstPort == s_httpsAltPort));
                        }
                        break;

                        case false:
                        {
                            Volatile.Write(ref m_v4ShouldFilter[tcpHeader->SrcPort], (int)FirewallAction.DontFilterApplication);

                            Volatile.Write(ref m_v4EncryptionHints[tcpHeader->SrcPort], (tcpHeader->DstPort == s_httpsStandardPort || tcpHeader->DstPort == s_httpsAltPort));
                        }
                        break;
                        }
                    }
                    else
                    {
                        LoggerProxy.Default.Info("RESPONSE");
                        switch (isIpv6)
                        {
                        case true:
                        {
                            Volatile.Write(ref m_v6ShouldFilter[tcpHeader->SrcPort], (int)response.Action);

                            Volatile.Write(ref m_v6EncryptionHints[tcpHeader->SrcPort], response.EncryptedHint ?? (tcpHeader->DstPort == s_httpsStandardPort || tcpHeader->DstPort == s_httpsAltPort));
                        }
                        break;

                        case false:
                        {
                            Volatile.Write(ref m_v4ShouldFilter[tcpHeader->SrcPort], (int)response.Action);

                            Volatile.Write(ref m_v4EncryptionHints[tcpHeader->SrcPort], response.EncryptedHint ?? (tcpHeader->DstPort == s_httpsStandardPort || tcpHeader->DstPort == s_httpsAltPort));
                        }
                        break;
                        }
                    }
                }
            }
            else
            {
                LoggerProxy.Default.Info("How on earth do we not have a valid TCP header.");

                // Somehow we fail to have even a valid TCP header here. Let the connection go
                // through, but warn.
                LoggerProxy.Default.Warn("TCP header was a null pointer. Allowing packet.");

                Volatile.Write(ref m_v4ShouldFilter[tcpHeader->SrcPort], (int)FirewallAction.DontFilterApplication);
            }
        }