using System; using System.Diagnostics; using System.Linq; using System.Security.Principal; using NLog; using NetFwTypeLib; namespace NzbDrone.Common { public class SecurityProvider { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private readonly ConfigFileProvider _configFileProvider; private readonly EnvironmentProvider _environmentProvider; private readonly ProcessProvider _processProvider; public SecurityProvider(ConfigFileProvider configFileProvider, EnvironmentProvider environmentProvider, ProcessProvider processProvider) { _configFileProvider = configFileProvider; _environmentProvider = environmentProvider; _processProvider = processProvider; } public SecurityProvider() { } public virtual void MakeAccessible() { if (!IsCurrentUserAdmin()) { Logger.Trace("User is not an admin, skipping."); return; } int port = 0; if (IsFirewallEnabled()) { if(IsNzbDronePortOpen()) { Logger.Trace("NzbDrone port is already open, skipping."); return; } //Close any old ports port = CloseFirewallPort(); //Open the new port OpenFirewallPort(_configFileProvider.Port); } //Skip Url Register if not Vista or 7 if (_environmentProvider.GetOsVersion().Major < 6) return; //Unregister Url (if port != 0) if (port != 0) UnregisterUrl(port); //Register Url RegisterUrl(_configFileProvider.Port); } public virtual bool IsCurrentUserAdmin() { try { var principal = new WindowsPrincipal(WindowsIdentity.GetCurrent()); return principal.IsInRole(WindowsBuiltInRole.Administrator); } catch(Exception ex) { Logger.WarnException("Error checking if the current user is an administrator.", ex); return false; } } public virtual bool IsNzbDronePortOpen() { try { var netFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false); var mgr = (INetFwMgr)Activator.CreateInstance(netFwMgrType); if (!mgr.LocalPolicy.CurrentProfile.FirewallEnabled) return false; var ports = mgr.LocalPolicy.CurrentProfile.GloballyOpenPorts; foreach (INetFwOpenPort p in ports) { if (p.Port == _configFileProvider.Port) return true; } } catch(Exception ex) { Logger.WarnException("Failed to check for open port in firewall", ex); } return false; } private bool OpenFirewallPort(int portNumber) { try { var type = Type.GetTypeFromProgID("HNetCfg.FWOpenPort", false); var port = Activator.CreateInstance(type) as INetFwOpenPort; port.Port = portNumber; port.Name = "NzbDrone"; port.Protocol = NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP; port.Enabled = true; var netFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false); var mgr = (INetFwMgr)Activator.CreateInstance(netFwMgrType); var ports = mgr.LocalPolicy.CurrentProfile.GloballyOpenPorts; ports.Add(port); return true; } catch(Exception ex) { Logger.WarnException("Failed to open port in firewall for NzbDrone " + portNumber, ex); return false; } } private int CloseFirewallPort() { try { var netFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false); var mgr = (INetFwMgr)Activator.CreateInstance(netFwMgrType); var ports = mgr.LocalPolicy.CurrentProfile.GloballyOpenPorts; var portNumber = 8989; foreach (INetFwOpenPort p in ports) { if (p.Name == "NzbDrone") { portNumber = p.Port; break; } } if (portNumber != _configFileProvider.Port) { ports.Remove(portNumber, NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP); return portNumber; } } catch(Exception ex) { Logger.WarnException("Failed to close port in firewall for NzbDrone", ex); } return 0; } private bool IsFirewallEnabled() { try { var netFwMgrType = Type.GetTypeFromProgID("HNetCfg.FwMgr", false); var mgr = (INetFwMgr)Activator.CreateInstance(netFwMgrType); return mgr.LocalPolicy.CurrentProfile.FirewallEnabled; } catch(Exception ex) { Logger.WarnException("Failed to check if the firewall is enabled", ex); return false; } } private bool RegisterUrl(int portNumber) { try { var startInfo = new ProcessStartInfo() { FileName = "netsh.exe", Arguments = string.Format("http add urlacl http://*:{0}/ user=EVERYONE", portNumber) }; var process = _processProvider.Start(startInfo); process.WaitForExit(5000); return true; } catch(Exception ex) { Logger.WarnException("Error registering URL", ex); } return false; } private bool UnregisterUrl(int portNumber) { try { var startInfo = new ProcessStartInfo() { FileName = "netsh.exe", Arguments = string.Format("http delete urlacl http://*:{0}/", portNumber) }; var process = _processProvider.Start(startInfo); process.WaitForExit(5000); return true; } catch (Exception ex) { Logger.WarnException("Error registering URL", ex); } return false; } } }