From 6d0aa05761933f997208911cb255a741b87ceba2 Mon Sep 17 00:00:00 2001 From: KZ Date: Fri, 7 Aug 2015 20:09:13 +0100 Subject: [PATCH] Saved password security --- src/Jackett.Test/Jackett.Test.csproj | 10 +- .../Services/ProtectionServiceTests.cs | 30 ++++ src/Jackett.Test/app.config | 4 + src/Jackett.Test/packages.config | 1 + src/Jackett/Controllers/AdminController.cs | 4 +- src/Jackett/Indexers/AlphaRatio.cs | 3 +- src/Jackett/Indexers/AnimeBytes.cs | 3 +- src/Jackett/Indexers/BB.cs | 3 +- src/Jackett/Indexers/BakaBT.cs | 3 +- src/Jackett/Indexers/BaseIndexer.cs | 9 +- src/Jackett/Indexers/BeyondHD.cs | 3 +- src/Jackett/Indexers/BitHdtv.cs | 3 +- src/Jackett/Indexers/BitMeTV.cs | 3 +- src/Jackett/Indexers/Demonoid.cs | 3 +- src/Jackett/Indexers/FrenchTorrentDb.cs | 3 +- src/Jackett/Indexers/Freshon.cs | 3 +- src/Jackett/Indexers/HDSpace.cs | 3 +- src/Jackett/Indexers/HDTorrents.cs | 3 +- src/Jackett/Indexers/IPTorrents.cs | 3 +- src/Jackett/Indexers/ImmortalSeed.cs | 3 +- src/Jackett/Indexers/MoreThanTV.cs | 3 +- src/Jackett/Indexers/Pretome.cs | 3 +- src/Jackett/Indexers/PrivateHD.cs | 3 +- src/Jackett/Indexers/RUTor.cs | 3 +- src/Jackett/Indexers/SceneAccess.cs | 3 +- src/Jackett/Indexers/SceneTime.cs | 3 +- src/Jackett/Indexers/ShowRSS.cs | 3 +- src/Jackett/Indexers/SpeedCD.cs | 3 +- src/Jackett/Indexers/Strike.cs | 3 +- src/Jackett/Indexers/T411.cs | 3 +- src/Jackett/Indexers/ThePirateBay.cs | 3 +- src/Jackett/Indexers/TorrentBytes.cs | 3 +- src/Jackett/Indexers/TorrentDay.cs | 3 +- src/Jackett/Indexers/TorrentLeech.cs | 3 +- src/Jackett/Indexers/TorrentShack.cs | 3 +- src/Jackett/Indexers/Torrentz.cs | 3 +- src/Jackett/Indexers/nCore.cs | 3 +- src/Jackett/Jackett.csproj | 3 + src/Jackett/JackettProtectedAttribute.cs | 12 ++ src/Jackett/Models/Config/ServerConfig.cs | 15 +- .../Models/IndexerConfig/ConfigurationData.cs | 42 ++++- src/Jackett/Services/ProtectionService.cs | 150 ++++++++++++++++++ src/Jackett/Services/ServerService.cs | 12 +- src/Jackett/Utils/StringUtil.cs | 17 +- 44 files changed, 340 insertions(+), 62 deletions(-) create mode 100644 src/Jackett.Test/Services/ProtectionServiceTests.cs create mode 100644 src/Jackett/JackettProtectedAttribute.cs create mode 100644 src/Jackett/Services/ProtectionService.cs diff --git a/src/Jackett.Test/Jackett.Test.csproj b/src/Jackett.Test/Jackett.Test.csproj index 234f876d1..1f07527e6 100644 --- a/src/Jackett.Test/Jackett.Test.csproj +++ b/src/Jackett.Test/Jackett.Test.csproj @@ -54,6 +54,10 @@ ..\packages\Autofac.WebApi2.Owin.3.2.0\lib\net45\Autofac.Integration.WebApi.Owin.dll True + + ..\packages\AutoMapper.4.0.4\lib\net45\AutoMapper.dll + True + ..\packages\CsQuery.1.3.4\lib\net40\CsQuery.dll True @@ -70,6 +74,7 @@ ..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll True + ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll True @@ -155,6 +160,7 @@ + @@ -162,7 +168,9 @@ - + + Designer + diff --git a/src/Jackett.Test/Services/ProtectionServiceTests.cs b/src/Jackett.Test/Services/ProtectionServiceTests.cs new file mode 100644 index 000000000..e8fb274d5 --- /dev/null +++ b/src/Jackett.Test/Services/ProtectionServiceTests.cs @@ -0,0 +1,30 @@ +using Jackett.Services; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Autofac; + +namespace JackettTest.Services +{ + [TestFixture] + class ProtectionServiceTests : TestBase + { + + [Test] + public void Should_be_able_to_encrypt_and_decrypt() + { + var ss = TestUtil.Container.Resolve(); + ss.Config.InstanceId = "12345678"; + var ps = TestUtil.Container.Resolve(); + var input = "test123"; + var protectedInput = ps.Protect(input); + var output = ps.UnProtect(protectedInput); + + Assert.AreEqual(output, input); + Assert.AreNotEqual(input, protectedInput); + } + } +} diff --git a/src/Jackett.Test/app.config b/src/Jackett.Test/app.config index eda8c8545..c88a03853 100644 --- a/src/Jackett.Test/app.config +++ b/src/Jackett.Test/app.config @@ -22,6 +22,10 @@ + + + + diff --git a/src/Jackett.Test/packages.config b/src/Jackett.Test/packages.config index 3813a8d52..b1b814859 100644 --- a/src/Jackett.Test/packages.config +++ b/src/Jackett.Test/packages.config @@ -4,6 +4,7 @@ + diff --git a/src/Jackett/Controllers/AdminController.cs b/src/Jackett/Controllers/AdminController.cs index b6b9514b4..04f215dbd 100644 --- a/src/Jackett/Controllers/AdminController.cs +++ b/src/Jackett/Controllers/AdminController.cs @@ -153,7 +153,7 @@ namespace Jackett.Controllers var postData = await ReadPostDataJson(); var indexer = indexerService.GetIndexer((string)postData["indexer"]); var config = await indexer.GetConfigurationForSetup(); - jsonReply["config"] = config.ToJson(); + jsonReply["config"] = config.ToJson(null); jsonReply["caps"] = indexer.TorznabCaps.CapsToJson(); jsonReply["name"] = indexer.DisplayName; jsonReply["result"] = "success"; @@ -192,7 +192,7 @@ namespace Jackett.Controllers baseIndexer.ResetBaseConfig(); if (ex is ExceptionWithConfigData) { - jsonReply["config"] = ((ExceptionWithConfigData)ex).ConfigData.ToJson(); + jsonReply["config"] = ((ExceptionWithConfigData)ex).ConfigData.ToJson(null); } else { logger.Error(ex, "Exception in Configure"); diff --git a/src/Jackett/Indexers/AlphaRatio.cs b/src/Jackett/Indexers/AlphaRatio.cs index 4be8a70d7..19e5f5065 100644 --- a/src/Jackett/Indexers/AlphaRatio.cs +++ b/src/Jackett/Indexers/AlphaRatio.cs @@ -25,7 +25,7 @@ namespace Jackett.Indexers private string DownloadUrl { get { return SiteLink + "torrents.php?action=download&id="; } } private string GuidUrl { get { return SiteLink + "torrents.php?torrentid="; } } - public AlphaRatio(IIndexerManagerService i, IWebClient w, Logger l) + public AlphaRatio(IIndexerManagerService i, IWebClient w, Logger l, IProtectionService ps) : base(name: "AlphaRatio", description: "Legendary", link: "https://alpharatio.cc/", @@ -33,6 +33,7 @@ namespace Jackett.Indexers manager: i, client: w, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { webclient = w; diff --git a/src/Jackett/Indexers/AnimeBytes.cs b/src/Jackett/Indexers/AnimeBytes.cs index e9822e95d..b9908c9a7 100644 --- a/src/Jackett/Indexers/AnimeBytes.cs +++ b/src/Jackett/Indexers/AnimeBytes.cs @@ -33,7 +33,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public AnimeBytes(IIndexerManagerService i, IWebClient client, Logger l) + public AnimeBytes(IIndexerManagerService i, IWebClient client, Logger l, IProtectionService ps) : base(name: "AnimeBytes", link: "https://animebytes.tv/", description: "Powered by Tentacles", @@ -41,6 +41,7 @@ namespace Jackett.Indexers client: client, caps: new TorznabCapabilities(TorznabCatType.Anime), logger: l, + p: ps, configData: new ConfigurationDataAnimeBytes()) { } diff --git a/src/Jackett/Indexers/BB.cs b/src/Jackett/Indexers/BB.cs index 8dc1293e8..65edb1901 100644 --- a/src/Jackett/Indexers/BB.cs +++ b/src/Jackett/Indexers/BB.cs @@ -32,7 +32,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public BB(IIndexerManagerService i, Logger l, IWebClient w) + public BB(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps) : base(name: "bB", description: "bB", link: "http://www.reddit.com/r/baconbits/", @@ -40,6 +40,7 @@ namespace Jackett.Indexers manager: i, client: w, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { } diff --git a/src/Jackett/Indexers/BakaBT.cs b/src/Jackett/Indexers/BakaBT.cs index 0416014a5..484650666 100644 --- a/src/Jackett/Indexers/BakaBT.cs +++ b/src/Jackett/Indexers/BakaBT.cs @@ -28,7 +28,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public BakaBT(IIndexerManagerService i, IWebClient wc, Logger l) + public BakaBT(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps) : base(name: "BakaBT", description: "Anime Community", link: "http://bakabt.me/", @@ -36,6 +36,7 @@ namespace Jackett.Indexers manager: i, client: wc, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { } diff --git a/src/Jackett/Indexers/BaseIndexer.cs b/src/Jackett/Indexers/BaseIndexer.cs index 7c951004e..c2b217c65 100644 --- a/src/Jackett/Indexers/BaseIndexer.cs +++ b/src/Jackett/Indexers/BaseIndexer.cs @@ -29,6 +29,8 @@ namespace Jackett.Indexers protected static List cache = new List(); protected static readonly TimeSpan cacheTime = new TimeSpan(0, 9, 0); protected IWebClient webclient; + protected IProtectionService protectionService; + protected string CookieHeader { get { return configData.CookieHeader.Value; } @@ -39,7 +41,7 @@ namespace Jackett.Indexers private List categoryMapping = new List(); - public BaseIndexer(string name, string link, string description, IIndexerManagerService manager, IWebClient client, Logger logger, ConfigurationData configData, TorznabCapabilities caps = null) + public BaseIndexer(string name, string link, string description, IIndexerManagerService manager, IWebClient client, Logger logger, ConfigurationData configData, IProtectionService p, TorznabCapabilities caps = null) { if (!link.EndsWith("/")) throw new Exception("Site link must end with a slash."); @@ -50,6 +52,7 @@ namespace Jackett.Indexers this.logger = logger; indexerService = manager; webclient = client; + protectionService = p; this.configData = configData; @@ -91,7 +94,7 @@ namespace Jackett.Indexers protected virtual void SaveConfig() { - indexerService.SaveConfig(this as IIndexer, configData.ToJson(forDisplay: false)); + indexerService.SaveConfig(this as IIndexer, configData.ToJson(protectionService,forDisplay: false)); } protected void OnParseError(string results, Exception ex) @@ -182,7 +185,7 @@ namespace Jackett.Indexers { if (jsonConfig is JArray) { - configData.LoadValuesFromJson(jsonConfig); + configData.LoadValuesFromJson(jsonConfig, protectionService); IsConfigured = true; } // read and upgrade old settings file format diff --git a/src/Jackett/Indexers/BeyondHD.cs b/src/Jackett/Indexers/BeyondHD.cs index 9c885fd97..4e7c0830f 100644 --- a/src/Jackett/Indexers/BeyondHD.cs +++ b/src/Jackett/Indexers/BeyondHD.cs @@ -28,7 +28,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public BeyondHD(IIndexerManagerService i, Logger l, IWebClient w) + public BeyondHD(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps) : base(name: "BeyondHD", description: "Without BeyondHD, your HDTV is just a TV", link: "https://beyondhd.me/", @@ -36,6 +36,7 @@ namespace Jackett.Indexers manager: i, client: w, logger: l, + p: ps, configData: new ConfigurationDataCookie()) { } diff --git a/src/Jackett/Indexers/BitHdtv.cs b/src/Jackett/Indexers/BitHdtv.cs index 5f6b32a85..f3910a312 100644 --- a/src/Jackett/Indexers/BitHdtv.cs +++ b/src/Jackett/Indexers/BitHdtv.cs @@ -30,7 +30,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public BitHdtv(IIndexerManagerService i, Logger l, IWebClient w) + public BitHdtv(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps) : base(name: "BIT-HDTV", description: "Home of high definition invites", link: "https://www.bit-hdtv.com/", @@ -38,6 +38,7 @@ namespace Jackett.Indexers manager: i, client: w, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { } diff --git a/src/Jackett/Indexers/BitMeTV.cs b/src/Jackett/Indexers/BitMeTV.cs index 47180b9fb..fd555cd72 100644 --- a/src/Jackett/Indexers/BitMeTV.cs +++ b/src/Jackett/Indexers/BitMeTV.cs @@ -32,7 +32,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public BitMeTV(IIndexerManagerService i, Logger l, IWebClient c) + public BitMeTV(IIndexerManagerService i, Logger l, IWebClient c, IProtectionService ps) : base(name: "BitMeTV", description: "TV Episode specialty tracker", link: "http://www.bitmetv.org/", @@ -40,6 +40,7 @@ namespace Jackett.Indexers manager: i, client: c, logger: l, + p: ps, configData: new ConfigurationDataCaptchaLogin()) { } diff --git a/src/Jackett/Indexers/Demonoid.cs b/src/Jackett/Indexers/Demonoid.cs index bd0da646b..44e5d68fe 100644 --- a/src/Jackett/Indexers/Demonoid.cs +++ b/src/Jackett/Indexers/Demonoid.cs @@ -27,7 +27,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public Demonoid(IIndexerManagerService i, Logger l, IWebClient wc) + public Demonoid(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps) : base(name: "Demonoid", description: "Demonoid", link: "http://www.demonoid.pw/", @@ -35,6 +35,7 @@ namespace Jackett.Indexers manager: i, client: wc, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { } diff --git a/src/Jackett/Indexers/FrenchTorrentDb.cs b/src/Jackett/Indexers/FrenchTorrentDb.cs index 85c631fb9..b3e3d67b8 100644 --- a/src/Jackett/Indexers/FrenchTorrentDb.cs +++ b/src/Jackett/Indexers/FrenchTorrentDb.cs @@ -24,7 +24,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public FrenchTorrentDb(IIndexerManagerService i, Logger l, IWebClient c) + public FrenchTorrentDb(IIndexerManagerService i, Logger l, IWebClient c, IProtectionService ps) : base(name: "FrenchTorrentDb", description: "One the biggest French Torrent Tracker", link: "http://www.frenchtorrentdb.com/", @@ -32,6 +32,7 @@ namespace Jackett.Indexers manager: i, client: c, logger: l, + p: ps, configData: new ConfigurationDataCookie()) { } diff --git a/src/Jackett/Indexers/Freshon.cs b/src/Jackett/Indexers/Freshon.cs index fcf01f75d..a6b025b44 100644 --- a/src/Jackett/Indexers/Freshon.cs +++ b/src/Jackett/Indexers/Freshon.cs @@ -33,7 +33,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public Freshon(IIndexerManagerService i, Logger l, IWebClient c) + public Freshon(IIndexerManagerService i, Logger l, IWebClient c, IProtectionService ps) : base(name: "FreshOnTV", description: "Our goal is to provide the latest stuff in the TV show domain", link: "https://freshon.tv/", @@ -41,6 +41,7 @@ namespace Jackett.Indexers manager: i, client: c, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { } diff --git a/src/Jackett/Indexers/HDSpace.cs b/src/Jackett/Indexers/HDSpace.cs index 3c5e6aaad..3cc7012dd 100644 --- a/src/Jackett/Indexers/HDSpace.cs +++ b/src/Jackett/Indexers/HDSpace.cs @@ -28,7 +28,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public HDSpace(IIndexerManagerService i, IWebClient wc, Logger l) + public HDSpace(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps) : base(name: "HD-Space", description: "Sharing The Universe", link: "https://hd-space.org/", @@ -36,6 +36,7 @@ namespace Jackett.Indexers manager: i, client: wc, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { } diff --git a/src/Jackett/Indexers/HDTorrents.cs b/src/Jackett/Indexers/HDTorrents.cs index 1a4a9073b..c11771d9a 100644 --- a/src/Jackett/Indexers/HDTorrents.cs +++ b/src/Jackett/Indexers/HDTorrents.cs @@ -30,7 +30,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public HDTorrents(IIndexerManagerService i, Logger l, IWebClient w) + public HDTorrents(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps) : base(name: "HD-Torrents", description: "HD-Torrents is a private torrent website with HD torrents and strict rules on their content.", link: "http://hdts.ru/",// Of the accessible domains the .ru seems the most reliable. https://hdts.ru | https://hd-torrents.org | https://hd-torrents.net | https://hd-torrents.me @@ -38,6 +38,7 @@ namespace Jackett.Indexers manager: i, client: w, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { } diff --git a/src/Jackett/Indexers/IPTorrents.cs b/src/Jackett/Indexers/IPTorrents.cs index 289cd2dee..ed844114f 100644 --- a/src/Jackett/Indexers/IPTorrents.cs +++ b/src/Jackett/Indexers/IPTorrents.cs @@ -29,7 +29,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public IPTorrents(IIndexerManagerService i, IWebClient wc, Logger l) + public IPTorrents(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps) : base(name: "IPTorrents", description: "Always a step ahead.", link: "https://iptorrents.com/", @@ -37,6 +37,7 @@ namespace Jackett.Indexers manager: i, client: wc, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { AddCategoryMapping(72, TorznabCatType.Movies); diff --git a/src/Jackett/Indexers/ImmortalSeed.cs b/src/Jackett/Indexers/ImmortalSeed.cs index 078e44264..f00a3849f 100644 --- a/src/Jackett/Indexers/ImmortalSeed.cs +++ b/src/Jackett/Indexers/ImmortalSeed.cs @@ -30,7 +30,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public ImmortalSeed(IIndexerManagerService i, IWebClient wc, Logger l) + public ImmortalSeed(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps) : base(name: "ImmortalSeed", description: "ImmortalSeed", link: "http://immortalseed.me/", @@ -38,6 +38,7 @@ namespace Jackett.Indexers manager: i, client: wc, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { AddCategoryMapping(32, TorznabCatType.Anime); diff --git a/src/Jackett/Indexers/MoreThanTV.cs b/src/Jackett/Indexers/MoreThanTV.cs index c1a59a1b9..ebfe674b5 100644 --- a/src/Jackett/Indexers/MoreThanTV.cs +++ b/src/Jackett/Indexers/MoreThanTV.cs @@ -31,7 +31,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public MoreThanTV(IIndexerManagerService i, IWebClient c, Logger l) + public MoreThanTV(IIndexerManagerService i, IWebClient c, Logger l, IProtectionService ps) : base(name: "MoreThanTV", description: "ROMANIAN Private Torrent Tracker for TV / MOVIES, and the internal tracker for the release group DRACULA.", link: "https://www.morethan.tv/", @@ -39,6 +39,7 @@ namespace Jackett.Indexers manager: i, client: c, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { } diff --git a/src/Jackett/Indexers/Pretome.cs b/src/Jackett/Indexers/Pretome.cs index 92cbf4282..6b84e3b28 100644 --- a/src/Jackett/Indexers/Pretome.cs +++ b/src/Jackett/Indexers/Pretome.cs @@ -27,7 +27,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public Pretome(IIndexerManagerService i, IWebClient wc, Logger l) + public Pretome(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps) : base(name: "PreToMe", description: "BitTorrent site for High Quality, High Definition (HD) movies and TV Shows", link: "https://pretome.info/", @@ -35,6 +35,7 @@ namespace Jackett.Indexers client: wc, manager: i, logger: l, + p: ps, configData: new ConfigurationDataPinNumber()) { } diff --git a/src/Jackett/Indexers/PrivateHD.cs b/src/Jackett/Indexers/PrivateHD.cs index 7707f2e57..0b60f3333 100644 --- a/src/Jackett/Indexers/PrivateHD.cs +++ b/src/Jackett/Indexers/PrivateHD.cs @@ -29,7 +29,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public PrivateHD(IIndexerManagerService i, IWebClient wc, Logger l) + public PrivateHD(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps) : base(name: "PrivateHD", description: "BitTorrent site for High Quality, High Definition (HD) movies and TV Shows", link: "https://privatehd.to/", @@ -37,6 +37,7 @@ namespace Jackett.Indexers manager: i, client: wc, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { AddCategoryMapping(1, TorznabCatType.Movies); diff --git a/src/Jackett/Indexers/RUTor.cs b/src/Jackett/Indexers/RUTor.cs index 063676461..aaf194c73 100644 --- a/src/Jackett/Indexers/RUTor.cs +++ b/src/Jackett/Indexers/RUTor.cs @@ -32,7 +32,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public RuTor(IIndexerManagerService i, Logger l, IWebClient wc) + public RuTor(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps) : base(name: "RUTor", description: "Свободный торрент трекер", link: "http://rutor.org/", @@ -40,6 +40,7 @@ namespace Jackett.Indexers manager: i, client: wc, logger: l, + p: ps, configData: new ConfigurationDataRuTor(defaultSiteLink)) { TorznabCaps.Categories.Add(TorznabCatType.Anime); diff --git a/src/Jackett/Indexers/SceneAccess.cs b/src/Jackett/Indexers/SceneAccess.cs index b73901192..624dbc10a 100644 --- a/src/Jackett/Indexers/SceneAccess.cs +++ b/src/Jackett/Indexers/SceneAccess.cs @@ -27,7 +27,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public SceneAccess(IIndexerManagerService i, IWebClient c, Logger l) + public SceneAccess(IIndexerManagerService i, IWebClient c, Logger l, IProtectionService ps) : base(name: "SceneAccess", description: "Your gateway to the scene", link: "https://sceneaccess.eu/", @@ -35,6 +35,7 @@ namespace Jackett.Indexers manager: i, client: c, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { } diff --git a/src/Jackett/Indexers/SceneTime.cs b/src/Jackett/Indexers/SceneTime.cs index 2de2accf1..bb3a67115 100644 --- a/src/Jackett/Indexers/SceneTime.cs +++ b/src/Jackett/Indexers/SceneTime.cs @@ -30,7 +30,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public SceneTime(IIndexerManagerService i, Logger l, IWebClient w) + public SceneTime(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps) : base(name: "SceneTime", description: "Always on time", link: "https://www.scenetime.com/", @@ -38,6 +38,7 @@ namespace Jackett.Indexers manager: i, client: w, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { } diff --git a/src/Jackett/Indexers/ShowRSS.cs b/src/Jackett/Indexers/ShowRSS.cs index 730ee62cb..3e87ed81c 100644 --- a/src/Jackett/Indexers/ShowRSS.cs +++ b/src/Jackett/Indexers/ShowRSS.cs @@ -36,7 +36,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public ShowRSS(IIndexerManagerService i, Logger l, IWebClient wc) + public ShowRSS(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps) : base(name: "ShowRSS", description: "showRSS is a service that allows you to keep track of your favorite TV shows", link: defaultSiteLink, @@ -44,6 +44,7 @@ namespace Jackett.Indexers manager: i, client: wc, logger: l, + p: ps, configData: new ConfigurationDataUrl(defaultSiteLink)) { } diff --git a/src/Jackett/Indexers/SpeedCD.cs b/src/Jackett/Indexers/SpeedCD.cs index ab7e442a0..0e9ee5333 100644 --- a/src/Jackett/Indexers/SpeedCD.cs +++ b/src/Jackett/Indexers/SpeedCD.cs @@ -33,7 +33,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public SpeedCD(IIndexerManagerService i, Logger l, IWebClient wc) + public SpeedCD(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps) : base(name: "Speed.cd", description: "Your home now!", link: "http://speed.cd/", @@ -41,6 +41,7 @@ namespace Jackett.Indexers manager: i, client: wc, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { } diff --git a/src/Jackett/Indexers/Strike.cs b/src/Jackett/Indexers/Strike.cs index 5bc4f55d2..508727aa2 100644 --- a/src/Jackett/Indexers/Strike.cs +++ b/src/Jackett/Indexers/Strike.cs @@ -37,7 +37,7 @@ namespace Jackett.Indexers } - public Strike(IIndexerManagerService i, Logger l, IWebClient wc) + public Strike(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps) : base(name: "Strike", description: "Torrent search engine", link: defaultSiteLink, @@ -45,6 +45,7 @@ namespace Jackett.Indexers manager: i, client: wc, logger: l, + p: ps, configData: new ConfigurationDataUrl(defaultSiteLink)) { } diff --git a/src/Jackett/Indexers/T411.cs b/src/Jackett/Indexers/T411.cs index d2d49f1bb..bd9caabb7 100644 --- a/src/Jackett/Indexers/T411.cs +++ b/src/Jackett/Indexers/T411.cs @@ -35,7 +35,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public T411(IIndexerManagerService i, Logger l, IWebClient wc) + public T411(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps) : base(name: "T411", description: "French Torrent Tracker", link: "http://www.t411.io/", @@ -43,6 +43,7 @@ namespace Jackett.Indexers manager: i, client: wc, logger: l, + p: ps, configData: new ConfigurationDataLoginTokin()) { CommentsUrl = SiteLink + "/torrents/{0}"; diff --git a/src/Jackett/Indexers/ThePirateBay.cs b/src/Jackett/Indexers/ThePirateBay.cs index 8115a62b0..86f6bc3c9 100644 --- a/src/Jackett/Indexers/ThePirateBay.cs +++ b/src/Jackett/Indexers/ThePirateBay.cs @@ -37,7 +37,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public ThePirateBay(IIndexerManagerService i, Logger l, IWebClient wc) + public ThePirateBay(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps) : base(name: "The Pirate Bay", description: "The worlds largest bittorrent indexer", link: defaultSiteLink, @@ -45,6 +45,7 @@ namespace Jackett.Indexers manager: i, client: wc, logger: l, + p: ps, configData: new ConfigurationDataUrl(defaultSiteLink)) { } diff --git a/src/Jackett/Indexers/TorrentBytes.cs b/src/Jackett/Indexers/TorrentBytes.cs index d58d567ba..fbaad6a8b 100644 --- a/src/Jackett/Indexers/TorrentBytes.cs +++ b/src/Jackett/Indexers/TorrentBytes.cs @@ -30,7 +30,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public TorrentBytes(IIndexerManagerService i, IWebClient wc, Logger l) + public TorrentBytes(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps) : base(name: "TorrentBytes", description: "A decade of torrentbytes", link: "https://www.torrentbytes.net/", @@ -38,6 +38,7 @@ namespace Jackett.Indexers manager: i, client: wc, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { diff --git a/src/Jackett/Indexers/TorrentDay.cs b/src/Jackett/Indexers/TorrentDay.cs index 6e40c808a..ce74a39f9 100644 --- a/src/Jackett/Indexers/TorrentDay.cs +++ b/src/Jackett/Indexers/TorrentDay.cs @@ -31,7 +31,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public TorrentDay(IIndexerManagerService i, Logger l, IWebClient wc) + public TorrentDay(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps) : base(name: "TorrentDay", description: "TorrentDay", link: "https://torrentday.eu/", @@ -39,6 +39,7 @@ namespace Jackett.Indexers manager: i, client: wc, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { diff --git a/src/Jackett/Indexers/TorrentLeech.cs b/src/Jackett/Indexers/TorrentLeech.cs index 4284d68e4..6e99e639e 100644 --- a/src/Jackett/Indexers/TorrentLeech.cs +++ b/src/Jackett/Indexers/TorrentLeech.cs @@ -29,7 +29,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public TorrentLeech(IIndexerManagerService i, Logger l, IWebClient wc) + public TorrentLeech(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps) : base(name: "TorrentLeech", description: "This is what happens when you seed", link: "http://www.torrentleech.org/", @@ -37,6 +37,7 @@ namespace Jackett.Indexers manager: i, client: wc, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { } diff --git a/src/Jackett/Indexers/TorrentShack.cs b/src/Jackett/Indexers/TorrentShack.cs index 31327f932..69765cf2a 100644 --- a/src/Jackett/Indexers/TorrentShack.cs +++ b/src/Jackett/Indexers/TorrentShack.cs @@ -29,7 +29,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public TorrentShack(IIndexerManagerService i, Logger l, IWebClient wc) + public TorrentShack(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps) : base(name: "TorrentShack", description: "TorrentShack", link: "http://torrentshack.me/", @@ -37,6 +37,7 @@ namespace Jackett.Indexers client: wc, manager: i, logger: l, + p: ps, configData: new ConfigurationDataBasicLogin()) { } diff --git a/src/Jackett/Indexers/Torrentz.cs b/src/Jackett/Indexers/Torrentz.cs index d54b2d17d..ca540a2dd 100644 --- a/src/Jackett/Indexers/Torrentz.cs +++ b/src/Jackett/Indexers/Torrentz.cs @@ -37,7 +37,7 @@ namespace Jackett.Indexers } - public Torrentz(IIndexerManagerService i, Logger l, IWebClient wc) + public Torrentz(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps) : base(name: "Torrentz", description: "Torrentz is a meta-search engine and a Multisearch. This means we just search other search engines.", link: defaultSiteLink, @@ -45,6 +45,7 @@ namespace Jackett.Indexers manager: i, client: wc, logger: l, + p: ps, configData: new ConfigurationDataUrl(defaultSiteLink)) { } diff --git a/src/Jackett/Indexers/nCore.cs b/src/Jackett/Indexers/nCore.cs index c26be2b15..05d6d1b34 100644 --- a/src/Jackett/Indexers/nCore.cs +++ b/src/Jackett/Indexers/nCore.cs @@ -30,7 +30,7 @@ namespace Jackett.Indexers set { base.configData = value; } } - public NCore(IIndexerManagerService i, IWebClient wc, Logger l) + public NCore(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps) : base(name: "nCore", description: "A Hungarian private torrent site.", link: "https://ncore.cc/", @@ -38,6 +38,7 @@ namespace Jackett.Indexers manager: i, client: wc, logger: l, + p: ps, configData: new ConfigurationDataNCore()) { } diff --git a/src/Jackett/Jackett.csproj b/src/Jackett/Jackett.csproj index ad391b285..89b91340a 100644 --- a/src/Jackett/Jackett.csproj +++ b/src/Jackett/Jackett.csproj @@ -148,6 +148,7 @@ True + @@ -207,6 +208,7 @@ + @@ -228,6 +230,7 @@ + diff --git a/src/Jackett/JackettProtectedAttribute.cs b/src/Jackett/JackettProtectedAttribute.cs new file mode 100644 index 000000000..a4e0d521d --- /dev/null +++ b/src/Jackett/JackettProtectedAttribute.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Jackett +{ + public class JackettProtectedAttribute : Attribute + { + } +} diff --git a/src/Jackett/Models/Config/ServerConfig.cs b/src/Jackett/Models/Config/ServerConfig.cs index 36f8e1d39..d2c1b3fc0 100644 --- a/src/Jackett/Models/Config/ServerConfig.cs +++ b/src/Jackett/Models/Config/ServerConfig.cs @@ -19,6 +19,7 @@ namespace Jackett.Models.Config public bool AllowExternal { get; set; } public string APIKey { get; set; } public string AdminPassword { get; set; } + public string InstanceId { get; set; } public string[] GetListenAddresses(bool? external = null) { @@ -38,19 +39,5 @@ namespace Jackett.Models.Config }; } } - - public string GenerateApi() - { - var chars = "abcdefghijklmnopqrstuvwxyz0123456789"; - var randBytes = new byte[32]; - var rngCsp = new RNGCryptoServiceProvider(); - rngCsp.GetBytes(randBytes); - var key = ""; - foreach (var b in randBytes) - { - key += chars[b % chars.Length]; - } - return key; - } } } diff --git a/src/Jackett/Models/IndexerConfig/ConfigurationData.cs b/src/Jackett/Models/IndexerConfig/ConfigurationData.cs index d8cdd75c5..360909642 100644 --- a/src/Jackett/Models/IndexerConfig/ConfigurationData.cs +++ b/src/Jackett/Models/IndexerConfig/ConfigurationData.cs @@ -1,4 +1,5 @@ -using Jackett.Utils; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; @@ -11,6 +12,8 @@ namespace Jackett.Models.IndexerConfig { public abstract class ConfigurationData { + const string PASSWORD_REPLACEMENT = "|||%%PREVJACKPASSWD%%|||"; + public enum ItemType { InputString, @@ -27,12 +30,12 @@ namespace Jackett.Models.IndexerConfig } - public ConfigurationData(JToken json) + public ConfigurationData(JToken json, IProtectionService ps) { - LoadValuesFromJson(json); + LoadValuesFromJson(json, ps); } - public void LoadValuesFromJson(JToken json) + public void LoadValuesFromJson(JToken json, IProtectionService ps= null) { var arr = (JArray)json; foreach (var item in GetItems(forDisplay: false)) @@ -44,7 +47,21 @@ namespace Jackett.Models.IndexerConfig switch (item.ItemType) { case ItemType.InputString: - ((StringItem)item).Value = arrItem.Value("value"); + var sItem = (StringItem)item; + var newValue = arrItem.Value("value"); + + if (string.Equals(item.Name, "password", StringComparison.InvariantCultureIgnoreCase)) + { + if (newValue != PASSWORD_REPLACEMENT) + { + sItem.Value = newValue; + if (ps != null) + sItem.Value = ps.UnProtect(newValue); + } + } else + { + sItem.Value = newValue; + } break; case ItemType.HiddenData: ((HiddenItem)item).Value = arrItem.Value("value"); @@ -56,7 +73,7 @@ namespace Jackett.Models.IndexerConfig } } - public JToken ToJson(bool forDisplay = true) + public JToken ToJson(IProtectionService ps, bool forDisplay = true) { var items = GetItems(forDisplay); var jArray = new JArray(); @@ -71,7 +88,18 @@ namespace Jackett.Models.IndexerConfig case ItemType.InputString: case ItemType.HiddenData: case ItemType.DisplayInfo: - jObject["value"] = ((StringItem)item).Value; + var value = ((StringItem)item).Value; + if (string.Equals(item.Name, "password", StringComparison.InvariantCultureIgnoreCase)) + { + if (string.IsNullOrEmpty(value)) + value = string.Empty; + else if (forDisplay) + value = PASSWORD_REPLACEMENT; + else if (ps != null) + value = ps.Protect(value); + } + + jObject["value"] = value; break; case ItemType.InputBool: jObject["value"] = ((BoolItem)item).Value; diff --git a/src/Jackett/Services/ProtectionService.cs b/src/Jackett/Services/ProtectionService.cs new file mode 100644 index 000000000..710b4f154 --- /dev/null +++ b/src/Jackett/Services/ProtectionService.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace Jackett.Services +{ + public interface IProtectionService + { + string Protect(string plainText); + string UnProtect(string plainText); + } + + public class ProtectionService : IProtectionService + { + DataProtectionScope PROTECTION_SCOPE = DataProtectionScope.LocalMachine; + const string APPLICATION_KEY = "Dvz66r3n8vhTGip2/quiw5ISyM37f7L2iOdupzdKmzkvXGhAgQiWK+6F+4qpxjPVNks1qO7LdWuVqRlzgLzeW8mChC6JnBMUS1Fin4N2nS9lh4XPuCZ1che75xO92Nk2vyXUo9KSFG1hvEszAuLfG2Mcg1r0sVyVXd2gQDU/TbY="; + + IServerService serverService; + + public ProtectionService(IServerService s) + { + serverService = s; + + if (System.Environment.OSVersion.Platform == PlatformID.Unix) + { + // We should not be running as root and will only have access to the local store. + PROTECTION_SCOPE = DataProtectionScope.CurrentUser; + } + } + + public string Protect(string plainText) + { + if (string.IsNullOrEmpty(plainText)) + return string.Empty; + + var plainBytes = Encoding.UTF8.GetBytes(plainText); + var appKey = Convert.FromBase64String(APPLICATION_KEY); + var instanceKey = Encoding.UTF8.GetBytes(serverService.Config.InstanceId); + var entropy = new byte[appKey.Length + instanceKey.Length]; + Buffer.BlockCopy(instanceKey, 0, entropy, 0, instanceKey.Length); + Buffer.BlockCopy(appKey, 0, entropy, instanceKey.Length, appKey.Length); + + var protectedBytes = ProtectedData.Protect(plainBytes, entropy, PROTECTION_SCOPE); + + using (MemoryStream ms = new MemoryStream()) + { + using (RijndaelManaged AES = new RijndaelManaged()) + { + AES.KeySize = 256; + AES.BlockSize = 128; + + var key = new Rfc2898DeriveBytes(instanceKey, instanceKey.Reverse().ToArray(), 64); + AES.Key = key.GetBytes(AES.KeySize / 8); + AES.IV = key.GetBytes(AES.BlockSize / 8); + + AES.Mode = CipherMode.CBC; + + using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write)) + { + cs.Write(protectedBytes, 0, protectedBytes.Length); + cs.Close(); + } + protectedBytes = ms.ToArray(); + } + } + + return Convert.ToBase64String(protectedBytes); + } + + public string UnProtect(string plainText) + { + if (string.IsNullOrEmpty(plainText)) + return string.Empty; + + var protectedBytes = Convert.FromBase64String(plainText); + var instanceKey = Encoding.UTF8.GetBytes(serverService.Config.InstanceId); + + using (MemoryStream ms = new MemoryStream()) + { + using (RijndaelManaged AES = new RijndaelManaged()) + { + AES.KeySize = 256; + AES.BlockSize = 128; + + var key = new Rfc2898DeriveBytes(instanceKey, instanceKey.Reverse().ToArray(), 64); + AES.Key = key.GetBytes(AES.KeySize / 8); + AES.IV = key.GetBytes(AES.BlockSize / 8); + + AES.Mode = CipherMode.CBC; + + using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write)) + { + cs.Write(protectedBytes, 0, protectedBytes.Length); + cs.Close(); + } + protectedBytes = ms.ToArray(); + } + } + + var appKey = Convert.FromBase64String(APPLICATION_KEY); + var entropy = new byte[appKey.Length + instanceKey.Length]; + Buffer.BlockCopy(instanceKey, 0, entropy, 0, instanceKey.Length); + Buffer.BlockCopy(appKey, 0, entropy, instanceKey.Length, appKey.Length); + + var unprotectedBytes = ProtectedData.Unprotect(protectedBytes, entropy, PROTECTION_SCOPE); + return Encoding.UTF8.GetString(unprotectedBytes); + } + + public void Protect(T obj) + { + var type = obj.GetType(); + + foreach(var property in type.GetProperties(BindingFlags.SetProperty |BindingFlags.GetProperty | BindingFlags.Public)) + { + if(property.GetCustomAttributes(typeof(JackettProtectedAttribute), false).Count() > 0) + { + var value = property.GetValue(obj); + if(value is string) + { + var protectedString = Protect(value as string); + property.SetValue(obj, protectedString); + } + } + } + } + + public void UnProtect(T obj) + { + var type = obj.GetType(); + + foreach (var property in type.GetProperties(BindingFlags.SetProperty | BindingFlags.GetProperty | BindingFlags.Public)) + { + if (property.GetCustomAttributes(typeof(JackettProtectedAttribute), false).Count() > 0) + { + var value = property.GetValue(obj); + if (value is string) + { + var unprotectedString = UnProtect(value as string); + property.SetValue(obj, unprotectedString); + } + } + } + } + } +} diff --git a/src/Jackett/Services/ServerService.cs b/src/Jackett/Services/ServerService.cs index cdd82b782..1bf2dd6d4 100644 --- a/src/Jackett/Services/ServerService.cs +++ b/src/Jackett/Services/ServerService.cs @@ -1,6 +1,7 @@ using Autofac; using Jackett.Models.Config; using Jackett.Services; +using Jackett.Utils; using Jackett.Utils.Clients; using Microsoft.Owin.Hosting; using Newtonsoft.Json.Linq; @@ -16,6 +17,7 @@ using System.IO; using System.Linq; using System.Net; using System.Net.NetworkInformation; +using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; @@ -91,12 +93,16 @@ namespace Jackett.Services } if (string.IsNullOrWhiteSpace(config.APIKey)) - { - config.APIKey = config.GenerateApi(); - } + config.APIKey = StringUtil.GenerateRandom(32); configService.SaveConfig(config); } + + if (string.IsNullOrWhiteSpace(config.InstanceId)) + { + config.InstanceId = StringUtil.GenerateRandom(64); + configService.SaveConfig(config); + } } public void SaveConfig() diff --git a/src/Jackett/Utils/StringUtil.cs b/src/Jackett/Utils/StringUtil.cs index ad024effa..2e6765c3f 100644 --- a/src/Jackett/Utils/StringUtil.cs +++ b/src/Jackett/Utils/StringUtil.cs @@ -51,7 +51,6 @@ namespace Jackett.Utils return sb.ToString(); } - public static string GetExceptionDetails(this Exception exception) { var properties = exception.GetType() @@ -74,5 +73,21 @@ namespace Jackett.Utils { return string.Join("&", collection.AllKeys.Select(a => a + "=" + HttpUtility.UrlEncode(collection[a]))); } + + public static string GenerateRandom(int length) + { + var chars = "abcdefghijklmnopqrstuvwxyz0123456789"; + var randBytes = new byte[length]; + using (var rngCsp = new RNGCryptoServiceProvider()) + { + rngCsp.GetBytes(randBytes); + var key = ""; + foreach (var b in randBytes) + { + key += chars[b % chars.Length]; + } + return key; + } + } } }