private HashSet <WatchedFile> GetCurrentWatchedFiles() { HashSet <WatchedFile> watchedFilesCopy = new HashSet <WatchedFile>(); try { // read in existing files that match the mask in the directory being watched if (Directory.Exists(directoryToWatch)) { string replacedDirectory = ReplacePathVars(directoryToWatch); string replacedFileMask = ReplacePathVars(fileMask); foreach (string file in Directory.EnumerateFiles(replacedDirectory, replacedFileMask, SearchOption.TopDirectoryOnly)) { watchedFilesCopy.Add(new WatchedFile(file, new FileInfo(file).Length)); } } } catch { // nothing to do here, something failed enumerating the directory files } lock (watchedFiles) { // remove files that no longer exist foreach (WatchedFile existing in watchedFiles.ToArray()) { if (!watchedFilesCopy.Contains(existing)) { IPBanLog.Debug("Removing parsed log file {0}", existing.FileName); watchedFiles.Remove(existing); } } // add new files foreach (WatchedFile newFile in watchedFilesCopy) { // add the file, will fail if it already exists if (watchedFiles.Add(newFile)) { IPBanLog.Debug("Adding parsed log file {0}", newFile.FileName); } } // make a copy of everything so we can enumerate outside a lock watchedFilesCopy.Clear(); foreach (WatchedFile file in watchedFiles) { watchedFilesCopy.Add(file); } } return(watchedFilesCopy); }
/// <summary> /// Process a line, checking for ip addresses /// </summary> /// <param name="line">Line to process</param> /// <returns>True</returns> protected override bool OnProcessLine(string line) { IPBanLog.Debug("Parsing log file line {0}...", line); bool result = ParseRegex(regexFailure, line, false); if (!result) { result = ParseRegex(regexSuccess, line, true); if (!result) { IPBanLog.Debug("No match for line {0}", line); } } return(true); }
private bool ParseRegex(Regex regex, string line, bool notifyOnly) { if (regex != null) { IPAddressLogEvent info = IPBanService.GetIPAddressInfoFromRegex(dns, regex, line); if (info.FoundMatch) { info.Type = (notifyOnly ? IPAddressEventType.SuccessfulLogin : IPAddressEventType.FailedLogin); info.Source = info.Source ?? Source; IPBanLog.Debug("Log file found match, ip: {0}, user: {1}, source: {2}, count: {3}, type: {4}", info.IPAddress, info.UserName, info.Source, info.Count, info.Type); loginHandler.AddIPAddressLogEvents(new IPAddressLogEvent[] { info }); return(true); } } return(false); }
/// <summary> /// Process event viewer XML /// </summary> /// <param name="xml">XML</param> /// <returns>Log event or null if fail to parse/process</returns> public IPAddressLogEvent ProcessEventViewerXml(string xml) { IPBanLog.Debug("Processing event viewer xml: {0}", xml); XmlDocument doc = ParseXml(xml); IPAddressLogEvent info = ExtractEventViewerXml(doc); if (info != null && info.FoundMatch && (info.Type == IPAddressEventType.FailedLogin || info.Type == IPAddressEventType.SuccessfulLogin)) { if (!FindSourceAndUserNameForInfo(info, doc)) { // bad ip address return(null); } service.AddIPAddressLogEvents(new IPAddressLogEvent[] { info }); IPBanLog.Debug("Event viewer found: {0}, {1}, {2}, {3}", info.IPAddress, info.Source, info.UserName, info.Type); } return(info); }
/// <summary> /// Get a firewall ip address, clean and normalize /// </summary> /// <param name="ipAddress">IP Address</param> /// <param name="normalizedIP">The normalized ip ready to go in the firewall or null if invalid ip address</param> /// <returns>True if ip address can go in the firewall, false otherwise</returns> public static bool TryNormalizeIPAddress(this string ipAddress, out string normalizedIP) { normalizedIP = (ipAddress ?? string.Empty).Trim(); if (string.IsNullOrWhiteSpace(normalizedIP) || normalizedIP == "-" || normalizedIP == "0.0.0.0" || normalizedIP == "127.0.0.1" || normalizedIP == "::0" || normalizedIP == "::1" || !IPAddressRange.TryParse(normalizedIP, out IPAddressRange range)) { // try parsing assuming the ip is followed by a port int pos = normalizedIP.LastIndexOf(':'); if (pos >= 0) { normalizedIP = normalizedIP.Substring(0, pos); if (!IPAddressRange.TryParse(normalizedIP, out range)) { normalizedIP = null; return(false); } } else { normalizedIP = null; return(false); } } try { normalizedIP = (range.Begin.Equals(range.End) ? range.Begin.ToString() : range.ToCidrString()); } catch (Exception ex) { IPBanLog.Debug("Failed to normalize ip {0}, it is not a single ip or cidr range: {1}", ipAddress, ex); return(false); } return(true); }
protected int RunProcess(string program, bool requireExitCode, out IReadOnlyList <string> lines, string commandLine, params object[] args) { commandLine = string.Format(commandLine, args); string bash = "-c \"" + program + " " + commandLine.Replace("\"", "\\\"") + "\""; IPBanLog.Debug("Running firewall process: {0} {1}", program, commandLine); using (Process p = new Process { StartInfo = new ProcessStartInfo { FileName = "/bin/bash", Arguments = bash, UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true } }) { p.Start(); List <string> lineList = new List <string>(); string line; while ((line = p.StandardOutput.ReadLine()) != null) { lineList.Add(line); } lines = lineList; if (!p.WaitForExit(60000)) { IPBanLog.Error("Process {0} {1} timed out", program, commandLine); p.Kill(); } if (requireExitCode && p.ExitCode != 0) { IPBanLog.Error("Process {0} {1} had exit code {2}", program, commandLine, p.ExitCode); } return(p.ExitCode); } }
private void UpdateLogFiles(IPBanConfig newConfig) { // remove existing log files that are no longer in config foreach (IPBanLogFileScanner file in logFilesToParse.ToArray()) { if (newConfig.LogFilesToParse.FirstOrDefault(f => f.PathsAndMasks.Contains(file.PathAndMask)) is null) { file.Dispose(); logFilesToParse.Remove(file); } } foreach (IPBanLogFileToParse newFile in newConfig.LogFilesToParse) { string[] pathsAndMasks = newFile.PathAndMask.Split('\n'); for (int i = 0; i < pathsAndMasks.Length; i++) { string pathAndMask = pathsAndMasks[i].Trim(); if (pathAndMask.Length != 0) { // if we don't have this log file and the platform matches, add it if (logFilesToParse.FirstOrDefault(f => f.PathAndMask == pathAndMask) is null && !string.IsNullOrWhiteSpace(newFile.PlatformRegex) && Regex.IsMatch(IPBanOS.Description, newFile.PlatformRegex.ToString().Trim(), RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)) { // log files use a timer internally and do not need to be updated regularly IPBanLogFileScanner scanner = new IPBanIPAddressLogFileScanner(this, DnsLookup, newFile.Source, pathAndMask, newFile.Recursive, newFile.FailedLoginRegex, newFile.SuccessfulLoginRegex, newFile.MaxFileSize, newFile.PingInterval); logFilesToParse.Add(scanner); IPBanLog.Debug("Adding log file to parse: {0}", pathAndMask); } else { IPBanLog.Debug("Ignoring log file path {0}, regex: {1}", pathAndMask, newFile.PlatformRegex); } } } }
/// <summary> /// Ping the files, this is normally done on a timer, but if you have passed a 0 second /// ping interval to the constructor, you must call this manually /// </summary> public void PingFiles() { try { if (pingTimer != null) { pingTimer.Enabled = false; } } catch { } foreach (WatchedFile file in GetCurrentWatchedFiles()) { try { // if file length has changed, ping the file bool delete = false; // ugly hack to force file to flush using (FileStream fs = new FileStream(file.FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 16)) { try { if (fs.Length != 0) { fs.Position = fs.Length - 1; fs.ReadByte(); } } catch { } } long len = new FileInfo(file.FileName).Length; // if file has shrunk (deleted and recreated for example) reset positions to 0 if (len < file.LastLength || len < file.LastPosition) { file.LastPosition = 0; } // use file info for length compare to avoid doing a full file open if (len != file.LastLength) { using (FileStream fs = new FileStream(file.FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 256)) { file.LastLength = len; delete = PingFile(file, fs); } } else { IPBanLog.Debug("Watched file {0} length has not changed", file.FileName); } if (delete) { try { File.Delete(file.FileName); } catch { // OK someone else might have it open, in which case we have no chance to delete } } } catch (Exception ex) { IPBanLog.Error(ex); } } try { if (pingTimer != null) { pingTimer.Enabled = true; } } catch { } }
private IPAddressLogEvent 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); IPAddressLogEvent info = null; bool foundNotifyOnly = false; if (keywordsNode != null) { // we must match on keywords foreach (EventViewerExpressionGroup group in service.Config.WindowsEventViewerGetGroupsMatchingKeywords(keywordsULONG)) { string userName = null; string source = null; 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.Debug("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.Debug("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); } userName = (userName ?? info.UserName); source = (source ?? info.Source); 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.Debug("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.UserName = (info.UserName ?? userName); info.Source = info.Source ?? source ?? group.Source; break; } info = null; // set null for next attempt } } if (info != null) { info.Type = (foundNotifyOnly ? IPAddressEventType.SuccessfulLogin : IPAddressEventType.FailedLogin); } return(info); }