Exemple #1
0
        /// <summary>
        /// Easy way to execute processes. Timeout to complete is 30 seconds.
        /// </summary>
        /// <param name="program">Program to run</param>
        /// <param name="args">Arguments</param>
        /// <param name="allowedExitCode">Allowed exit codes, if empty not checked, otherwise a mismatch will throw an exception.</param>
        public static void StartProcessAndWait(string program, string args, params int[] allowedExitCode)
        {
            IPBanLog.Info($"Executing process {program} {args}...");

            var p = new Process
            {
                StartInfo = new ProcessStartInfo(program, args)
                {
                    CreateNoWindow  = true,
                    UseShellExecute = false,
                    WindowStyle     = ProcessWindowStyle.Hidden,
                    Verb            = "runas"
                }
            };

            p.Start();
            if (!p.WaitForExit(30000))
            {
                p.Kill();
            }
            if (allowedExitCode.Length != 0 && Array.IndexOf(allowedExitCode, p.ExitCode) < 0)
            {
                throw new ApplicationException($"Program {program} {args}: failed with exit code {p.ExitCode}");
            }
        }
        private bool PingFile(WatchedFile file, FileStream fs)
        {
            const int maxCountBeforeNewline = 1024;
            int       b;
            long      lastNewlinePos     = -1;
            long      end                = Math.Min(file.LastLength, fs.Length);
            int       countBeforeNewline = 0;

            fs.Position = file.LastPosition;

            IPBanLog.Info("Processing watched file {0}, len = {1}, pos = {2}", file.FileName, file.LastLength, file.LastPosition);

            while (fs.Position < end && countBeforeNewline++ != maxCountBeforeNewline)
            {
                // read until last \n is found
                b = fs.ReadByte();
                if (b == '\n')
                {
                    lastNewlinePos     = fs.Position - 1;
                    countBeforeNewline = 0;
                }
            }

            if (countBeforeNewline == maxCountBeforeNewline)
            {
                throw new InvalidOperationException($"Log file '{fileMask}' may not be a plain text new line delimited file");
            }

            if (lastNewlinePos > -1)
            {
                try
                {
                    // we could read line by line by going one byte at a time, but the hope here is that by taking
                    // advantage of stream reader and binary reader read bytes we can get some improved cpu usage
                    // at the expense of having to store all the bytes in memory for a small time
                    fs.Position = file.LastPosition;
                    byte[] bytes = new BinaryReader(fs).ReadBytes((int)(lastNewlinePos - fs.Position));

                    using (StreamReader reader = new StreamReader(new MemoryStream(bytes), Encoding.UTF8))
                    {
                        string line;
                        while ((line = reader.ReadLine()) != null)
                        {
                            line = line.Trim();
                            if (!OnProcessLine(line) || (ProcessLine != null && !ProcessLine(line)))
                            {
                                break;
                            }
                        }
                    }
                }
                finally
                {
                    // set file position for next ping
                    fs.Position = file.LastPosition = ++lastNewlinePos;
                }
            }

            return(maxFileSize > 0 && fs.Length > maxFileSize);
        }
Exemple #3
0
        private async Task SetNetworkInfo()
        {
            if (string.IsNullOrWhiteSpace(FQDN))
            {
                string serverName = System.Environment.MachineName;
                try
                {
                    FQDN = (await DnsLookup.GetHostEntryAsync(serverName)).HostName;
                }
                catch
                {
                    FQDN = serverName;
                }
            }

            if (string.IsNullOrWhiteSpace(LocalIPAddressString))
            {
                try
                {
                    LocalIPAddressString = (await DnsLookup.GetLocalIPAddresses()).FirstOrDefault()?.ToString();
                    IPBanLog.Info("Local ip address: {0}", LocalIPAddressString);
                }
                catch
                {
                    // sometimes dns will fail, there is nothing that can be done, don't bother logging
                }
            }

            if (string.IsNullOrWhiteSpace(RemoteIPAddressString))
            {
                try
                {
                    IPAddress ipAddress = await ExternalIPAddressLookup.LookupExternalIPAddressAsync(RequestMaker, Config.ExternalIPAddressUrl);

                    RemoteIPAddressString = ipAddress.ToString();
                    IPBanLog.Info("Remote ip address: {0}", RemoteIPAddressString);
                }
                catch
                {
                    // sometimes ip check url will fail, there is nothing that can be done, don't bother logging
                }
            }

            // hit start url if first time, if not first time will be ignored
            await GetUrl(UrlType.Start);

            // send update
            await GetUrl(UrlType.Update);

            // request new config file
            await GetUrl(UrlType.Config);
        }
Exemple #4
0
        /// <summary>
        /// Easy way to execute processes. If the process has not finished after timeoutMilliseconds, it is forced killed.
        /// </summary>
        /// <param name="timeoutMilliseconds">Timeout in milliseconds</param>
        /// <param name="program">Program to run</param>
        /// <param name="args">Arguments</param>
        /// <param name="allowedExitCodes">Allowed exit codes, if null or empty it is not checked, otherwise a mismatch will throw an exception.</param>
        /// <returns>Output</returns>
        /// <exception cref="ApplicationException">Exit code did not match allowed exit codes</exception>
        public static string StartProcessAndWait(int timeoutMilliseconds, string program, string args, params int[] allowedExitCodes)
        {
            IPBanLog.Info($"Executing process {program} {args}...");

            var process = new Process
            {
                StartInfo = new ProcessStartInfo(program, args)
                {
                    CreateNoWindow         = true,
                    UseShellExecute        = false,
                    WindowStyle            = ProcessWindowStyle.Hidden,
                    RedirectStandardOutput = true,
                    RedirectStandardError  = true,
                    Verb = processVerb
                }
            };
            StringBuilder output = new StringBuilder();

            process.OutputDataReceived += (object sender, DataReceivedEventArgs e) =>
            {
                lock (output)
                {
                    output.Append("[OUT]: ");
                    output.AppendLine(e.Data);
                }
            };
            process.ErrorDataReceived += (object sender, DataReceivedEventArgs e) =>
            {
                lock (output)
                {
                    output.Append("[ERR]: ");
                    output.AppendLine(e.Data);
                }
            };
            process.Start();
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();
            if (!process.WaitForExit(timeoutMilliseconds))
            {
                lock (output)
                {
                    output.Append("[ERR]: Terminating process due to 60 second timeout");
                }
                process.Kill();
            }
            if (allowedExitCodes.Length != 0 && Array.IndexOf(allowedExitCodes, process.ExitCode) < 0)
            {
                throw new ApplicationException($"Program {program} {args}: failed with exit code {process.ExitCode}, output: {output}");
            }
            return(output.ToString());
        }
        private async Task SetNetworkInfo()
        {
            if (string.IsNullOrWhiteSpace(FQDN))
            {
                string serverName = System.Environment.MachineName;
                try
                {
                    FQDN = System.Net.Dns.GetHostEntry(serverName).HostName;
                }
                catch
                {
                    FQDN = serverName;
                }
            }

            if (string.IsNullOrWhiteSpace(LocalIPAddressString))
            {
                try
                {
                    LocalIPAddressString = DnsLookup.GetLocalIPAddress().Sync()?.ToString();
                    IPBanLog.Info("Local ip address: {0}", LocalIPAddressString);
                }
                catch
                {
                }
            }

            if (string.IsNullOrWhiteSpace(RemoteIPAddressString))
            {
                try
                {
                    IPAddress ipAddress = await ExternalIPAddressLookup.LookupExternalIPAddressAsync(RequestMaker, Config.ExternalIPAddressUrl);

                    RemoteIPAddressString = ipAddress.ToString();
                    IPBanLog.Info("Remote ip address: {0}", RemoteIPAddressString);
                }
                catch
                {
                }
            }

            // hit start url if first time, if not first time will be ignored
            await GetUrl(UrlType.Start);

            // send update
            await GetUrl(UrlType.Update);

            // request new config file
            await GetUrl(UrlType.Config);
        }
Exemple #6
0
        private void Initialize()
        {
            IPBanLog.Info("Initializing IPBan database at {0}", connString);
            SQLitePCL.Batteries.Init();
            ExecuteNonQuery("PRAGMA auto_vacuum = INCREMENTAL;");
            ExecuteNonQuery("PRAGMA journal_mode = WAL;");
            ExecuteNonQuery("CREATE TABLE IF NOT EXISTS IPAddresses (IPAddress VARBINARY(16) NOT NULL, IPAddressText VARCHAR(64) NOT NULL, LastFailedLogin BIGINT NOT NULL, FailedLoginCount BIGINT NOT NULL, BanDate BIGINT NULL, PRIMARY KEY (IPAddress))");
            ExecuteNonQueryIgnoreExceptions("ALTER TABLE IPAddresses ADD COLUMN State INT NOT NULL DEFAULT 0");
            ExecuteNonQueryIgnoreExceptions("ALTER TABLE IPAddresses ADD COLUMN BanEndDate BIGINT NULL");
            ExecuteNonQuery("CREATE INDEX IF NOT EXISTS IPAddresses_LastFailedLoginDate ON IPAddresses (LastFailedLogin)");
            ExecuteNonQuery("CREATE INDEX IF NOT EXISTS IPAddresses_BanDate ON IPAddresses (BanDate)");
            ExecuteNonQuery("CREATE INDEX IF NOT EXISTS IPAddresses_BanEndDate ON IPAddresses (BanEndDate)");
            ExecuteNonQuery("CREATE INDEX IF NOT EXISTS IPAddresses_State ON IPAddresses (State)");

            // set to failed login state if no ban date
            ExecuteNonQuery("UPDATE IPAddresses SET State = 3 WHERE State IN (0, 1) AND BanDate IS NULL");
        }
Exemple #7
0
 private Task StartQueue()
 {
     return(Task.Run(() =>
     {
         try
         {
             while (!taskQueueRunnerCancel.IsCancellationRequested)
             {
                 if (taskQueue.TryTake(out Func <Task> runner, -1, taskQueueRunnerCancel.Token))
                 {
                     try
                     {
                         Task task = runner();
                         task.Wait(-1, taskQueueRunnerCancel.Token);
                         if (taskQueue.Count == 0)
                         {
                             taskEmptyEvent.Set();
                         }
                     }
                     catch (OperationCanceledException)
                     {
                         break;
                     }
                     catch (Exception ex)
                     {
                         IPBanLog.Info(ex.ToString());
                     }
                 }
             }
         }
         catch (Exception ex)
         {
             IPBanLog.Info(ex.ToString());
         }
         Dispose();
     }));
 }
        /// <summary>
        /// Process event viewer XML
        /// </summary>
        /// <param name="xml">XML</param>
        public void ProcessEventViewerXml(string xml)
        {
            IPBanLog.Info("Processing event viewer xml: {0}", xml);

            XmlDocument    doc  = ParseXml(xml);
            IPAddressEvent info = ExtractEventViewerXml(doc);

            if (info != null && info.FoundMatch)
            {
                if (info.Flag.HasFlag(IPAddressEventFlag.FailedLogin))
                {
                    // if fail to add the failed login (bad ip, etc.) exit out
                    if (!AddFailedLoginForEventViewerXml(info, doc))
                    {
                        return;
                    }
                }
                else
                {
                    service.IPBanDelegate?.LoginAttemptSucceeded(info.IPAddress, info.Source, info.UserName).ConfigureAwait(false).GetAwaiter();
                }
                IPBanLog.Info("Event viewer found: {0}, {1}, {2}", info.IPAddress, info.Source, info.UserName);
            }
        }
Exemple #9
0
        /// <summary>
        /// Get an ip address and user name out of text using regex. Regex may contain groups named source_[sourcename] to override the source.
        /// </summary>
        /// <param name="dns">Dns lookup to resolve ip addresses</param>
        /// <param name="regex">Regex</param>
        /// <param name="text">Text</param>
        /// <param name="ipAddress">Found ip address or null if none</param>
        /// <param name="userName">Found user name or null if none</param>
        /// <returns>True if a regex match was found, false otherwise</returns>
        public static IPAddressLogEvent GetIPAddressInfoFromRegex(IDnsLookup dns, Regex regex, string text)
        {
            const string customSourcePrefix = "source_";
            bool         foundMatch         = false;
            string       userName           = null;
            string       ipAddress          = null;
            string       source             = null;
            int          repeatCount        = 1;

            Match repeater = Regex.Match(text, "message repeated (?<count>[0-9]+) times", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);

            if (repeater.Success)
            {
                repeatCount = int.Parse(repeater.Groups["count"].Value, CultureInfo.InvariantCulture);
            }

            foreach (Match m in regex.Matches(text))
            {
                if (!m.Success)
                {
                    continue;
                }

                // check for a user name
                Group userNameGroup = m.Groups["username"];
                if (userNameGroup != null && userNameGroup.Success)
                {
                    userName = (userName ?? userNameGroup.Value.Trim('\'', '\"', '(', ')', '[', ']', '{', '}', ' ', '\r', '\n'));
                }
                Group sourceGroup = m.Groups["source"];
                if (sourceGroup != null && sourceGroup.Success)
                {
                    source = (source ?? sourceGroup.Value.Trim('\'', '\"', '(', ')', '[', ']', '{', '}', ' ', '\r', '\n'));
                }
                if (string.IsNullOrWhiteSpace(source))
                {
                    foreach (Group group in m.Groups)
                    {
                        if (group.Success && group.Name != null && group.Name.StartsWith(customSourcePrefix))
                        {
                            source = group.Name.Substring(customSourcePrefix.Length);
                        }
                    }
                }

                // check if the regex had an ipadddress group
                Group ipAddressGroup = m.Groups["ipaddress"];
                if (ipAddressGroup == null)
                {
                    ipAddressGroup = m.Groups["ipaddress_exact"];
                }
                if (ipAddressGroup != null && ipAddressGroup.Success && !string.IsNullOrWhiteSpace(ipAddressGroup.Value))
                {
                    string tempIPAddress = ipAddressGroup.Value.Trim();

                    // in case of IP:PORT format, try a second time, stripping off the :PORT, saves having to do this in all
                    //  the different ip regex.
                    int  lastColon        = tempIPAddress.LastIndexOf(':');
                    bool isValidIPAddress = IPAddress.TryParse(tempIPAddress, out IPAddress tmp);
                    if (isValidIPAddress || (lastColon >= 0 && IPAddress.TryParse(tempIPAddress.Substring(0, lastColon), out tmp)))
                    {
                        ipAddress  = tmp.ToString();
                        foundMatch = true;
                        break;
                    }

                    // if we are parsing anything as ip address (including dns names)
                    if (ipAddressGroup.Name == "ipaddress" && tempIPAddress != Environment.MachineName && tempIPAddress != "-")
                    {
                        // Check Host by name
                        IPBanLog.Info("Parsing as IP failed, checking dns '{0}'", tempIPAddress);
                        try
                        {
                            IPHostEntry entry = dns.GetHostEntryAsync(tempIPAddress).Sync();
                            if (entry != null && entry.AddressList != null && entry.AddressList.Length > 0)
                            {
                                ipAddress = entry.AddressList.FirstOrDefault().ToString();
                                IPBanLog.Info("Dns result '{0}' = '{1}'", tempIPAddress, ipAddress);
                                foundMatch = true;
                                break;
                            }
                        }
                        catch
                        {
                            IPBanLog.Info("Parsing as dns failed '{0}'", tempIPAddress);
                        }
                    }
                }
                else
                {
                    // found a match but no ip address, that is OK.
                    foundMatch = true;
                }
            }

            return(new IPAddressLogEvent(ipAddress, userName, source, repeatCount, IPAddressEventType.FailedLogin)
            {
                FoundMatch = foundMatch
            });
        }
Exemple #10
0
        private async Task ProcessPendingFailedLogins(IReadOnlyList <IPAddressLogEvent> ipAddresses)
        {
            List <IPAddressLogEvent> bannedIpAddresses = new List <IPAddressLogEvent>();
            object transaction = BeginTransaction();

            try
            {
                foreach (IPAddressLogEvent failedLogin in ipAddresses)
                {
                    try
                    {
                        string ipAddress = failedLogin.IPAddress;
                        string userName  = failedLogin.UserName;
                        string source    = failedLogin.Source;
                        if (IsWhitelisted(ipAddress))
                        {
                            IPBanLog.Warn("Login failure, ignoring whitelisted ip address {0}, {1}, {2}", ipAddress, userName, source);
                        }
                        else
                        {
                            int maxFailedLoginAttempts;
                            if (Config.IsUserNameWhitelisted(userName))
                            {
                                maxFailedLoginAttempts = Config.FailedLoginAttemptsBeforeBanUserNameWhitelist;
                            }
                            else
                            {
                                maxFailedLoginAttempts = Config.FailedLoginAttemptsBeforeBan;
                            }

                            DateTime now = failedLogin.Timestamp;

                            // check for the target user name for additional blacklisting checks
                            bool ipBlacklisted           = Config.IsBlackListed(ipAddress);
                            bool userBlacklisted         = (ipBlacklisted ? false : Config.IsBlackListed(userName));
                            bool editDistanceBlacklisted = (ipBlacklisted || userBlacklisted ? false : !Config.IsUserNameWithinMaximumEditDistanceOfUserNameWhitelist(userName));
                            bool configBlacklisted       = ipBlacklisted || userBlacklisted || editDistanceBlacklisted;
                            int  newCount = ipDB.IncrementFailedLoginCount(ipAddress, UtcNow, failedLogin.Count, transaction);

                            IPBanLog.Warn(now, "Login failure: {0}, {1}, {2}, {3}", ipAddress, userName, source, newCount);

                            // if the ip address is black listed or the ip address has reached the maximum failed login attempts before ban, ban the ip address
                            if (configBlacklisted || newCount >= maxFailedLoginAttempts)
                            {
                                IPBanLog.Info("IP blacklisted: {0}, user name blacklisted: {1}, user name edit distance blacklisted: {2}", ipBlacklisted, userBlacklisted, editDistanceBlacklisted);

                                if (ipDB.TryGetIPAddressState(ipAddress, out IPBanDB.IPAddressState state, transaction) &&
                                    (state == IPBanDB.IPAddressState.Active || state == IPBanDB.IPAddressState.AddPending))
                                {
                                    IPBanLog.Warn(now, "IP {0}, {1}, {2} ban pending.", ipAddress, userName, source);
                                }
                                else
                                {
                                    IPBanLog.Debug("Failed login count {0} >= ban count {1}{2}", newCount, maxFailedLoginAttempts, (configBlacklisted ? " config blacklisted" : string.Empty));

                                    // if delegate and non-zero count, forward on - count of 0 means it was from external source, like a delegate
                                    if (IPBanDelegate != null && failedLogin.Count > 0)
                                    {
                                        await IPBanDelegate.LoginAttemptFailed(ipAddress, source, userName, MachineGuid, OSName, OSVersion, UtcNow);
                                    }
                                    AddBannedIPAddress(ipAddress, source, userName, bannedIpAddresses, now, configBlacklisted, newCount, string.Empty, transaction);
                                }
                            }
                            else
                            {
                                IPBanLog.Debug("Failed login count {0} <= ban count {1}", newCount, maxFailedLoginAttempts);
                                if (IPBanOS.UserIsActive(userName))
                                {
                                    IPBanLog.Warn("Login failed for known active user {0}", userName);
                                }

                                // if delegate and non-zero count, forward on - count of 0 means it was from external source, like a delegate
                                if (IPBanDelegate != null && failedLogin.Count > 0)
                                {
                                    await IPBanDelegate.LoginAttemptFailed(ipAddress, source, userName, MachineGuid, OSName, OSVersion, UtcNow);
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        IPBanLog.Error(ex);
                    }
                }
        private IPAddressEvent ExtractEventViewerXml(XmlDocument doc)
        {
            XmlNode keywordsNode = doc.SelectSingleNode("//Keywords");
            string  keywordsText = keywordsNode.InnerText;

            if (keywordsText.StartsWith("0x"))
            {
                keywordsText = keywordsText.Substring(2);
            }
            ulong          keywordsULONG   = ulong.Parse(keywordsText, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
            IPAddressEvent info            = null;
            bool           foundNotifyOnly = false;

            if (keywordsNode != null)
            {
                // we must match on keywords
                foreach (EventViewerExpressionGroup group in service.Config.WindowsEventViewerGetGroupsMatchingKeywords(keywordsULONG))
                {
                    foreach (EventViewerExpression expression in group.Expressions)
                    {
                        // find all the nodes, try and get an ip from any of them, all must match
                        XmlNodeList nodes = doc.SelectNodes(expression.XPath);

                        if (nodes.Count == 0)
                        {
                            IPBanLog.Info("No nodes found for xpath {0}", expression.XPath);
                            info = null;
                            break;
                        }

                        // if there is a regex, it must match
                        if (string.IsNullOrWhiteSpace(expression.Regex))
                        {
                            // count as a match, do not modify the ip address if it was already set
                            IPBanLog.Info("No regex, so counting as a match");
                        }
                        else
                        {
                            info = null;

                            // try and find an ip from any of the nodes
                            foreach (XmlNode node in nodes)
                            {
                                // if we get a match, stop checking nodes
                                info = IPBanService.GetIPAddressInfoFromRegex(service.DnsLookup, expression.RegexObject, node.InnerText);
                                if (info.FoundMatch)
                                {
                                    if (group.NotifyOnly)
                                    {
                                        foundNotifyOnly = true;
                                    }
                                    else if (foundNotifyOnly)
                                    {
                                        throw new InvalidDataException("Conflicting expressions in event viewer, both failed and success logins matched keywords " + group.Keywords);
                                    }
                                    break;
                                }
                            }

                            if (info != null && !info.FoundMatch)
                            {
                                // match fail, null out ip, we have to match ALL the nodes or we get null ip and do not ban
                                IPBanLog.Info("Regex {0} did not match any nodes with xpath {1}", expression.Regex, expression.XPath);
                                info            = null;
                                foundNotifyOnly = false;
                                break;
                            }
                        }
                    }

                    if (info != null && info.FoundMatch && info.IPAddress != null)
                    {
                        info.Source = info.Source ?? group.Source;
                        break;
                    }
                    info = null; // set null for next attempt
                }
            }

            if (info != null)
            {
                info.Flag = (foundNotifyOnly ? IPAddressEventFlag.SuccessfulLogin : IPAddressEventFlag.FailedLogin);
            }
            return(info);
        }