// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Net; using System.Data; using System.Security.Cryptography; namespace Spludlow { public class Credentials { private static object Lock = new object(); private static DataTable CachedTable = null; public static string Filename = null; static Credentials() { StringBuilder name = new StringBuilder(Spludlow.Config.ProgramData); name.Append(@"\Config\Credentials\"); name.Append(Environment.UserDomainName); name.Append("@"); name.Append(Environment.UserName); name.Append(".txt"); Filename = name.ToString(); lock (Lock) { CachedTable = Load(); } } public static void Refresh() { lock (Lock) { CachedTable = Load(); } } private static DataTable Load() { if (File.Exists(Filename) == false) { return Spludlow.Data.TextTable.ReadText(new string[] { "Key UserName Password Domain", "String* String String String", }); } string textTable = Spludlow.Io.Files.SafeReadAllText(Filename, Encoding.UTF8); DataTable table = Spludlow.Data.TextTable.ReadText(textTable, false); return table; } private static void Save(DataTable cryTable) { string textTable = Spludlow.Data.TextTable.Write(cryTable); Spludlow.Io.Files.SafeWriteAllText(Filename, textTable, Encoding.UTF8); } public static NetworkCredential GetCredential(string key) { lock (Lock) { DataRow row = CachedTable.Rows.Find(key); if (row == null) throw new ApplicationException("Credential not found:\t" + key); NetworkCredential credential = new NetworkCredential(); credential.UserName = Unprotect(row, "UserName"); credential.Password = Unprotect(row, "Password"); credential.Domain = Unprotect(row, "Domain"); return credential; } } public static string GetPassword(string key) { lock (Lock) { DataRow row = CachedTable.Rows.Find(key); if (row == null) throw new ApplicationException("Credential not found:\t" + key); return Unprotect(row, "Password"); } } public static void Set(string key, string userName, string password, string domain) { DataTable table = Load(); // Should keep locked DataRow row = table.Rows.Find(key); if (row != null) { for (int index = 1; index < table.Columns.Count; ++index) row[index] = DBNull.Value; } else { row = table.NewRow(); row["Key"] = key; table.Rows.Add(row); } string[] cryTexts = new string[] { null, Protect(userName), Protect(password), Protect(domain), }; for (int columnIndex = 1; columnIndex < table.Columns.Count; ++columnIndex) { string cryText = cryTexts[columnIndex]; if (cryText != null) row[columnIndex] = cryText; } Save(table); } private static string Unprotect(DataRow row, string columnName) { if (row.IsNull(columnName) == true) return null; string encodedBase64 = (string)row[columnName]; if (encodedBase64.Length == 0) return null; byte[] encodedData = Convert.FromBase64String(encodedBase64); byte[] data = ProtectedData.Unprotect(encodedData, null, DataProtectionScope.CurrentUser); string text = Encoding.UTF8.GetString(data); return text; } private static string Protect(string clearText) { if (clearText == null || clearText.Length == 0) return null; byte[] clearData = Encoding.UTF8.GetBytes(clearText); byte[] cryData = ProtectedData.Protect(clearData, null, DataProtectionScope.CurrentUser); string cryText = Convert.ToBase64String(cryData); return cryText; } public static void DecodeAll(string directory) { foreach (string host in Spludlow.Config.Hosts()) { string filename = directory + @"\" + host + ".txt"; Decode(host, filename); } } public static void Decode(string filename) { DataSet dataSet = Decode(); Spludlow.Data.TextTable.Write(filename, dataSet.Tables[0], Encoding.UTF8); } public static void Decode(string host, string filename) { DataSet dataSet = (DataSet)Spludlow.Call.Now(host, "Spludlow", "Spludlow.Credentials", "Decode"); Spludlow.Data.TextTable.Write(filename, dataSet.Tables[0], Encoding.UTF8); } public static DataSet Decode() { DataTable cryTable = Load(); DataTable clearTable = cryTable.Clone(); foreach (DataRow row in cryTable.Rows) { DataRow clearRow = clearTable.NewRow(); clearRow["Key"] = (string)row["Key"]; for (int columnIndex = 1; columnIndex < clearTable.Columns.Count; ++columnIndex) { string columnName = clearTable.Columns[columnIndex].ColumnName; string clearData = Unprotect(row, columnName); if (clearData != null) clearRow[columnIndex] = clearData; } clearTable.Rows.Add(clearRow); } return Spludlow.Data.ADO.WireDataSet(clearTable); } public static void EncodeAll(string directory) { foreach (string host in Spludlow.Config.Hosts()) { string filename = directory + @"\" + host + ".txt"; Encode(host, filename); } } public static void Encode(string filename) { DataTable clearTable = Spludlow.Data.TextTable.ReadFile(filename); DataSet dataSet = new DataSet(); dataSet.Tables.Add(clearTable); Encode(dataSet); } public static void Encode(string host, string filename) { DataTable clearTable = Spludlow.Data.TextTable.ReadFile(filename); Spludlow.Call.Now(host, "Spludlow", "Spludlow.Credentials", "Encode", new object[] { Spludlow.Data.ADO.WireDataSet(clearTable) }); } public static void Encode(DataSet clearDataSet) { DataTable clearTable = clearDataSet.Tables[0]; DataTable cryTable = clearTable.Clone(); foreach (DataRow clearRow in clearTable.Rows) { DataRow cryRow = cryTable.NewRow(); cryRow["Key"] = (string)clearRow["Key"]; for (int columnIndex = 1; columnIndex < clearTable.Columns.Count; ++columnIndex) { string columnName = clearTable.Columns[columnIndex].ColumnName; if (clearRow.IsNull(columnName) == true) continue; string clearText = (string)clearRow[columnName]; string cryText = Protect(clearText); if (cryText != null) cryRow[columnName] = cryText; } cryTable.Rows.Add(cryRow); } Spludlow.Data.TextTable.Write(Filename, cryTable, Encoding.UTF8); } public static void ImportCurrentUser(string host) { DataSet dataSet = (DataSet)Spludlow.Call.Now(host, "Spludlow", "Spludlow.Credentials", "Decode"); DataTable sourceTable = dataSet.Tables[0]; string[] rowContents = new string[sourceTable.Columns.Count]; foreach (DataRow row in sourceTable.Rows) { for (int index = 0; index < row.Table.Columns.Count; ++index) { rowContents[index] = null; if (row.IsNull(index) == false) rowContents[index] = (string)row[index]; } //if (rowContents[0] == "SecurityKey") // ??? // continue; Set(rowContents[0], rowContents[1], rowContents[2], rowContents[3]); } } } }