WIP Lib version 2.39.1

This commit is contained in:
BONNEVILLE Geoffroy
2018-05-22 18:27:44 +02:00
parent 0b95669db0
commit ad02740d8a
43 changed files with 1469 additions and 522 deletions

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A3354969-5AAC-4075-8CBF-EA4805B59EFA}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ModernKeePass.Shared</RootNamespace>
<AssemblyName>ModernKeePass.Shared</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<!-- A reference to the entire .NET Framework is automatically included -->
<None Include="project.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,30 @@
using System.Resources;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ModernKeePass.Shared")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ModernKeePass.Shared")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,10 @@
{
"supports": {},
"dependencies": {
"Microsoft.NETCore.Portable.Compatibility": "1.0.1",
"NETStandard.Library": "1.6.0"
},
"frameworks": {
"netstandard1.2": {}
}
}

View File

@@ -9,8 +9,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModernKeePass.Lib", "Modern
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModernKeePass.LibTest", "ModernKeePassLib.Test\ModernKeePass.LibTest.csproj", "{0A4279CF-2A67-4868-9906-052E50C25F3B}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModernKeePass.LibTest", "ModernKeePassLib.Test\ModernKeePass.LibTest.csproj", "{0A4279CF-2A67-4868-9906-052E50C25F3B}"
EndProject EndProject
Project("{F5034706-568F-408A-B7B3-4D38C6DB8A32}") = "Scripts", "Scripts\Scripts.pssproj", "{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModernKeePass.AppTest", "ModernKeePassApp.Test\ModernKeePass.AppTest.csproj", "{7E80F5E7-724A-4668-9333-B10F5D75C6D0}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModernKeePass.AppTest", "ModernKeePassApp.Test\ModernKeePass.AppTest.csproj", "{7E80F5E7-724A-4668-9333-B10F5D75C6D0}"
EndProject EndProject
Global Global
@@ -89,22 +87,6 @@ Global
{0A4279CF-2A67-4868-9906-052E50C25F3B}.Release|x86.ActiveCfg = Release|x86 {0A4279CF-2A67-4868-9906-052E50C25F3B}.Release|x86.ActiveCfg = Release|x86
{0A4279CF-2A67-4868-9906-052E50C25F3B}.Release|x86.Build.0 = Release|x86 {0A4279CF-2A67-4868-9906-052E50C25F3B}.Release|x86.Build.0 = Release|x86
{0A4279CF-2A67-4868-9906-052E50C25F3B}.Release|x86.Deploy.0 = Release|x86 {0A4279CF-2A67-4868-9906-052E50C25F3B}.Release|x86.Deploy.0 = Release|x86
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|ARM.ActiveCfg = Debug|Any CPU
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|ARM.Build.0 = Debug|Any CPU
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|x64.ActiveCfg = Debug|Any CPU
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|x64.Build.0 = Debug|Any CPU
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|x86.ActiveCfg = Debug|Any CPU
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|x86.Build.0 = Debug|Any CPU
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|Any CPU.Build.0 = Release|Any CPU
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|ARM.ActiveCfg = Release|Any CPU
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|ARM.Build.0 = Release|Any CPU
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|x64.ActiveCfg = Release|Any CPU
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|x64.Build.0 = Release|Any CPU
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|x86.ActiveCfg = Release|Any CPU
{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|x86.Build.0 = Release|Any CPU
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|Any CPU.Build.0 = Debug|Any CPU {7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|Any CPU.Deploy.0 = Debug|Any CPU {7E80F5E7-724A-4668-9333-B10F5D75C6D0}.Debug|Any CPU.Deploy.0 = Debug|Any CPU

View File

@@ -1,14 +0,0 @@
using System;
using ModernKeePass.Services;
namespace ModernKeePass.Attributes
{
[AttributeUsage(AttributeTargets.All)]
public class DatabaseChangedAttribute: Attribute
{
public DatabaseChangedAttribute()
{
DatabaseService.Instance.HasChanged = true;
}
}
}

View File

@@ -1,26 +0,0 @@
using System.Reflection;
using ModernKeePass.Interfaces;
namespace ModernKeePass.Aop
{
public class DatabaseChangedProxy<T>: IProxyInvocationHandler
{
private readonly T _decorated;
private readonly IDatabaseService _databaseService;
public DatabaseChangedProxy(T decorated, IDatabaseService databaseService)
{
_decorated = decorated;
_databaseService = databaseService;
}
public object Invoke(object proxy, MethodInfo method, object[] parameters)
{
object retVal = null;
retVal = method.Invoke(proxy, parameters);
_databaseService.HasChanged = true;
return retVal;
}
}
}

View File

@@ -111,11 +111,9 @@
<Compile Include="Actions\ClipboardAction.cs" /> <Compile Include="Actions\ClipboardAction.cs" />
<Compile Include="Actions\NavigateToUrlAction.cs" /> <Compile Include="Actions\NavigateToUrlAction.cs" />
<Compile Include="Actions\SetupFocusAction.cs" /> <Compile Include="Actions\SetupFocusAction.cs" />
<Compile Include="Aop\DatabaseChangedProxy.cs" />
<Compile Include="App.xaml.cs"> <Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon> <DependentUpon>App.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Aop\DatabaseChanged.cs" />
<Compile Include="Exceptions\DatabaseOpenedException.cs" /> <Compile Include="Exceptions\DatabaseOpenedException.cs" />
<Compile Include="Interfaces\ILicenseService.cs" /> <Compile Include="Interfaces\ILicenseService.cs" />
<Compile Include="Interfaces\IProxyInvocationHandler.cs" /> <Compile Include="Interfaces\IProxyInvocationHandler.cs" />

View File

@@ -136,7 +136,7 @@ namespace ModernKeePassLib.Collections
return false; return false;
} }
if(ps.ReadString() != kvp.Value.ReadString()) return false; if(!ps.Equals(kvp.Value, false)) return false;
} }
if(bNeEqStd) if(bNeEqStd)
@@ -292,12 +292,7 @@ namespace ModernKeePassLib.Collections
if(ps == null) return; // Nothing to do, no assert if(ps == null) return; // Nothing to do, no assert
if(ps.IsProtected != bProtect) if(ps.IsProtected != bProtect)
{ Set(strField, ps.WithProtection(bProtect));
byte[] pbData = ps.ReadUtf8();
Set(strField, new ProtectedString(bProtect, pbData));
if(bProtect) MemUtil.ZeroByteArray(pbData);
}
} }
} }
} }

View File

@@ -352,6 +352,13 @@ namespace ModernKeePassLib.Collections
m_vObjects.Sort(tComparer); m_vObjects.Sort(tComparer);
} }
public void Sort(Comparison<T> tComparison)
{
if(tComparison == null) throw new ArgumentNullException("tComparison");
m_vObjects.Sort(tComparison);
}
public static PwObjectList<T> FromArray(T[] tArray) public static PwObjectList<T> FromArray(T[] tArray)
{ {
if(tArray == null) throw new ArgumentNullException("tArray"); if(tArray == null) throw new ArgumentNullException("tArray");

View File

@@ -223,11 +223,10 @@ namespace ModernKeePassLib.Cryptography
pb = DiagnosticsExt.GetProcessEntropy(); pb = DiagnosticsExt.GetProcessEntropy();
MemUtil.Write(ms, pb); MemUtil.Write(ms, pb);
#elif !KeePassLibSD #elif !KeePassLibSD
Process p = null;
try try
{ {
p = Process.GetCurrentProcess(); using(Process p = Process.GetCurrentProcess())
{
pb = MemUtil.Int64ToBytes(p.Handle.ToInt64()); pb = MemUtil.Int64ToBytes(p.Handle.ToInt64());
MemUtil.Write(ms, pb); MemUtil.Write(ms, pb);
pb = MemUtil.Int32ToBytes(p.HandleCount); pb = MemUtil.Int32ToBytes(p.HandleCount);
@@ -259,12 +258,8 @@ namespace ModernKeePassLib.Cryptography
// pb = MemUtil.UInt32ToBytes((uint)p.SessionId); // pb = MemUtil.UInt32ToBytes((uint)p.SessionId);
// MemUtil.Write(ms, pb); // MemUtil.Write(ms, pb);
} }
catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
finally
{
try { if(p != null) p.Dispose(); }
catch(Exception) { Debug.Assert(false); }
} }
catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
#endif #endif
#endif #endif

View File

@@ -35,6 +35,42 @@ namespace ModernKeePassLib.Cryptography
{ {
public static class CryptoUtil public static class CryptoUtil
{ {
private static bool? g_obProtData = null;
public static bool IsProtectedDataSupported
{
get
{
if(g_obProtData.HasValue) return g_obProtData.Value;
bool b = false;
try
{
Random r = CryptoRandom.NewWeakRandom();
byte[] pbData = new byte[137];
r.NextBytes(pbData);
byte[] pbEnt = new byte[41];
r.NextBytes(pbEnt);
byte[] pbEnc = ProtectedData.Protect(pbData, pbEnt,
DataProtectionScope.CurrentUser);
if((pbEnc != null) && !MemUtil.ArraysEqual(pbEnc, pbData))
{
byte[] pbDec = ProtectedData.Unprotect(pbEnc, pbEnt,
DataProtectionScope.CurrentUser);
if((pbDec != null) && MemUtil.ArraysEqual(pbDec, pbData))
b = true;
}
}
catch(Exception) { Debug.Assert(false); }
Debug.Assert(b); // Should be supported on all systems
g_obProtData = b;
return b;
}
}
public static byte[] HashSha256(byte[] pbData) public static byte[] HashSha256(byte[] pbData)
{ {
if(pbData == null) throw new ArgumentNullException("pbData"); if(pbData == null) throw new ArgumentNullException("pbData");
@@ -165,5 +201,37 @@ namespace ModernKeePassLib.Cryptography
return null; return null;
} }
#endif #endif
public static byte[] ProtectData(byte[] pb, byte[] pbOptEntropy,
DataProtectionScope s)
{
return ProtectDataPriv(pb, true, pbOptEntropy, s);
}
public static byte[] UnprotectData(byte[] pb, byte[] pbOptEntropy,
DataProtectionScope s)
{
return ProtectDataPriv(pb, false, pbOptEntropy, s);
}
private static byte[] ProtectDataPriv(byte[] pb, bool bProtect,
byte[] pbOptEntropy, DataProtectionScope s)
{
if(pb == null) throw new ArgumentNullException("pb");
if((pbOptEntropy != null) && (pbOptEntropy.Length == 0))
pbOptEntropy = null;
if(CryptoUtil.IsProtectedDataSupported)
{
if(bProtect)
return ProtectedData.Protect(pb, pbOptEntropy, s);
return ProtectedData.Unprotect(pb, pbOptEntropy, s);
}
Debug.Assert(false);
byte[] pbCopy = new byte[pb.Length];
Array.Copy(pb, pbCopy, pb.Length);
return pbCopy;
}
} }
} }

View File

@@ -71,9 +71,6 @@ namespace ModernKeePassLib.Cryptography.KeyDerivation
private static bool GCryptInitLib() private static bool GCryptInitLib()
{ {
Debug.Assert(Marshal.SizeOf(typeof(int)) == 4); // Also on 64-bit systems
Debug.Assert(Marshal.SizeOf(typeof(uint)) == 4);
if(!NativeLib.IsUnix()) return false; // Independent of workaround state if(!NativeLib.IsUnix()) return false; // Independent of workaround state
if(!MonoWorkarounds.IsRequired(1468)) return false; // Can be turned off if(!MonoWorkarounds.IsRequired(1468)) return false; // Can be turned off

View File

@@ -239,8 +239,7 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
PwProfile pp = new PwProfile(); PwProfile pp = new PwProfile();
Debug.Assert(psPassword != null); if(psPassword == null) return pp; Debug.Assert(psPassword != null); if(psPassword == null) return pp;
byte[] pbUtf8 = psPassword.ReadUtf8(); char[] vChars = psPassword.ReadChars();
char[] vChars = StrUtil.Utf8.GetChars(pbUtf8);
pp.GeneratorType = PasswordGeneratorType.CharSet; pp.GeneratorType = PasswordGeneratorType.CharSet;
pp.Length = (uint)vChars.Length; pp.Length = (uint)vChars.Length;
@@ -266,7 +265,6 @@ namespace ModernKeePassLib.Cryptography.PasswordGenerator
} }
MemUtil.ZeroArray<char>(vChars); MemUtil.ZeroArray<char>(vChars);
MemUtil.ZeroByteArray(pbUtf8);
return pp; return pp;
} }

View File

@@ -420,11 +420,12 @@ namespace ModernKeePassLib.Cryptography
{ {
if(pbUnprotectedUtf8 == null) { Debug.Assert(false); return 0; } if(pbUnprotectedUtf8 == null) { Debug.Assert(false); return 0; }
char[] vChars = StrUtil.Utf8.GetChars(pbUnprotectedUtf8); char[] v = StrUtil.Utf8.GetChars(pbUnprotectedUtf8);
uint uResult = EstimatePasswordBits(vChars); uint r;
MemUtil.ZeroArray<char>(vChars); try { r = EstimatePasswordBits(v); }
finally { MemUtil.ZeroArray<char>(v); }
return uResult; return r;
} }
private static QeCharType GetCharType(char ch) private static QeCharType GetCharType(char ch)

View File

@@ -22,6 +22,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using System.Security; using System.Security;
using System.Text; using System.Text;
@@ -55,6 +56,25 @@ namespace ModernKeePassLib.Cryptography
/// </summary> /// </summary>
public static void Perform() public static void Perform()
{ {
#if KeePassUAP
Debug.Assert(Marshal.SizeOf<int>() == 4);
Debug.Assert(Marshal.SizeOf<uint>() == 4);
Debug.Assert(Marshal.SizeOf<long>() == 8);
Debug.Assert(Marshal.SizeOf<ulong>() == 8);
Debug.Assert(Marshal.SizeOf<IntPtr>() == IntPtr.Size);
#else
Debug.Assert(Marshal.SizeOf(typeof(int)) == 4);
Debug.Assert(Marshal.SizeOf(typeof(uint)) == 4);
Debug.Assert(Marshal.SizeOf(typeof(long)) == 8);
Debug.Assert(Marshal.SizeOf(typeof(ulong)) == 8);
Debug.Assert(Marshal.SizeOf(typeof(IntPtr)) == IntPtr.Size);
#endif
Debug.Assert((IntPtr.Size == 4) || (IntPtr.Size == 8));
Debug.Assert((int)PwIcon.World == 1);
Debug.Assert((int)PwIcon.Warning == 2);
Debug.Assert((int)PwIcon.BlackBerry == 68);
Random r = CryptoRandom.NewWeakRandom(); Random r = CryptoRandom.NewWeakRandom();
TestFipsComplianceProblems(); // Must be the first test TestFipsComplianceProblems(); // Must be the first test
@@ -76,10 +96,6 @@ namespace ModernKeePassLib.Cryptography
TestStrUtil(); TestStrUtil();
TestUrlUtil(); TestUrlUtil();
Debug.Assert((int)PwIcon.World == 1);
Debug.Assert((int)PwIcon.Warning == 2);
Debug.Assert((int)PwIcon.BlackBerry == 68);
#if KeePassUAP #if KeePassUAP
SelfTestEx.Perform(); SelfTestEx.Perform();
#endif #endif
@@ -944,6 +960,14 @@ namespace ModernKeePassLib.Cryptography
if(ps.ReadString() != str) if(ps.ReadString() != str)
throw new SecurityException("ProtectedString-14"); throw new SecurityException("ProtectedString-14");
} }
ps = new ProtectedString(false, "ABCD");
ps2 = new ProtectedString(true, "EFG");
ps += (ps2 + "HI");
if(!ps.Equals(new ProtectedString(true, "ABCDEFGHI"), true))
throw new SecurityException("ProtectedString-15");
if(!ps.Equals(new ProtectedString(false, "ABCDEFGHI"), false))
throw new SecurityException("ProtectedString-16");
#endif #endif
} }
@@ -1041,6 +1065,30 @@ namespace ModernKeePassLib.Cryptography
throw new InvalidOperationException("StrUtil-Case1"); throw new InvalidOperationException("StrUtil-Case1");
if(string.Equals(@"a<b", @"a>b", StrUtil.CaseIgnoreCmp)) if(string.Equals(@"a<b", @"a>b", StrUtil.CaseIgnoreCmp))
throw new InvalidOperationException("StrUtil-Case2"); throw new InvalidOperationException("StrUtil-Case2");
const string strNL = "\n01\r23\n45\r\n67\r";
string strW = StrUtil.NormalizeNewLines(strNL, true);
string strL = StrUtil.NormalizeNewLines(strNL, false);
if(strW != "\r\n01\r\n23\r\n45\r\n67\r\n")
throw new InvalidOperationException("StrUtil-NewLine1");
if(strL != "\n01\n23\n45\n67\n")
throw new InvalidOperationException("StrUtil-NewLine2");
if(StrUtil.IsNewLineNormalized(strNL.ToCharArray(), true))
throw new InvalidOperationException("StrUtil-NewLine3");
if(StrUtil.IsNewLineNormalized(strNL.ToCharArray(), false))
throw new InvalidOperationException("StrUtil-NewLine4");
if(!StrUtil.IsNewLineNormalized(strW.ToCharArray(), true))
throw new InvalidOperationException("StrUtil-NewLine5");
if(StrUtil.IsNewLineNormalized(strW.ToCharArray(), false))
throw new InvalidOperationException("StrUtil-NewLine6");
if(StrUtil.IsNewLineNormalized(strL.ToCharArray(), true))
throw new InvalidOperationException("StrUtil-NewLine7");
if(!StrUtil.IsNewLineNormalized(strL.ToCharArray(), false))
throw new InvalidOperationException("StrUtil-NewLine8");
if(!StrUtil.IsNewLineNormalized(string.Empty.ToCharArray(), true))
throw new InvalidOperationException("StrUtil-NewLine9");
if(!StrUtil.IsNewLineNormalized(string.Empty.ToCharArray(), false))
throw new InvalidOperationException("StrUtil-NewLine10");
#endif #endif
} }

View File

@@ -275,7 +275,7 @@ namespace ModernKeePassLib.Keys
} }
} }
#else #else
XmlDocument doc = new XmlDocument(); XmlDocument doc = XmlUtilEx.CreateXmlDocument();
doc.Load(ms); doc.Load(ms);
XmlElement el = doc.DocumentElement; XmlElement el = doc.DocumentElement;
@@ -320,49 +320,31 @@ namespace ModernKeePassLib.Keys
#else #else
IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile); IOConnectionInfo ioc = IOConnectionInfo.FromPath(strFile);
#endif #endif
Stream sOut = IOConnection.OpenWrite(ioc); using(Stream s = IOConnection.OpenWrite(ioc))
{
using(XmlWriter xw = XmlUtilEx.CreateXmlWriter(s))
{
xw.WriteStartDocument();
xw.WriteStartElement(RootElementName); // <KeyFile>
#if ModernKeePassLib || KeePassUAP xw.WriteStartElement(MetaElementName); // <Meta>
XmlWriterSettings xws = new XmlWriterSettings(); xw.WriteStartElement(VersionElementName); // <Version>
xws.Encoding = StrUtil.Utf8; xw.WriteString("1.00");
xws.Indent = false; xw.WriteEndElement(); // </Version>
xw.WriteEndElement(); // </Meta>
XmlWriter xtw = XmlWriter.Create(sOut, xws); xw.WriteStartElement(KeyElementName); // <Key>
#else
XmlTextWriter xtw = new XmlTextWriter(sOut, StrUtil.Utf8);
#endif
xtw.WriteStartDocument(); xw.WriteStartElement(KeyDataElementName); // <Data>
xtw.WriteWhitespace("\r\n"); xw.WriteString(Convert.ToBase64String(pbKeyData));
xtw.WriteStartElement(RootElementName); // KeyFile xw.WriteEndElement(); // </Data>
xtw.WriteWhitespace("\r\n\t");
xtw.WriteStartElement(MetaElementName); // Meta xw.WriteEndElement(); // </Key>
xtw.WriteWhitespace("\r\n\t\t");
xtw.WriteStartElement(VersionElementName); // Version
xtw.WriteString("1.00");
xtw.WriteEndElement(); // End Version
xtw.WriteWhitespace("\r\n\t");
xtw.WriteEndElement(); // End Meta
xtw.WriteWhitespace("\r\n\t");
xtw.WriteStartElement(KeyElementName); // Key xw.WriteEndElement(); // </KeyFile>
xtw.WriteWhitespace("\r\n\t\t"); xw.WriteEndDocument();
}
xtw.WriteStartElement(KeyDataElementName); // Data }
xtw.WriteString(Convert.ToBase64String(pbKeyData));
xtw.WriteEndElement(); // End Data
xtw.WriteWhitespace("\r\n\t");
xtw.WriteEndElement(); // End Key
xtw.WriteWhitespace("\r\n");
xtw.WriteEndElement(); // RootElementName
xtw.WriteWhitespace("\r\n");
xtw.WriteEndDocument(); // End KeyFile
xtw.Dispose();
sOut.Dispose();
} }
} }
} }

View File

@@ -65,11 +65,8 @@ namespace ModernKeePassLib.Keys
/// </summary> /// </summary>
public KcpUserAccount() public KcpUserAccount()
{ {
// Test if ProtectedData is supported -- throws an exception if(!CryptoUtil.IsProtectedDataSupported)
// when running on an old system (Windows 98 / ME). throw new PlatformNotSupportedException(); // Windows 98/ME
byte[] pbDummyData = new byte[128];
ProtectedData.Protect(pbDummyData, m_pbEntropy,
DataProtectionScope.CurrentUser);
byte[] pbKey = LoadUserKey(false); byte[] pbKey = LoadUserKey(false);
if(pbKey == null) pbKey = CreateUserKey(); if(pbKey == null) pbKey = CreateUserKey();
@@ -128,7 +125,7 @@ namespace ModernKeePassLib.Keys
byte[] pbProtectedKey = File.ReadAllBytes(strFilePath); byte[] pbProtectedKey = File.ReadAllBytes(strFilePath);
#endif #endif
pbKey = ProtectedData.Unprotect(pbProtectedKey, m_pbEntropy, pbKey = CryptoUtil.UnprotectData(pbProtectedKey, m_pbEntropy,
DataProtectionScope.CurrentUser); DataProtectionScope.CurrentUser);
} }
catch(Exception) catch(Exception)
@@ -149,7 +146,7 @@ namespace ModernKeePassLib.Keys
string strFilePath = GetUserKeyFilePath(true); string strFilePath = GetUserKeyFilePath(true);
byte[] pbRandomKey = CryptoRandom.Instance.GetRandomBytes(64); byte[] pbRandomKey = CryptoRandom.Instance.GetRandomBytes(64);
byte[] pbProtectedKey = ProtectedData.Protect(pbRandomKey, byte[] pbProtectedKey = CryptoUtil.ProtectData(pbRandomKey,
m_pbEntropy, DataProtectionScope.CurrentUser); m_pbEntropy, DataProtectionScope.CurrentUser);
#if ModernKeePassLib #if ModernKeePassLib
var fileStream = StorageFile.GetFileFromPathAsync(strFilePath).GetAwaiter().GetResult().OpenStreamForWriteAsync().GetAwaiter().GetResult(); var fileStream = StorageFile.GetFileFromPathAsync(strFilePath).GetAwaiter().GetResult().OpenStreamForWriteAsync().GetAwaiter().GetResult();

View File

@@ -130,6 +130,8 @@
<Compile Include="Serialization\IOConnectionInfo.cs" /> <Compile Include="Serialization\IOConnectionInfo.cs" />
<Compile Include="Serialization\OldFormatException.cs" /> <Compile Include="Serialization\OldFormatException.cs" />
<Compile Include="Utility\AppLogEx.cs" /> <Compile Include="Utility\AppLogEx.cs" />
<Compile Include="Utility\TypeOverridePool.cs" />
<Compile Include="Utility\XmlUtilEx.cs" />
<None Include="Utility\GfxUtil.cs" /> <None Include="Utility\GfxUtil.cs" />
<Compile Include="Utility\GfxUtil.PCL.cs" /> <Compile Include="Utility\GfxUtil.PCL.cs" />
<Compile Include="Utility\MemUtil.cs" /> <Compile Include="Utility\MemUtil.cs" />
@@ -149,7 +151,7 @@
<None Include="Libs\Windows.winmd" /> <None Include="Libs\Windows.winmd" />
<None Include="ModernKeePassLib.nuspec" /> <None Include="ModernKeePassLib.nuspec" />
<None Include="project.json" /> <None Include="project.json" />
<None Include="Utility\MessageService.cs" /> <Compile Include="Utility\MessageService.cs" />
<None Include="Native\NativeLib.cs" /> <None Include="Native\NativeLib.cs" />
<None Include="Native\NativeMethods.cs" /> <None Include="Native\NativeMethods.cs" />
<None Include="Native\NativeMethods.Unix.cs" /> <None Include="Native\NativeMethods.Unix.cs" />

View File

@@ -2,7 +2,7 @@
<package > <package >
<metadata> <metadata>
<id>ModernKeePassLib</id> <id>ModernKeePassLib</id>
<version>2.38.2</version> <version>2.39.1</version>
<title>ModernKeePassLib</title> <title>ModernKeePassLib</title>
<authors>Geoffroy Bonneville</authors> <authors>Geoffroy Bonneville</authors>
<owners>Geoffroy Bonneville</owners> <owners>Geoffroy Bonneville</owners>
@@ -10,7 +10,7 @@
<projectUrl>https://github.com/wismna/ModernKeePass</projectUrl> <projectUrl>https://github.com/wismna/ModernKeePass</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Portable KeePass Password Management Library that targets .Net Standard and WinRT. Allows reading, editing and writing to KeePass 2.x databases.</description> <description>Portable KeePass Password Management Library that targets .Net Standard and WinRT. Allows reading, editing and writing to KeePass 2.x databases.</description>
<releaseNotes>Deactivation of buggy custom icon implementation</releaseNotes> <releaseNotes>Implementation of KeePass library version 2.39.1</releaseNotes>
<copyright>Copyright © 2018 Geoffroy Bonneville</copyright> <copyright>Copyright © 2018 Geoffroy Bonneville</copyright>
<tags>KeePass KeePassLib Portable PCL NetStandard</tags> <tags>KeePass KeePassLib Portable PCL NetStandard</tags>
<dependencies> <dependencies>

View File

@@ -0,0 +1,190 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text;
namespace ModernKeePassLib.Native
{
internal static class ClipboardU
{
private const string XSel = "xsel";
private const string XSelV = "--version";
private const string XSelR = "--output --clipboard";
private const string XSelC = "--clear --clipboard";
private const string XSelW = "--input --clipboard";
private const string XSelND = " --nodetach";
private const AppRunFlags XSelWF = AppRunFlags.WaitForExit;
private static bool? g_obXSel = null;
public static string GetText()
{
// System.Windows.Forms.Clipboard doesn't work properly,
// see Mono workaround 1530
// string str = GtkGetText();
// if(str != null) return str;
return XSelGetText();
}
public static bool SetText(string strText, bool bMayBlock)
{
string str = (strText ?? string.Empty);
// System.Windows.Forms.Clipboard doesn't work properly,
// see Mono workaround 1530
// if(GtkSetText(str)) return true;
return XSelSetText(str, bMayBlock);
}
// =============================================================
// LibGTK
// Even though GTK+ 3 appears to be loaded already, performing a
// P/Invoke of LibGTK's gtk_init_check function terminates the
// process (!) with the following error message:
// "Gtk-ERROR **: GTK+ 2.x symbols detected. Using GTK+ 2.x and
// GTK+ 3 in the same process is not supported".
/* private static bool GtkInit()
{
try
{
// GTK requires GLib;
// the following throws if and only if GLib is unavailable
NativeMethods.g_free(IntPtr.Zero);
if(NativeMethods.gtk_init_check(IntPtr.Zero, IntPtr.Zero) !=
NativeMethods.G_FALSE)
return true;
Debug.Assert(false);
}
catch(Exception) { Debug.Assert(false); }
return false;
}
private static string GtkGetText()
{
IntPtr lpText = IntPtr.Zero;
try
{
if(GtkInit())
{
IntPtr h = NativeMethods.gtk_clipboard_get(
NativeMethods.GDK_SELECTION_CLIPBOARD);
if(h != IntPtr.Zero)
{
lpText = NativeMethods.gtk_clipboard_wait_for_text(h);
if(lpText != IntPtr.Zero)
return NativeMethods.Utf8ZToString(lpText);
}
}
}
catch(Exception) { Debug.Assert(false); }
finally
{
try { NativeMethods.g_free(lpText); }
catch(Exception) { Debug.Assert(false); }
}
return null;
}
private static bool GtkSetText(string str)
{
IntPtr lpText = IntPtr.Zero;
try
{
if(GtkInit())
{
lpText = NativeMethods.Utf8ZFromString(str ?? string.Empty);
if(lpText == IntPtr.Zero) { Debug.Assert(false); return false; }
bool b = false;
for(int i = 0; i < 2; ++i)
{
IntPtr h = NativeMethods.gtk_clipboard_get((i == 0) ?
NativeMethods.GDK_SELECTION_PRIMARY :
NativeMethods.GDK_SELECTION_CLIPBOARD);
if(h != IntPtr.Zero)
{
NativeMethods.gtk_clipboard_clear(h);
NativeMethods.gtk_clipboard_set_text(h, lpText, -1);
NativeMethods.gtk_clipboard_store(h);
b = true;
}
}
return b;
}
}
catch(Exception) { Debug.Assert(false); }
finally { NativeMethods.Utf8ZFree(lpText); }
return false;
} */
// =============================================================
// XSel
private static bool XSelInit()
{
if(g_obXSel.HasValue) return g_obXSel.Value;
string strTest = NativeLib.RunConsoleApp(XSel, XSelV);
bool b = (strTest != null);
g_obXSel = b;
return b;
}
private static string XSelGetText()
{
if(!XSelInit()) return null;
return NativeLib.RunConsoleApp(XSel, XSelR);
}
private static bool XSelSetText(string str, bool bMayBlock)
{
if(!XSelInit()) return false;
string strOpt = (bMayBlock ? XSelND : string.Empty);
// xsel with an empty input can hang, thus use --clear
if(str.Length == 0)
return (NativeLib.RunConsoleApp(XSel, XSelC + strOpt,
null, XSelWF) != null);
// Use --nodetach to prevent clipboard corruption;
// https://sourceforge.net/p/keepass/bugs/1603/
return (NativeLib.RunConsoleApp(XSel, XSelW + strOpt,
str, XSelWF) != null);
}
}
}

View File

@@ -53,24 +53,6 @@ namespace ModernKeePassLib.Native
set { m_bAllowNative = value; } set { m_bAllowNative = value; }
} }
private static int? g_oiPointerSize = null;
/// <summary>
/// Size of a native pointer (in bytes).
/// </summary>
public static int PointerSize
{
get
{
if(!g_oiPointerSize.HasValue)
#if KeePassUAP
g_oiPointerSize = Marshal.SizeOf<IntPtr>();
#else
g_oiPointerSize = Marshal.SizeOf(typeof(IntPtr));
#endif
return g_oiPointerSize.Value;
}
}
private static ulong? m_ouMonoVersion = null; private static ulong? m_ouMonoVersion = null;
public static ulong MonoVersion public static ulong MonoVersion
{ {
@@ -195,19 +177,21 @@ namespace ModernKeePassLib.Native
t = DesktopType.Xfce; t = DesktopType.Xfce;
else if(strXdg.Equals("MATE", sc)) else if(strXdg.Equals("MATE", sc))
t = DesktopType.Mate; t = DesktopType.Mate;
else if(strXdg.Equals("X-Cinnamon", sc)) else if(strXdg.Equals("X-Cinnamon", sc)) // Mint 18.3
t = DesktopType.Cinnamon; t = DesktopType.Cinnamon;
else if(strXdg.Equals("Pantheon", sc)) // Elementary OS else if(strXdg.Equals("Pantheon", sc)) // Elementary OS
t = DesktopType.Pantheon; t = DesktopType.Pantheon;
else if(strXdg.Equals("KDE", sc) || // Mint 16 else if(strXdg.Equals("KDE", sc) || // Mint 16, Kubuntu 17.10
strGdm.Equals("kde-plasma", sc)) // Ubuntu 12.04 strGdm.Equals("kde-plasma", sc)) // Ubuntu 12.04
t = DesktopType.Kde; t = DesktopType.Kde;
else if(strXdg.Equals("GNOME", sc)) else if(strXdg.Equals("GNOME", sc))
{ {
if(strGdm.Equals("cinnamon", sc)) // Mint 13 if(strGdm.Equals("cinnamon", sc)) // Mint 13
t = DesktopType.Cinnamon; t = DesktopType.Cinnamon;
else t = DesktopType.Gnome; else t = DesktopType.Gnome; // Fedora 27
} }
else if(strXdg.Equals("ubuntu:GNOME", sc))
t = DesktopType.Gnome;
} }
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
} }
@@ -243,6 +227,7 @@ namespace ModernKeePassLib.Native
RunProcessDelegate fnRun = delegate() RunProcessDelegate fnRun = delegate()
{ {
Process pToDispose = null;
try try
{ {
ProcessStartInfo psi = new ProcessStartInfo(); ProcessStartInfo psi = new ProcessStartInfo();
@@ -258,6 +243,7 @@ namespace ModernKeePassLib.Native
if(!string.IsNullOrEmpty(strParams)) psi.Arguments = strParams; if(!string.IsNullOrEmpty(strParams)) psi.Arguments = strParams;
Process p = Process.Start(psi); Process p = Process.Start(psi);
pToDispose = p;
if(strStdInput != null) if(strStdInput != null)
{ {
@@ -274,9 +260,11 @@ namespace ModernKeePassLib.Native
p.WaitForExit(); p.WaitForExit();
else if((f & AppRunFlags.GCKeepAlive) != AppRunFlags.None) else if((f & AppRunFlags.GCKeepAlive) != AppRunFlags.None)
{ {
pToDispose = null; // Thread disposes it
Thread th = new Thread(delegate() Thread th = new Thread(delegate()
{ {
try { p.WaitForExit(); } try { p.WaitForExit(); p.Dispose(); }
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
}); });
th.Start(); th.Start();
@@ -289,6 +277,11 @@ namespace ModernKeePassLib.Native
#else #else
catch(Exception) { } catch(Exception) { }
#endif #endif
finally
{
try { if(pToDispose != null) pToDispose.Dispose(); }
catch(Exception) { Debug.Assert(false); }
}
return null; return null;
}; };

View File

@@ -31,6 +31,14 @@ namespace ModernKeePassLib.Native
{ {
internal const int MAX_PATH = 260; internal const int MAX_PATH = 260;
internal const long INVALID_HANDLE_VALUE = -1;
internal const uint MOVEFILE_REPLACE_EXISTING = 0x00000001;
internal const uint MOVEFILE_COPY_ALLOWED = 0x00000002;
internal const uint FILE_SUPPORTS_TRANSACTIONS = 0x00200000;
internal const int MAX_TRANSACTION_DESCRIPTION_LENGTH = 64;
// internal const uint TF_SFT_SHOWNORMAL = 0x00000001; // internal const uint TF_SFT_SHOWNORMAL = 0x00000001;
// internal const uint TF_SFT_HIDDEN = 0x00000008; // internal const uint TF_SFT_HIDDEN = 0x00000008;
@@ -47,10 +55,9 @@ namespace ModernKeePassLib.Native
internal static bool TransformKey(IntPtr pBuf256, IntPtr pKey256, internal static bool TransformKey(IntPtr pBuf256, IntPtr pKey256,
UInt64 uRounds) UInt64 uRounds)
{ {
if(Marshal.SizeOf(typeof(IntPtr)) == 8) if(IntPtr.Size == 4)
return TransformKey64(pBuf256, pKey256, uRounds);
else
return TransformKey32(pBuf256, pKey256, uRounds); return TransformKey32(pBuf256, pKey256, uRounds);
return TransformKey64(pBuf256, pKey256, uRounds);
} }
[DllImport("KeePassNtv32.dll", EntryPoint = "TransformKeyTimed")] [DllImport("KeePassNtv32.dll", EntryPoint = "TransformKeyTimed")]
@@ -66,10 +73,9 @@ namespace ModernKeePassLib.Native
internal static bool TransformKeyTimed(IntPtr pBuf256, IntPtr pKey256, internal static bool TransformKeyTimed(IntPtr pBuf256, IntPtr pKey256,
ref UInt64 puRounds, UInt32 uSeconds) ref UInt64 puRounds, UInt32 uSeconds)
{ {
if(Marshal.SizeOf(typeof(IntPtr)) == 8) if(IntPtr.Size == 4)
return TransformKeyTimed64(pBuf256, pKey256, ref puRounds, uSeconds);
else
return TransformKeyTimed32(pBuf256, pKey256, ref puRounds, uSeconds); return TransformKeyTimed32(pBuf256, pKey256, ref puRounds, uSeconds);
return TransformKeyTimed64(pBuf256, pKey256, ref puRounds, uSeconds);
} */ } */
#if !KeePassUAP #if !KeePassUAP
@@ -86,10 +92,9 @@ namespace ModernKeePassLib.Native
internal static bool TransformKey(IntPtr pBuf256, IntPtr pKey256, internal static bool TransformKey(IntPtr pBuf256, IntPtr pKey256,
UInt64 uRounds) UInt64 uRounds)
{ {
if(NativeLib.PointerSize == 8) if(IntPtr.Size == 4)
return TransformKey64(pBuf256, pKey256, uRounds);
else
return TransformKey32(pBuf256, pKey256, uRounds); return TransformKey32(pBuf256, pKey256, uRounds);
return TransformKey64(pBuf256, pKey256, uRounds);
} }
[DllImport("KeePassLibC32.dll", EntryPoint = "TransformKeyBenchmark256")] [DllImport("KeePassLibC32.dll", EntryPoint = "TransformKeyBenchmark256")]
@@ -100,9 +105,9 @@ namespace ModernKeePassLib.Native
internal static UInt64 TransformKeyBenchmark(UInt32 uTimeMs) internal static UInt64 TransformKeyBenchmark(UInt32 uTimeMs)
{ {
if(NativeLib.PointerSize == 8) if(IntPtr.Size == 4)
return TransformKeyBenchmark64(uTimeMs);
return TransformKeyBenchmark32(uTimeMs); return TransformKeyBenchmark32(uTimeMs);
return TransformKeyBenchmark64(uTimeMs);
} }
#endif #endif
@@ -116,11 +121,64 @@ namespace ModernKeePassLib.Native
internal static bool TfShowLangBar(uint dwFlags) internal static bool TfShowLangBar(uint dwFlags)
{ {
if(Marshal.SizeOf(typeof(IntPtr)) == 8) if(IntPtr.Size == 4) return TF_ShowLangBar32(dwFlags);
return TF_ShowLangBar64(dwFlags); return TF_ShowLangBar64(dwFlags);
return TF_ShowLangBar32(dwFlags);
} */ } */
[DllImport("KeePassLibC32.dll", EntryPoint = "ProtectProcessWithDacl")]
private static extern void ProtectProcessWithDacl32();
[DllImport("KeePassLibC64.dll", EntryPoint = "ProtectProcessWithDacl")]
private static extern void ProtectProcessWithDacl64();
internal static void ProtectProcessWithDacl()
{
try
{
if(NativeLib.IsUnix()) return;
if(IntPtr.Size == 4) ProtectProcessWithDacl32();
else ProtectProcessWithDacl64();
}
catch(Exception) { Debug.Assert(false); }
}
[DllImport("Kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseHandle(IntPtr hObject);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false,
SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetVolumeInformation(string lpRootPathName,
StringBuilder lpVolumeNameBuffer, UInt32 nVolumeNameSize,
ref UInt32 lpVolumeSerialNumber, ref UInt32 lpMaximumComponentLength,
ref UInt32 lpFileSystemFlags, StringBuilder lpFileSystemNameBuffer,
UInt32 nFileSystemNameSize);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false,
SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool MoveFileEx(string lpExistingFileName,
string lpNewFileName, UInt32 dwFlags);
[DllImport("KtmW32.dll", CharSet = CharSet.Unicode, ExactSpelling = true,
SetLastError = true)]
internal static extern IntPtr CreateTransaction(IntPtr lpTransactionAttributes,
IntPtr lpUOW, UInt32 dwCreateOptions, UInt32 dwIsolationLevel,
UInt32 dwIsolationFlags, UInt32 dwTimeout, string lpDescription);
[DllImport("KtmW32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CommitTransaction(IntPtr hTransaction);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false,
SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool MoveFileTransacted(string lpExistingFileName,
string lpNewFileName, IntPtr lpProgressRoutine, IntPtr lpData,
UInt32 dwFlags, IntPtr hTransaction);
#if (!KeePassLibSD && !KeePassUAP) #if (!KeePassLibSD && !KeePassUAP)
[DllImport("ShlWApi.dll", CharSet = CharSet.Auto)] [DllImport("ShlWApi.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]

View File

@@ -1,6 +1,6 @@
/* /*
KeePass Password Safe - The Open-Source Password Manager KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2017 Dominik Reichl <dominik.reichl@t-online.de> Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -27,7 +27,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("wismna")] [assembly: AssemblyCompany("wismna")]
[assembly: AssemblyProduct("ModernKeePassLib")] [assembly: AssemblyProduct("ModernKeePassLib")]
[assembly: AssemblyCopyright("Copyright © 2017 Geoffroy Bonneville")] [assembly: AssemblyCopyright("Copyright © 2018 Geoffroy Bonneville")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
@@ -40,5 +40,5 @@ using System.Runtime.InteropServices;
#endif #endif
// Assembly version information // Assembly version information
[assembly: AssemblyVersion("2.37.0.2000")] [assembly: AssemblyVersion("2.39.1.*")]
[assembly: AssemblyFileVersion("2.37.0.2000")] [assembly: AssemblyFileVersion("2.39.1.0")]

View File

@@ -607,9 +607,8 @@ namespace ModernKeePassLib
m_bDatabaseOpened = true; m_bDatabaseOpened = true;
m_bModified = true; m_bModified = true;
m_pgRootGroup = new PwGroup(true, true, m_pgRootGroup = new PwGroup(true, true, UrlUtil.StripExtension(
UrlUtil.StripExtension(UrlUtil.GetFileName(ioConnection.Path)), UrlUtil.GetFileName(ioConnection.Path)), PwIcon.FolderOpen);
PwIcon.FolderOpen);
m_pgRootGroup.IsExpanded = true; m_pgRootGroup.IsExpanded = true;
} }
@@ -636,15 +635,15 @@ namespace ModernKeePassLib
m_pgRootGroup.IsExpanded = true; m_pgRootGroup.IsExpanded = true;
m_pwUserKey = pwKey; m_pwUserKey = pwKey;
m_bModified = false; m_bModified = false;
KdbxFile kdbx = new KdbxFile(this); KdbxFile kdbx = new KdbxFile(this);
kdbx.DetachBinaries = m_strDetachBins; kdbx.DetachBinaries = m_strDetachBins;
Stream s = IOConnection.OpenRead(ioSource); using(Stream s = IOConnection.OpenRead(ioSource))
{
kdbx.Load(s, KdbxFormat.Default, slLogger); kdbx.Load(s, KdbxFormat.Default, slLogger);
s.Dispose(); }
m_pbHashOfLastIO = kdbx.HashOfFileOnDisk; m_pbHashOfLastIO = kdbx.HashOfFileOnDisk;
m_pbHashOfFileOnDisk = kdbx.HashOfFileOnDisk; m_pbHashOfFileOnDisk = kdbx.HashOfFileOnDisk;
@@ -673,17 +672,21 @@ namespace ModernKeePassLib
if(m_bUseFileLocks) fl = new FileLock(m_ioSource); if(m_bUseFileLocks) fl = new FileLock(m_ioSource);
try try
{ {
FileTransactionEx ft = new FileTransactionEx(m_ioSource, KdbxFile kdbx = new KdbxFile(this);
m_bUseFileTransactions);
Stream s = ft.OpenWrite();
KdbxFile kdb = new KdbxFile(this); using(FileTransactionEx ft = new FileTransactionEx(m_ioSource,
kdb.Save(s, null, KdbxFormat.Default, slLogger); m_bUseFileTransactions))
{
using(Stream s = ft.OpenWrite())
{
kdbx.Save(s, null, KdbxFormat.Default, slLogger);
}
ft.CommitWrite(); ft.CommitWrite();
}
m_pbHashOfLastIO = kdb.HashOfFileOnDisk; m_pbHashOfLastIO = kdbx.HashOfFileOnDisk;
m_pbHashOfFileOnDisk = kdb.HashOfFileOnDisk; m_pbHashOfFileOnDisk = kdbx.HashOfFileOnDisk;
Debug.Assert(m_pbHashOfFileOnDisk != null); Debug.Assert(m_pbHashOfFileOnDisk != null);
} }
finally { if(fl != null) fl.Dispose(); } finally { if(fl != null) fl.Dispose(); }
@@ -1856,10 +1859,7 @@ namespace ModernKeePassLib
using(Stream sOut = IOConnection.OpenWrite(iocBk)) using(Stream sOut = IOConnection.OpenWrite(iocBk))
{ {
MemUtil.CopyStream(sIn, sOut); MemUtil.CopyStream(sIn, sOut);
sOut.Close();
} }
sIn.Close();
} }
} }
@@ -1969,7 +1969,7 @@ namespace ModernKeePassLib
if(psB == null) return false; if(psB == null) return false;
// Ignore protection setting, compare values only // Ignore protection setting, compare values only
if(!kvpA.Value.ReadString().Equals(psB.ReadString())) return false; if(!psB.Equals(kvpA.Value, false)) return false;
} }
foreach(KeyValuePair<string, ProtectedString> kvpB in b.Strings) foreach(KeyValuePair<string, ProtectedString> kvpB in b.Strings)
@@ -1980,22 +1980,18 @@ namespace ModernKeePassLib
if(psA == null) return false; if(psA == null) return false;
// Must be equal by logic // Must be equal by logic
Debug.Assert(kvpB.Value.ReadString().Equals(psA.ReadString())); Debug.Assert(psA.Equals(kvpB.Value, false));
} }
if(a.Binaries.UCount != b.Binaries.UCount) return false; if(a.Binaries.UCount != b.Binaries.UCount) return false;
foreach(KeyValuePair<string, ProtectedBinary> kvpBin in a.Binaries) foreach(KeyValuePair<string, ProtectedBinary> kvpBin in a.Binaries)
{ {
ProtectedBinary pbA = kvpBin.Value;
ProtectedBinary pbB = b.Binaries.Get(kvpBin.Key); ProtectedBinary pbB = b.Binaries.Get(kvpBin.Key);
if(pbB == null) return false; if(pbB == null) return false;
// Ignore protection setting, compare values only // Ignore protection setting, compare values only
byte[] pbDataA = kvpBin.Value.ReadData(); if(!pbB.Equals(pbA, false)) return false;
byte[] pbDataB = pbB.ReadData();
bool bBinEq = MemUtil.ArraysEqual(pbDataA, pbDataB);
MemUtil.ZeroByteArray(pbDataA);
MemUtil.ZeroByteArray(pbDataB);
if(!bBinEq) return false;
} }
return true; return true;

View File

@@ -55,18 +55,18 @@ namespace ModernKeePassLib
/// e.g. 2.19 = 0x02130000. /// e.g. 2.19 = 0x02130000.
/// It is highly recommended to use <c>FileVersion64</c> instead. /// It is highly recommended to use <c>FileVersion64</c> instead.
/// </summary> /// </summary>
public const uint Version32 = 0x02260000; public const uint Version32 = 0x02270100;
/// <summary> /// <summary>
/// Version, encoded as 64-bit unsigned integer /// Version, encoded as 64-bit unsigned integer
/// (component-wise, 16 bits per component). /// (component-wise, 16 bits per component).
/// </summary> /// </summary>
public const ulong FileVersion64 = 0x0002002600000000UL; public const ulong FileVersion64 = 0x0002002700010000UL;
/// <summary> /// <summary>
/// Version, encoded as string. /// Version, encoded as string.
/// </summary> /// </summary>
public const string VersionString = "2.38"; public const string VersionString = "2.39.1";
public const string Copyright = @"Copyright © 2003-2018 Dominik Reichl"; public const string Copyright = @"Copyright © 2003-2018 Dominik Reichl";
@@ -219,6 +219,15 @@ namespace ModernKeePassLib
return (pe.Strings.ReadSafe(PwDefs.TitleField) == TanTitle); return (pe.Strings.ReadSafe(PwDefs.TitleField) == TanTitle);
} }
internal static string GetTranslationDisplayVersion(string strFileVersion)
{
if(strFileVersion == null) { Debug.Assert(false); return string.Empty; }
if(strFileVersion == "2.39") return "2.39 / 2.39.1";
return strFileVersion;
}
} }
// #pragma warning disable 1591 // Missing XML comments warning // #pragma warning disable 1591 // Missing XML comments warning

View File

@@ -782,45 +782,49 @@ namespace ModernKeePassLib
} }
/// <summary> /// <summary>
/// Approximate the total size of this entry in bytes (including /// Approximate the total size (in process memory) of this entry
/// strings, binaries and history entries). /// in bytes (including strings, binaries and history entries).
/// </summary> /// </summary>
/// <returns>Size in bytes.</returns> /// <returns>Size in bytes.</returns>
public ulong GetSize() public ulong GetSize()
{ {
ulong uSize = 128; // Approx fixed length data // This method assumes 64-bit pointers/references and Unicode
// strings (i.e. 2 bytes per character)
ulong cb = 248; // Number of bytes; approx. fixed length data
ulong cc = 0; // Number of characters
cb += (ulong)m_listStrings.UCount * 40;
foreach(KeyValuePair<string, ProtectedString> kvpStr in m_listStrings) foreach(KeyValuePair<string, ProtectedString> kvpStr in m_listStrings)
{ cc += (ulong)kvpStr.Key.Length + (ulong)kvpStr.Value.Length;
uSize += (ulong)kvpStr.Key.Length;
uSize += (ulong)kvpStr.Value.Length;
}
cb += (ulong)m_listBinaries.UCount * 65;
foreach(KeyValuePair<string, ProtectedBinary> kvpBin in m_listBinaries) foreach(KeyValuePair<string, ProtectedBinary> kvpBin in m_listBinaries)
{ {
uSize += (ulong)kvpBin.Key.Length; cc += (ulong)kvpBin.Key.Length;
uSize += kvpBin.Value.Length; cb += (ulong)kvpBin.Value.Length;
} }
uSize += (ulong)m_listAutoType.DefaultSequence.Length; cc += (ulong)m_listAutoType.DefaultSequence.Length;
cb += (ulong)m_listAutoType.AssociationsCount * 24;
foreach(AutoTypeAssociation a in m_listAutoType.Associations) foreach(AutoTypeAssociation a in m_listAutoType.Associations)
{ cc += (ulong)a.WindowName.Length + (ulong)a.Sequence.Length;
uSize += (ulong)a.WindowName.Length;
uSize += (ulong)a.Sequence.Length;
}
cb += (ulong)m_listHistory.UCount * 8;
foreach(PwEntry peHistory in m_listHistory) foreach(PwEntry peHistory in m_listHistory)
uSize += peHistory.GetSize(); cb += peHistory.GetSize();
uSize += (ulong)m_strOverrideUrl.Length; cc += (ulong)m_strOverrideUrl.Length;
cb += (ulong)m_vTags.Count * 8;
foreach(string strTag in m_vTags) foreach(string strTag in m_vTags)
uSize += (ulong)strTag.Length; cc += (ulong)strTag.Length;
cb += (ulong)m_dCustomData.Count * 16;
foreach(KeyValuePair<string, string> kvp in m_dCustomData) foreach(KeyValuePair<string, string> kvp in m_dCustomData)
uSize += (ulong)kvp.Key.Length + (ulong)kvp.Value.Length; cc += (ulong)kvp.Key.Length + (ulong)kvp.Value.Length;
return uSize; return (cb + (cc << 1));
} }
public bool HasTag(string strTag) public bool HasTag(string strTag)

View File

@@ -44,9 +44,8 @@ namespace ModernKeePassLib.Security
long lID); long lID);
/// <summary> /// <summary>
/// Represents a protected binary, i.e. a byte array that is encrypted /// A protected binary, i.e. a byte array that is encrypted in memory.
/// in memory. A <c>ProtectedBinary</c> object is immutable and /// A <c>ProtectedBinary</c> object is immutable and thread-safe.
/// thread-safe.
/// </summary> /// </summary>
public sealed class ProtectedBinary : IEquatable<ProtectedBinary> public sealed class ProtectedBinary : IEquatable<ProtectedBinary>
{ {
@@ -71,7 +70,7 @@ namespace ModernKeePassLib.Security
private enum PbMemProt private enum PbMemProt
{ {
None = 0, None = 0,
ProtectedMemory, ProtectedMemory, // DPAPI on Windows
ChaCha20, ChaCha20,
ExtCrypt ExtCrypt
} }
@@ -90,7 +89,8 @@ namespace ModernKeePassLib.Security
bool? ob = g_obProtectedMemorySupported; bool? ob = g_obProtectedMemorySupported;
if(ob.HasValue) return ob.Value; if(ob.HasValue) return ob.Value;
// Mono does not implement any encryption for ProtectedMemory; // Mono does not implement any encryption for ProtectedMemory
// on Linux (Mono uses DPAPI on Windows);
// https://sourceforge.net/p/keepass/feature-requests/1907/ // https://sourceforge.net/p/keepass/feature-requests/1907/
if(NativeLib.IsUnix()) if(NativeLib.IsUnix())
{ {
@@ -177,7 +177,7 @@ namespace ModernKeePassLib.Security
/// i.e. the caller is responsible for clearing it.</param> /// i.e. the caller is responsible for clearing it.</param>
public ProtectedBinary(bool bEnableProtection, byte[] pbData) public ProtectedBinary(bool bEnableProtection, byte[] pbData)
{ {
if(pbData == null) throw new ArgumentNullException("pbData"); if(pbData == null) throw new ArgumentNullException("pbData"); // For .Length
Init(bEnableProtection, pbData, 0, pbData.Length); Init(bEnableProtection, pbData, 0, pbData.Length);
} }
@@ -213,9 +213,8 @@ namespace ModernKeePassLib.Security
if(xbProtected == null) throw new ArgumentNullException("xbProtected"); if(xbProtected == null) throw new ArgumentNullException("xbProtected");
byte[] pb = xbProtected.ReadPlainText(); byte[] pb = xbProtected.ReadPlainText();
Init(bEnableProtection, pb, 0, pb.Length); try { Init(bEnableProtection, pb, 0, pb.Length); }
finally { if(bEnableProtection) MemUtil.ZeroByteArray(pb); }
if(bEnableProtection) MemUtil.ZeroByteArray(pb);
} }
private void Init(bool bEnableProtection, byte[] pbData, int iOffset, private void Init(bool bEnableProtection, byte[] pbData, int iOffset,
@@ -374,7 +373,7 @@ namespace ModernKeePassLib.Security
for(int i = 0; i < pb.Length; ++i) for(int i = 0; i < pb.Length; ++i)
h = (h << 3) + h + (int)pb[i]; h = (h << 3) + h + (int)pb[i];
} }
MemUtil.ZeroByteArray(pb); if(m_bProtected) MemUtil.ZeroByteArray(pb);
m_hash = h; m_hash = h;
return h; return h;
@@ -382,25 +381,36 @@ namespace ModernKeePassLib.Security
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return Equals(obj as ProtectedBinary); return this.Equals(obj as ProtectedBinary, true);
} }
public bool Equals(ProtectedBinary other) public bool Equals(ProtectedBinary other)
{ {
if(other == null) return false; // No assert return this.Equals(other, true);
}
public bool Equals(ProtectedBinary other, bool bCheckProtEqual)
{
if(other == null) return false; // No assert
if(object.ReferenceEquals(this, other)) return true; // Perf. opt.
if(bCheckProtEqual && (m_bProtected != other.m_bProtected))
return false;
if(m_bProtected != other.m_bProtected) return false;
if(m_uDataLen != other.m_uDataLen) return false; if(m_uDataLen != other.m_uDataLen) return false;
byte[] pbL = ReadData(); byte[] pbL = ReadData(), pbR = null;
byte[] pbR = other.ReadData(); bool bEq;
bool bEq = MemUtil.ArraysEqual(pbL, pbR); try
MemUtil.ZeroByteArray(pbL); {
MemUtil.ZeroByteArray(pbR); pbR = other.ReadData();
bEq = MemUtil.ArraysEqual(pbL, pbR);
#if DEBUG }
if(bEq) { Debug.Assert(GetHashCode() == other.GetHashCode()); } finally
#endif {
if(m_bProtected) MemUtil.ZeroByteArray(pbL);
if(other.m_bProtected && (pbR != null)) MemUtil.ZeroByteArray(pbR);
}
return bEq; return bEq;
} }

View File

@@ -33,11 +33,11 @@ using KeePassLibSD;
namespace ModernKeePassLib.Security namespace ModernKeePassLib.Security
{ {
/// <summary> /// <summary>
/// Represents an in-memory encrypted string. /// A string that is protected in process memory.
/// <c>ProtectedString</c> objects are immutable and thread-safe. /// <c>ProtectedString</c> objects are immutable and thread-safe.
/// </summary> /// </summary>
#if (DEBUG && !KeePassLibSD) #if (DEBUG && !KeePassLibSD)
[DebuggerDisplay(@"{ReadString()}")] [DebuggerDisplay("{ReadString()}")]
#endif #endif
public sealed class ProtectedString public sealed class ProtectedString
{ {
@@ -48,11 +48,24 @@ namespace ModernKeePassLib.Security
private bool m_bIsProtected; private bool m_bIsProtected;
private static readonly ProtectedString m_psEmpty = new ProtectedString(); private static readonly ProtectedString m_psEmpty = new ProtectedString();
/// <summary>
/// Get an empty <c>ProtectedString</c> object, without protection.
/// </summary>
public static ProtectedString Empty public static ProtectedString Empty
{ {
get { return m_psEmpty; } get { return m_psEmpty; }
} }
private static readonly ProtectedString m_psEmptyEx = new ProtectedString(
true, new byte[0]);
/// <summary>
/// Get an empty <c>ProtectedString</c> object, with protection turned on.
/// </summary>
public static ProtectedString EmptyEx
{
get { return m_psEmptyEx; }
}
/// <summary> /// <summary>
/// A flag specifying whether the <c>ProtectedString</c> object /// A flag specifying whether the <c>ProtectedString</c> object
/// has turned on memory protection or not. /// has turned on memory protection or not.
@@ -66,8 +79,8 @@ namespace ModernKeePassLib.Security
{ {
get get
{ {
ProtectedBinary pBin = m_pbUtf8; // Local ref for thread-safety ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
if(pBin != null) return (pBin.Length == 0); if(p != null) return (p.Length == 0);
Debug.Assert(m_strPlainText != null); Debug.Assert(m_strPlainText != null);
return (m_strPlainText.Length == 0); return (m_strPlainText.Length == 0);
@@ -75,18 +88,21 @@ namespace ModernKeePassLib.Security
} }
private int m_nCachedLength = -1; private int m_nCachedLength = -1;
/// <summary>
/// Length of the protected string, in characters.
/// </summary>
public int Length public int Length
{ {
get get
{ {
if(m_nCachedLength >= 0) return m_nCachedLength; if(m_nCachedLength >= 0) return m_nCachedLength;
ProtectedBinary pBin = m_pbUtf8; // Local ref for thread-safety ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
if(pBin != null) if(p != null)
{ {
byte[] pbPlain = pBin.ReadData(); byte[] pbPlain = p.ReadData();
m_nCachedLength = StrUtil.Utf8.GetCharCount(pbPlain); try { m_nCachedLength = StrUtil.Utf8.GetCharCount(pbPlain); }
MemUtil.ZeroByteArray(pbPlain); finally { MemUtil.ZeroByteArray(pbPlain); }
} }
else else
{ {
@@ -149,9 +165,8 @@ namespace ModernKeePassLib.Security
if(xbProtected == null) throw new ArgumentNullException("xbProtected"); if(xbProtected == null) throw new ArgumentNullException("xbProtected");
byte[] pb = xbProtected.ReadPlainText(); byte[] pb = xbProtected.ReadPlainText();
Init(bEnableProtection, pb); try { Init(bEnableProtection, pb); }
finally { if(bEnableProtection) MemUtil.ZeroByteArray(pb); }
if(bEnableProtection) MemUtil.ZeroByteArray(pb);
} }
private void Init(bool bEnableProtection, string str) private void Init(bool bEnableProtection, string str)
@@ -160,7 +175,7 @@ namespace ModernKeePassLib.Security
m_bIsProtected = bEnableProtection; m_bIsProtected = bEnableProtection;
// The string already is in memory and immutable, // As the string already is in memory and immutable,
// protection would be useless // protection would be useless
m_strPlainText = str; m_strPlainText = str;
} }
@@ -178,8 +193,8 @@ namespace ModernKeePassLib.Security
} }
/// <summary> /// <summary>
/// Convert the protected string to a normal string object. /// Convert the protected string to a standard string object.
/// Be careful with this function, the returned string object /// Be careful with this function, as the returned string object
/// isn't protected anymore and stored in plain-text in the /// isn't protected anymore and stored in plain-text in the
/// process memory. /// process memory.
/// </summary> /// </summary>
@@ -194,23 +209,42 @@ namespace ModernKeePassLib.Security
// No need to clear pb // No need to clear pb
// As the text is now visible in process memory anyway, // As the text is now visible in process memory anyway,
// there's no need to protect it anymore // there's no need to protect it anymore (strings are
// immutable and thus cannot be overwritten)
m_strPlainText = str; m_strPlainText = str;
m_pbUtf8 = null; // Thread-safe order m_pbUtf8 = null; // Thread-safe order
return str; return str;
} }
/// <summary>
/// Read out the string and return it as a char array.
/// The returned array is not protected and should be cleared by
/// the caller.
/// </summary>
/// <returns>Plain-text char array.</returns>
public char[] ReadChars()
{
if(m_strPlainText != null) return m_strPlainText.ToCharArray();
byte[] pb = ReadUtf8();
char[] v;
try { v = StrUtil.Utf8.GetChars(pb); }
finally { MemUtil.ZeroByteArray(pb); }
return v;
}
/// <summary> /// <summary>
/// Read out the string and return a byte array that contains the /// Read out the string and return a byte array that contains the
/// string encoded using UTF-8. The returned string is not protected /// string encoded using UTF-8.
/// anymore! /// The returned array is not protected and should be cleared by
/// the caller.
/// </summary> /// </summary>
/// <returns>Plain-text UTF-8 byte array.</returns> /// <returns>Plain-text UTF-8 byte array.</returns>
public byte[] ReadUtf8() public byte[] ReadUtf8()
{ {
ProtectedBinary pBin = m_pbUtf8; // Local ref for thread-safety ProtectedBinary p = m_pbUtf8; // Local ref for thread-safety
if(pBin != null) return pBin.ReadData(); if(p != null) return p.ReadData();
return StrUtil.Utf8.GetBytes(m_strPlainText); return StrUtil.Utf8.GetBytes(m_strPlainText);
} }
@@ -223,7 +257,7 @@ namespace ModernKeePassLib.Security
/// <returns>Protected string.</returns> /// <returns>Protected string.</returns>
public byte[] ReadXorredString(CryptoRandomStream crsRandomSource) public byte[] ReadXorredString(CryptoRandomStream crsRandomSource)
{ {
Debug.Assert(crsRandomSource != null); if(crsRandomSource == null) throw new ArgumentNullException("crsRandomSource"); if(crsRandomSource == null) { Debug.Assert(false); throw new ArgumentNullException("crsRandomSource"); }
byte[] pbData = ReadUtf8(); byte[] pbData = ReadUtf8();
uint uLen = (uint)pbData.Length; uint uLen = (uint)pbData.Length;
@@ -242,10 +276,34 @@ namespace ModernKeePassLib.Security
if(bProtect == m_bIsProtected) return this; if(bProtect == m_bIsProtected) return this;
byte[] pb = ReadUtf8(); byte[] pb = ReadUtf8();
ProtectedString ps = new ProtectedString(bProtect, pb);
if(bProtect) MemUtil.ZeroByteArray(pb); // No need to clear pb; either the current or the new object is unprotected
return ps; return new ProtectedString(bProtect, pb);
}
public bool Equals(ProtectedString ps, bool bCheckProtEqual)
{
if(ps == null) throw new ArgumentNullException("ps");
if(object.ReferenceEquals(this, ps)) return true; // Perf. opt.
bool bPA = m_bIsProtected, bPB = ps.m_bIsProtected;
if(bCheckProtEqual && (bPA != bPB)) return false;
if(!bPA && !bPB) return (ReadString() == ps.ReadString());
byte[] pbA = ReadUtf8(), pbB = null;
bool bEq;
try
{
pbB = ps.ReadUtf8();
bEq = MemUtil.ArraysEqual(pbA, pbB);
}
finally
{
if(bPA) MemUtil.ZeroByteArray(pbA);
if(bPB && (pbB != null)) MemUtil.ZeroByteArray(pbB);
}
return bEq;
} }
public ProtectedString Insert(int iStart, string strInsert) public ProtectedString Insert(int iStart, string strInsert)
@@ -254,18 +312,14 @@ namespace ModernKeePassLib.Security
if(strInsert == null) throw new ArgumentNullException("strInsert"); if(strInsert == null) throw new ArgumentNullException("strInsert");
if(strInsert.Length == 0) return this; if(strInsert.Length == 0) return this;
// Only operate directly with strings when m_bIsProtected is
// false, not in the case of non-null m_strPlainText, because
// the operation creates a new sequence in memory
if(!m_bIsProtected) if(!m_bIsProtected)
return new ProtectedString(false, ReadString().Insert( return new ProtectedString(false, ReadString().Insert(
iStart, strInsert)); iStart, strInsert));
UTF8Encoding utf8 = StrUtil.Utf8; UTF8Encoding utf8 = StrUtil.Utf8;
char[] v = ReadChars(), vNew = null;
byte[] pb = ReadUtf8(); byte[] pbNew = null;
char[] v = utf8.GetChars(pb); ProtectedString ps;
char[] vNew;
try try
{ {
@@ -279,21 +333,20 @@ namespace ModernKeePassLib.Security
Array.Copy(vIns, 0, vNew, iStart, vIns.Length); Array.Copy(vIns, 0, vNew, iStart, vIns.Length);
Array.Copy(v, iStart, vNew, iStart + vIns.Length, Array.Copy(v, iStart, vNew, iStart + vIns.Length,
v.Length - iStart); v.Length - iStart);
pbNew = utf8.GetBytes(vNew);
ps = new ProtectedString(true, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Insert(iStart, strInsert));
} }
finally finally
{ {
MemUtil.ZeroArray<char>(v); MemUtil.ZeroArray<char>(v);
MemUtil.ZeroByteArray(pb); if(vNew != null) MemUtil.ZeroArray<char>(vNew);
if(pbNew != null) MemUtil.ZeroByteArray(pbNew);
} }
byte[] pbNew = utf8.GetBytes(vNew);
ProtectedString ps = new ProtectedString(m_bIsProtected, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Insert(iStart, strInsert));
MemUtil.ZeroArray<char>(vNew);
MemUtil.ZeroByteArray(pbNew);
return ps; return ps;
} }
@@ -303,44 +356,81 @@ namespace ModernKeePassLib.Security
if(nCount < 0) throw new ArgumentOutOfRangeException("nCount"); if(nCount < 0) throw new ArgumentOutOfRangeException("nCount");
if(nCount == 0) return this; if(nCount == 0) return this;
// Only operate directly with strings when m_bIsProtected is
// false, not in the case of non-null m_strPlainText, because
// the operation creates a new sequence in memory
if(!m_bIsProtected) if(!m_bIsProtected)
return new ProtectedString(false, ReadString().Remove( return new ProtectedString(false, ReadString().Remove(
iStart, nCount)); iStart, nCount));
UTF8Encoding utf8 = StrUtil.Utf8; UTF8Encoding utf8 = StrUtil.Utf8;
char[] v = ReadChars(), vNew = null;
byte[] pb = ReadUtf8(); byte[] pbNew = null;
char[] v = utf8.GetChars(pb); ProtectedString ps;
char[] vNew;
try try
{ {
if((iStart + nCount) > v.Length) if((iStart + nCount) > v.Length)
throw new ArgumentException("iStart + nCount"); throw new ArgumentException("(iStart + nCount) > v.Length");
vNew = new char[v.Length - nCount]; vNew = new char[v.Length - nCount];
Array.Copy(v, 0, vNew, 0, iStart); Array.Copy(v, 0, vNew, 0, iStart);
Array.Copy(v, iStart + nCount, vNew, iStart, v.Length - Array.Copy(v, iStart + nCount, vNew, iStart, v.Length -
(iStart + nCount)); (iStart + nCount));
pbNew = utf8.GetBytes(vNew);
ps = new ProtectedString(true, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Remove(iStart, nCount));
} }
finally finally
{ {
MemUtil.ZeroArray<char>(v); MemUtil.ZeroArray<char>(v);
MemUtil.ZeroByteArray(pb); if(vNew != null) MemUtil.ZeroArray<char>(vNew);
if(pbNew != null) MemUtil.ZeroByteArray(pbNew);
} }
byte[] pbNew = utf8.GetBytes(vNew);
ProtectedString ps = new ProtectedString(m_bIsProtected, pbNew);
Debug.Assert(utf8.GetString(pbNew, 0, pbNew.Length) ==
ReadString().Remove(iStart, nCount));
MemUtil.ZeroArray<char>(vNew);
MemUtil.ZeroByteArray(pbNew);
return ps; return ps;
} }
public static ProtectedString operator +(ProtectedString a, ProtectedString b)
{
if(a == null) throw new ArgumentNullException("a");
if(b == null) throw new ArgumentNullException("b");
if(b.IsEmpty) return a;
if(a.IsEmpty) return b;
if(!a.IsProtected && !b.IsProtected)
return new ProtectedString(false, a.ReadString() + b.ReadString());
char[] vA = a.ReadChars(), vB = null, vNew = null;
byte[] pbNew = null;
ProtectedString ps;
try
{
vB = b.ReadChars();
vNew = new char[vA.Length + vB.Length];
Array.Copy(vA, vNew, vA.Length);
Array.Copy(vB, 0, vNew, vA.Length, vB.Length);
pbNew = StrUtil.Utf8.GetBytes(vNew);
ps = new ProtectedString(true, pbNew);
}
finally
{
MemUtil.ZeroArray<char>(vA);
if(vB != null) MemUtil.ZeroArray<char>(vB);
if(vNew != null) MemUtil.ZeroArray<char>(vNew);
if(pbNew != null) MemUtil.ZeroByteArray(pbNew);
}
return ps;
}
public static ProtectedString operator +(ProtectedString a, string b)
{
ProtectedString psB = new ProtectedString(false, b);
return (a + psB);
}
} }
} }

View File

@@ -129,9 +129,12 @@ namespace ModernKeePassLib.Serialization
{ {
s = IOConnection.OpenRead(iocLockFile); s = IOConnection.OpenRead(iocLockFile);
if(s == null) return null; if(s == null) return null;
StreamReader sr = new StreamReader(s, StrUtil.Utf8);
string str = sr.ReadToEnd(); string str = null;
sr.Dispose(); using(StreamReader sr = new StreamReader(s, StrUtil.Utf8))
{
str = sr.ReadToEnd();
}
if(str == null) { Debug.Assert(false); return null; } if(str == null) { Debug.Assert(false); return null; }
str = StrUtil.NormalizeNewLines(str, false); str = StrUtil.NormalizeNewLines(str, false);

View File

@@ -19,6 +19,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Text; using System.Text;
@@ -32,19 +33,25 @@ using ModernKeePassLib.Utility;
using System.Threading.Tasks; using System.Threading.Tasks;
using Windows.Storage; using Windows.Storage;
using Windows.Storage.Streams; using Windows.Storage.Streams;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Resources; using ModernKeePassLib.Resources;
namespace ModernKeePassLib.Serialization namespace ModernKeePassLib.Serialization
{ {
public sealed class FileTransactionEx public sealed class FileTransactionEx : IDisposable
{ {
private bool m_bTransacted; private bool m_bTransacted;
private IOConnectionInfo m_iocBase; private IOConnectionInfo m_iocBase; // Null means disposed
private IOConnectionInfo m_iocTemp; private IOConnectionInfo m_iocTemp;
private IOConnectionInfo m_iocTxfMidFallback = null; // Null <=> TxF not used
private bool m_bMadeUnhidden = false; private bool m_bMadeUnhidden = false;
private List<IOConnectionInfo> m_lToDelete = new List<IOConnectionInfo>();
private const string StrTempSuffix = ".tmp"; private const string StrTempSuffix = ".tmp";
private const string StrTxfTempPrefix = PwDefs.ShortProductName + "_TxF_";
private const string StrTxfTempSuffix = ".tmp";
private static Dictionary<string, bool> g_dEnabled = private static Dictionary<string, bool> g_dEnabled =
new Dictionary<string, bool>(StrUtil.CaseIgnoreComparer); new Dictionary<string, bool>(StrUtil.CaseIgnoreComparer);
@@ -56,26 +63,23 @@ namespace ModernKeePassLib.Serialization
set { g_bExtraSafe = value; } set { g_bExtraSafe = value; }
} }
public FileTransactionEx(IOConnectionInfo iocBaseFile) public FileTransactionEx(IOConnectionInfo iocBaseFile) :
this(iocBaseFile, true)
{ {
Initialize(iocBaseFile, true);
} }
public FileTransactionEx(IOConnectionInfo iocBaseFile, bool bTransacted) public FileTransactionEx(IOConnectionInfo iocBaseFile, bool bTransacted)
{
Initialize(iocBaseFile, bTransacted);
}
private void Initialize(IOConnectionInfo iocBaseFile, bool bTransacted)
{ {
if(iocBaseFile == null) throw new ArgumentNullException("iocBaseFile"); if(iocBaseFile == null) throw new ArgumentNullException("iocBaseFile");
m_bTransacted = bTransacted; m_bTransacted = bTransacted;
m_iocBase = iocBaseFile.CloneDeep(); m_iocBase = iocBaseFile.CloneDeep();
if(m_iocBase.IsLocalFile())
m_iocBase.Path = UrlUtil.GetShortestAbsolutePath(m_iocBase.Path);
string strPath = m_iocBase.Path; string strPath = m_iocBase.Path;
#if !ModernKeePassLib
if(m_iocBase.IsLocalFile()) if(m_iocBase.IsLocalFile())
{ {
try try
@@ -93,10 +97,23 @@ namespace ModernKeePassLib.Serialization
if((long)(fa & FileAttributes.ReparsePoint) != 0) if((long)(fa & FileAttributes.ReparsePoint) != 0)
m_bTransacted = false; m_bTransacted = false;
} }
else
{
// If the base and the temporary file are in different
// folders and the base file doesn't exist (i.e. we can't
// backup the ACL), a transaction would cause the new file
// to have the default ACL of the temporary folder instead
// of the one of the base folder; therefore, we don't use
// a transaction when the base file doesn't exist (this
// also results in other applications monitoring the folder
// to see one file creation only)
m_bTransacted = false;
}
} }
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
} }
#if !ModernKeePassLib
// Prevent transactions for FTP URLs under .NET 4.0 in order to // Prevent transactions for FTP URLs under .NET 4.0 in order to
// avoid/workaround .NET bug 621450: // avoid/workaround .NET bug 621450:
// https://connect.microsoft.com/VisualStudio/feedback/details/621450/problem-renaming-file-on-ftp-server-using-ftpwebrequest-in-net-framework-4-0-vs2010-only // https://connect.microsoft.com/VisualStudio/feedback/details/621450/problem-renaming-file-on-ftp-server-using-ftpwebrequest-in-net-framework-4-0-vs2010-only
@@ -118,90 +135,152 @@ namespace ModernKeePassLib.Serialization
{ {
m_iocTemp = m_iocBase.CloneDeep(); m_iocTemp = m_iocBase.CloneDeep();
m_iocTemp.Path += StrTempSuffix; m_iocTemp.Path += StrTempSuffix;
TxfPrepare(); // Adjusts m_iocTemp
} }
else m_iocTemp = m_iocBase; else m_iocTemp = m_iocBase;
} }
~FileTransactionEx()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool bDisposing)
{
m_iocBase = null;
if(!bDisposing) return;
try
{
foreach(IOConnectionInfo ioc in m_lToDelete)
{
if(IOConnection.FileExists(ioc, false))
IOConnection.DeleteFile(ioc);
}
m_lToDelete.Clear();
}
catch(Exception) { Debug.Assert(false); }
}
public Stream OpenWrite() public Stream OpenWrite()
{ {
if(!m_bTransacted) m_bMadeUnhidden = UrlUtil.UnhideFile(m_iocTemp.Path); if(m_iocBase == null) { Debug.Assert(false); throw new ObjectDisposedException(null); }
else // m_bTransacted
{ if(!m_bTransacted) m_bMadeUnhidden |= UrlUtil.UnhideFile(m_iocTemp.Path);
try { IOConnection.DeleteFile(m_iocTemp); }
catch(Exception) { }
}
return IOConnection.OpenWrite(m_iocTemp); return IOConnection.OpenWrite(m_iocTemp);
} }
public void CommitWrite() public void CommitWrite()
{ {
if(m_bTransacted) CommitWriteTransaction(); if(m_iocBase == null) { Debug.Assert(false); throw new ObjectDisposedException(null); }
else // !m_bTransacted
if(!m_bTransacted)
{ {
if(m_bMadeUnhidden) UrlUtil.HideFile(m_iocTemp.Path, true); // Hide again if(m_bMadeUnhidden) UrlUtil.HideFile(m_iocTemp.Path, true);
} }
else CommitWriteTransaction();
m_iocBase = null; // Dispose
} }
private void CommitWriteTransaction() private void CommitWriteTransaction()
{ {
bool bMadeUnhidden = UrlUtil.UnhideFile(m_iocBase.Path);
#if (!ModernKeePassLib && !KeePassLibSD)
FileSecurity bkSecurity = null;
bool bEfsEncrypted = false;
#endif
if(g_bExtraSafe) if(g_bExtraSafe)
{ {
if(!IOConnection.FileExists(m_iocTemp)) if(!IOConnection.FileExists(m_iocTemp))
throw new FileNotFoundException(m_iocTemp.Path + throw new FileNotFoundException(m_iocTemp.Path +
Environment.NewLine + KLRes.FileSaveFailed); MessageService.NewLine + KLRes.FileSaveFailed);
} }
if(IOConnection.FileExists(m_iocBase)) bool bMadeUnhidden = UrlUtil.UnhideFile(m_iocBase.Path);
{
#if (!ModernKeePassLib && !KeePassLibSD) #if !ModernKeePassLib
if(m_iocBase.IsLocalFile()) // 'All' includes 'Audit' (SACL), which requires SeSecurityPrivilege,
// which we usually don't have and therefore get an exception;
// trying to set 'Owner' or 'Group' can result in an
// UnauthorizedAccessException; thus we restore 'Access' (DACL) only
const AccessControlSections acs = AccessControlSections.Access;
bool bEfsEncrypted = false;
byte[] pbSec = null;
#endif
DateTime? otCreation = null;
bool bBaseExists = IOConnection.FileExists(m_iocBase);
if(bBaseExists && m_iocBase.IsLocalFile())
{ {
// FileAttributes faBase = FileAttributes.Normal;
try try
{ {
#if !ModernKeePassLib
FileAttributes faBase = File.GetAttributes(m_iocBase.Path); FileAttributes faBase = File.GetAttributes(m_iocBase.Path);
bEfsEncrypted = ((long)(faBase & FileAttributes.Encrypted) != 0); bEfsEncrypted = ((long)(faBase & FileAttributes.Encrypted) != 0);
try { if(bEfsEncrypted) File.Decrypt(m_iocBase.Path); } // For TxF
DateTime tCreation = File.GetCreationTimeUtc(m_iocBase.Path);
bkSecurity = File.GetAccessControl(m_iocBase.Path);
File.SetCreationTimeUtc(m_iocTemp.Path, tCreation);
}
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
}
#endif #endif
otCreation = File.GetCreationTimeUtc(m_iocBase.Path);
#if !ModernKeePassLib
// May throw with Mono
FileSecurity sec = File.GetAccessControl(m_iocBase.Path, acs);
if(sec != null) pbSec = sec.GetSecurityDescriptorBinaryForm();
#endif
}
catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
IOConnection.DeleteFile(m_iocBase); // if((long)(faBase & FileAttributes.ReadOnly) != 0)
// throw new UnauthorizedAccessException();
} }
IOConnection.RenameFile(m_iocTemp, m_iocBase); if(!TxfMove())
#if (!ModernKeePassLib && !KeePassLibSD)
if(m_iocBase.IsLocalFile())
{ {
if(bBaseExists) IOConnection.DeleteFile(m_iocBase);
IOConnection.RenameFile(m_iocTemp, m_iocBase);
}
else { Debug.Assert(pbSec != null); } // TxF success => NTFS => has ACL
try try
{ {
// If File.GetCreationTimeUtc fails, it may return a
// date with year 1601, and Unix times start in 1970,
// so testing for 1971 should ensure validity;
// https://msdn.microsoft.com/en-us/library/system.io.file.getcreationtimeutc.aspx
if(otCreation.HasValue && (otCreation.Value.Year >= 1971))
File.SetCreationTimeUtc(m_iocBase.Path, otCreation.Value);
#if !ModernKeePassLib
if(bEfsEncrypted) if(bEfsEncrypted)
{ {
try { File.Encrypt(m_iocBase.Path); } try { File.Encrypt(m_iocBase.Path); }
catch(Exception) { Debug.Assert(false); } catch(Exception) { Debug.Assert(false); }
} }
if(bkSecurity != null) // File.SetAccessControl(m_iocBase.Path, secPrev);
File.SetAccessControl(m_iocBase.Path, bkSecurity); // Directly calling File.SetAccessControl with the previous
} // FileSecurity object does not work; the binary form
catch(Exception) { Debug.Assert(false); } // indirection is required;
// https://sourceforge.net/p/keepass/bugs/1738/
// https://msdn.microsoft.com/en-us/library/system.io.file.setaccesscontrol.aspx
if((pbSec != null) && (pbSec.Length != 0))
{
FileSecurity sec = new FileSecurity();
sec.SetSecurityDescriptorBinaryForm(pbSec, acs);
File.SetAccessControl(m_iocBase.Path, sec);
} }
#endif #endif
}
catch(Exception) { Debug.Assert(false); }
if(bMadeUnhidden) UrlUtil.HideFile(m_iocBase.Path, true); // Hide again if(bMadeUnhidden) UrlUtil.HideFile(m_iocBase.Path, true);
} }
// For plugins // For plugins
@@ -213,5 +292,155 @@ namespace ModernKeePassLib.Serialization
g_dEnabled[strPrefix] = obTransacted.Value; g_dEnabled[strPrefix] = obTransacted.Value;
else g_dEnabled.Remove(strPrefix); else g_dEnabled.Remove(strPrefix);
} }
private static bool TxfIsSupported(char chDriveLetter)
{
if(chDriveLetter == '\0') return false;
#if !ModernKeePassLib
try
{
string strRoot = (new string(chDriveLetter, 1)) + ":\\";
const int cch = NativeMethods.MAX_PATH + 1;
StringBuilder sbName = new StringBuilder(cch + 1);
uint uSerial = 0, cchMaxComp = 0, uFlags = 0;
StringBuilder sbFileSystem = new StringBuilder(cch + 1);
if(!NativeMethods.GetVolumeInformation(strRoot, sbName, (uint)cch,
ref uSerial, ref cchMaxComp, ref uFlags, sbFileSystem, (uint)cch))
{
Debug.Assert(false, (new Win32Exception()).Message);
return false;
}
return ((uFlags & NativeMethods.FILE_SUPPORTS_TRANSACTIONS) != 0);
}
catch(Exception) { Debug.Assert(false); }
#endif
return false;
}
private void TxfPrepare()
{
try
{
if(NativeLib.IsUnix()) return;
if(!m_iocBase.IsLocalFile()) return;
string strID = StrUtil.AlphaNumericOnly(Convert.ToBase64String(
CryptoRandom.Instance.GetRandomBytes(16)));
string strTempDir = UrlUtil.GetTempPath();
// See also ClearOld method
string strTemp = UrlUtil.EnsureTerminatingSeparator(strTempDir,
false) + StrTxfTempPrefix + strID + StrTxfTempSuffix;
char chB = UrlUtil.GetDriveLetter(m_iocBase.Path);
char chT = UrlUtil.GetDriveLetter(strTemp);
if(!TxfIsSupported(chB)) return;
if((chT != chB) && !TxfIsSupported(chT)) return;
m_iocTxfMidFallback = m_iocTemp;
m_iocTemp = IOConnectionInfo.FromPath(strTemp);
m_lToDelete.Add(m_iocTemp);
}
catch(Exception) { Debug.Assert(false); m_iocTxfMidFallback = null; }
}
#if !ModernKeePassLib
private bool TxfMove()
{
if(m_iocTxfMidFallback == null) return false;
if(TxfMoveWithTx()) return true;
// Move the temporary file onto the base file's drive first,
// such that it cannot happen that both the base file and
// the temporary file are deleted/corrupted
const uint f = (NativeMethods.MOVEFILE_COPY_ALLOWED |
NativeMethods.MOVEFILE_REPLACE_EXISTING);
bool b = NativeMethods.MoveFileEx(m_iocTemp.Path, m_iocTxfMidFallback.Path, f);
if(b) b = NativeMethods.MoveFileEx(m_iocTxfMidFallback.Path, m_iocBase.Path, f);
if(!b) throw new Win32Exception();
Debug.Assert(!File.Exists(m_iocTemp.Path));
Debug.Assert(!File.Exists(m_iocTxfMidFallback.Path));
return true;
}
private bool TxfMoveWithTx()
{
IntPtr hTx = new IntPtr((int)NativeMethods.INVALID_HANDLE_VALUE);
Debug.Assert(hTx.ToInt64() == NativeMethods.INVALID_HANDLE_VALUE);
try
{
string strTx = PwDefs.ShortProductName + " TxF - " +
StrUtil.AlphaNumericOnly(Convert.ToBase64String(
CryptoRandom.Instance.GetRandomBytes(16)));
const int mchTx = NativeMethods.MAX_TRANSACTION_DESCRIPTION_LENGTH;
if(strTx.Length >= mchTx) strTx = strTx.Substring(0, mchTx - 1);
hTx = NativeMethods.CreateTransaction(IntPtr.Zero,
IntPtr.Zero, 0, 0, 0, 0, strTx);
if(hTx.ToInt64() == NativeMethods.INVALID_HANDLE_VALUE)
{
Debug.Assert(false, (new Win32Exception()).Message);
return false;
}
if(!NativeMethods.MoveFileTransacted(m_iocTemp.Path, m_iocBase.Path,
IntPtr.Zero, IntPtr.Zero, (NativeMethods.MOVEFILE_COPY_ALLOWED |
NativeMethods.MOVEFILE_REPLACE_EXISTING), hTx))
{
Debug.Assert(false, (new Win32Exception()).Message);
return false;
}
if(!NativeMethods.CommitTransaction(hTx))
{
Debug.Assert(false, (new Win32Exception()).Message);
return false;
}
Debug.Assert(!File.Exists(m_iocTemp.Path));
return true;
}
catch(Exception) { Debug.Assert(false); }
finally
{
if(hTx.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE)
{
try { if(!NativeMethods.CloseHandle(hTx)) { Debug.Assert(false); } }
catch(Exception) { Debug.Assert(false); }
}
}
return false;
}
internal static void ClearOld()
{
try
{
// See also TxfPrepare method
DirectoryInfo di = new DirectoryInfo(UrlUtil.GetTempPath());
List<FileInfo> l = UrlUtil.GetFileInfos(di, StrTxfTempPrefix +
"*" + StrTxfTempSuffix, SearchOption.TopDirectoryOnly);
foreach(FileInfo fi in l)
{
if(fi == null) { Debug.Assert(false); continue; }
if(!fi.Name.StartsWith(StrTxfTempPrefix, StrUtil.CaseIgnoreCmp) ||
!fi.Name.EndsWith(StrTxfTempSuffix, StrUtil.CaseIgnoreCmp))
continue;
if((DateTime.UtcNow - fi.LastWriteTimeUtc).TotalDays > 1.0)
fi.Delete();
}
}
catch(Exception) { Debug.Assert(false); }
}
#endif
} }
} }

View File

@@ -431,7 +431,7 @@ namespace ModernKeePassLib.Serialization
string strInfo = m_strProxyAddr; string strInfo = m_strProxyAddr;
if(m_strProxyPort.Length > 0) if(m_strProxyPort.Length > 0)
strInfo += ":" + m_strProxyPort; strInfo += ":" + m_strProxyPort;
MessageService.ShowWarning(strInfo, ex.Message); MessageService.ShowWarning(strInfo, ex);
} }
#endif #endif

View File

@@ -530,7 +530,7 @@ namespace ModernKeePassLib.Serialization
/* KdbxFile f = new KdbxFile(pwDatabase); /* KdbxFile f = new KdbxFile(pwDatabase);
f.m_format = KdbxFormat.PlainXml; f.m_format = KdbxFormat.PlainXml;
XmlDocument doc = new XmlDocument(); XmlDocument doc = XmlUtilEx.CreateXmlDocument();
doc.Load(msData); doc.Load(msData);
XmlElement el = doc.DocumentElement; XmlElement el = doc.DocumentElement;

View File

@@ -87,6 +87,7 @@ namespace ModernKeePassLib.Serialization
m_format = fmt; m_format = fmt;
m_slLogger = slLogger; m_slLogger = slLogger;
m_xmlWriter = null;
PwGroup pgRoot = (pgDataSource ?? m_pwDatabase.RootGroup); PwGroup pgRoot = (pgDataSource ?? m_pwDatabase.RootGroup);
UTF8Encoding encNoBom = StrUtil.Utf8; UTF8Encoding encNoBom = StrUtil.Utf8;
@@ -203,44 +204,25 @@ namespace ModernKeePassLib.Serialization
throw new ArgumentOutOfRangeException("fmt"); throw new ArgumentOutOfRangeException("fmt");
} }
#if ModernKeePassLib || KeePassUAP m_xmlWriter = XmlUtilEx.CreateXmlWriter(sXml, m_uFileVersion >= FileVersion32_4);
XmlWriterSettings xws = new XmlWriterSettings();
xws.Encoding = encNoBom;
xws.Indent = true;
xws.IndentChars = "\t";
xws.NewLineOnAttributes = false;
#if ModernKeePassLib
// This is needed for Argon2Kdf write
xws.Async = true;
if (m_uFileVersion >= FileVersion32_4) xws.CloseOutput = true;
#endif
XmlWriter xw = XmlWriter.Create(sXml, xws);
#else
XmlTextWriter xw = new XmlTextWriter(sXml, encNoBom);
xw.Formatting = Formatting.Indented;
xw.IndentChar = '\t';
xw.Indentation = 1;
#endif
m_xmlWriter = xw;
WriteDocument(pgRoot); WriteDocument(pgRoot);
m_xmlWriter.Flush(); m_xmlWriter.Flush();
m_xmlWriter.Dispose();
} }
finally finally
{ {
CommonCleanUpWrite(lStreams, sHashing);
if(pbCipherKey != null) MemUtil.ZeroByteArray(pbCipherKey); if(pbCipherKey != null) MemUtil.ZeroByteArray(pbCipherKey);
if(pbHmacKey64 != null) MemUtil.ZeroByteArray(pbHmacKey64); if(pbHmacKey64 != null) MemUtil.ZeroByteArray(pbHmacKey64);
CommonCleanUpWrite(lStreams, sHashing);
} }
} }
private void CommonCleanUpWrite(List<Stream> lStreams, HashingStreamEx sHashing) private void CommonCleanUpWrite(List<Stream> lStreams, HashingStreamEx sHashing)
{ {
if(m_xmlWriter != null) { m_xmlWriter.Dispose(); m_xmlWriter = null; }
CloseStreams(lStreams); CloseStreams(lStreams);
Debug.Assert(lStreams.Contains(sHashing)); // sHashing must be closed Debug.Assert(lStreams.Contains(sHashing)); // sHashing must be closed
@@ -249,7 +231,6 @@ namespace ModernKeePassLib.Serialization
CleanUpInnerRandomStream(); CleanUpInnerRandomStream();
m_xmlWriter = null;
m_pbHashOfHeader = null; m_pbHashOfHeader = null;
} }

View File

@@ -403,8 +403,8 @@ namespace ModernKeePassLib.Serialization
ICipherEngine iCipher = CipherPool.GlobalPool.GetCipher(pu); ICipherEngine iCipher = CipherPool.GlobalPool.GetCipher(pu);
if(iCipher == null) // CryptographicExceptions are translated to "file corrupted" if(iCipher == null) // CryptographicExceptions are translated to "file corrupted"
throw new Exception(KLRes.FileUnknownCipher + throw new Exception(KLRes.FileUnknownCipher +
Environment.NewLine + KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph + KLRes.FileNewVerOrPlgReq +
Environment.NewLine + "UUID: " + pu.ToHexString() + "."); MessageService.NewParagraph + "UUID: " + pu.ToHexString() + ".");
ICipherEngine2 iCipher2 = (iCipher as ICipherEngine2); ICipherEngine2 iCipher2 = (iCipher as ICipherEngine2);
if(iCipher2 != null) if(iCipher2 != null)
@@ -539,8 +539,8 @@ namespace ModernKeePassLib.Serialization
FileStream fs = new FileStream(strPath, FileMode.Create, FileStream fs = new FileStream(strPath, FileMode.Create,
FileAccess.Write, FileShare.None); FileAccess.Write, FileShare.None);
byte[] pbData = pb.ReadData(); byte[] pbData = pb.ReadData();
fs.Write(pbData, 0, pbData.Length); try { File.WriteAllBytes(strPath, pbData); }
fs.Close(); finally { if(pb.IsProtected) MemUtil.ZeroByteArray(pbData); }
#endif #endif
} }
} }

View File

@@ -111,23 +111,17 @@ namespace ModernKeePassLib.Translation
if(xs == null) throw new ArgumentNullException("xs"); if(xs == null) throw new ArgumentNullException("xs");
#if !KeePassLibSD #if !KeePassLibSD
GZipStream gz = new GZipStream(sOut, CompressionMode.Compress); using(GZipStream gz = new GZipStream(sOut, CompressionMode.Compress))
#else #else
GZipOutputStream gz = new GZipOutputStream(sOut); using(GZipOutputStream gz = new GZipOutputStream(sOut))
#endif #endif
{
XmlWriterSettings xws = new XmlWriterSettings(); using(XmlWriter xw = XmlUtilEx.CreateXmlWriter(gz))
xws.CheckCharacters = true; {
xws.Encoding = StrUtil.Utf8;
xws.Indent = true;
xws.IndentChars = "\t";
XmlWriter xw = XmlWriter.Create(gz, xws);
xs.Serialize(xw, kpTrl); xs.Serialize(xw, kpTrl);
}
}
xw.Close();
gz.Close();
sOut.Close(); sOut.Close();
} }
@@ -148,15 +142,17 @@ namespace ModernKeePassLib.Translation
{ {
if(xs == null) throw new ArgumentNullException("xs"); if(xs == null) throw new ArgumentNullException("xs");
KPTranslation kpTrl = null;
#if !KeePassLibSD #if !KeePassLibSD
GZipStream gz = new GZipStream(s, CompressionMode.Decompress); using(GZipStream gz = new GZipStream(s, CompressionMode.Decompress))
#else #else
GZipInputStream gz = new GZipInputStream(s); using(GZipInputStream gz = new GZipInputStream(s))
#endif #endif
{
kpTrl = (xs.Deserialize(gz) as KPTranslation);
}
KPTranslation kpTrl = (xs.Deserialize(gz) as KPTranslation);
gz.Close();
s.Close(); s.Close();
return kpTrl; return kpTrl;
} }

View File

@@ -496,11 +496,21 @@ namespace ModernKeePassLib.Utility
return UInt32ToBytes((uint)iValue); return UInt32ToBytes((uint)iValue);
} }
public static void Int32ToBytesEx(int iValue, byte[] pb, int iOffset)
{
UInt32ToBytesEx((uint)iValue, pb, iOffset);
}
public static byte[] Int64ToBytes(long lValue) public static byte[] Int64ToBytes(long lValue)
{ {
return UInt64ToBytes((ulong)lValue); return UInt64ToBytes((ulong)lValue);
} }
public static void Int64ToBytesEx(long lValue, byte[] pb, int iOffset)
{
UInt64ToBytesEx((ulong)lValue, pb, iOffset);
}
public static uint RotateLeft32(uint u, int nBits) public static uint RotateLeft32(uint u, int nBits)
{ {
return ((u << nBits) | (u >> (32 - nBits))); return ((u << nBits) | (u >> (32 - nBits)));

View File

@@ -23,7 +23,7 @@ using System.Collections.Specialized;
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
#if !KeePassUAP #if !ModernKeePassLib
using System.Windows.Forms; using System.Windows.Forms;
#endif #endif
@@ -34,6 +34,7 @@ namespace ModernKeePassLib.Utility
{ {
public sealed class MessageServiceEventArgs : EventArgs public sealed class MessageServiceEventArgs : EventArgs
{ {
#if !ModernKeePassLib
private string m_strTitle = string.Empty; private string m_strTitle = string.Empty;
private string m_strText = string.Empty; private string m_strText = string.Empty;
private MessageBoxButtons m_msgButtons = MessageBoxButtons.OK; private MessageBoxButtons m_msgButtons = MessageBoxButtons.OK;
@@ -54,12 +55,14 @@ namespace ModernKeePassLib.Utility
m_msgButtons = msgButtons; m_msgButtons = msgButtons;
m_msgIcon = msgIcon; m_msgIcon = msgIcon;
} }
#endif
} }
public static class MessageService public static class MessageService
{ {
private static volatile uint m_uCurrentMessageCount = 0; private static volatile uint m_uCurrentMessageCount = 0;
#if !ModernKeePassLib
#if !KeePassLibSD #if !KeePassLibSD
private const MessageBoxIcon m_mbiInfo = MessageBoxIcon.Information; private const MessageBoxIcon m_mbiInfo = MessageBoxIcon.Information;
private const MessageBoxIcon m_mbiWarning = MessageBoxIcon.Warning; private const MessageBoxIcon m_mbiWarning = MessageBoxIcon.Warning;
@@ -73,7 +76,7 @@ namespace ModernKeePassLib.Utility
private const MessageBoxIcon m_mbiFatal = MessageBoxIcon.Hand; private const MessageBoxIcon m_mbiFatal = MessageBoxIcon.Hand;
#endif #endif
private const MessageBoxIcon m_mbiQuestion = MessageBoxIcon.Question; private const MessageBoxIcon m_mbiQuestion = MessageBoxIcon.Question;
#endif
public static string NewLine public static string NewLine
{ {
#if !KeePassLibSD #if !KeePassLibSD
@@ -123,7 +126,7 @@ namespace ModernKeePassLib.Utility
Exception exObj = (obj as Exception); Exception exObj = (obj as Exception);
string strObj = (obj as string); string strObj = (obj as string);
#if (!KeePassLibSD && !KeePassRT) #if (!KeePassLibSD && !ModernKeePassLib)
StringCollection scObj = (obj as StringCollection); StringCollection scObj = (obj as StringCollection);
#endif #endif
@@ -131,10 +134,10 @@ namespace ModernKeePassLib.Utility
{ {
if(bFullExceptions) if(bFullExceptions)
strAppend = StrUtil.FormatException(exObj); strAppend = StrUtil.FormatException(exObj);
else if((exObj.Message != null) && (exObj.Message.Length > 0)) else if(!string.IsNullOrEmpty(exObj.Message))
strAppend = exObj.Message; strAppend = exObj.Message;
} }
#if (!KeePassLibSD && !KeePassRT) #if (!KeePassLibSD && !ModernKeePassLib)
else if(scObj != null) else if(scObj != null)
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@@ -163,7 +166,7 @@ namespace ModernKeePassLib.Utility
return sbText.ToString(); return sbText.ToString();
} }
#if (!KeePassLibSD && !KeePassRT) #if (!KeePassLibSD && !ModernKeePassLib)
internal static Form GetTopForm() internal static Form GetTopForm()
{ {
FormCollection fc = Application.OpenForms; FormCollection fc = Application.OpenForms;
@@ -173,7 +176,7 @@ namespace ModernKeePassLib.Utility
} }
#endif #endif
#if !KeePassUAP #if !ModernKeePassLib
internal static DialogResult SafeShowMessageBox(string strText, string strTitle, internal static DialogResult SafeShowMessageBox(string strText, string strTitle,
MessageBoxButtons mb, MessageBoxIcon mi, MessageBoxDefaultButton mdb) MessageBoxButtons mb, MessageBoxIcon mi, MessageBoxDefaultButton mdb)
{ {

View File

@@ -104,6 +104,9 @@ namespace ModernKeePassLib.Utility
// 1690: // 1690:
// Removing items from a list view doesn't work properly. // Removing items from a list view doesn't work properly.
// https://sourceforge.net/p/keepass/bugs/1690/ // https://sourceforge.net/p/keepass/bugs/1690/
// 1716:
// 'Always on Top' doesn't work properly on the Cinnamon desktop.
// https://sourceforge.net/p/keepass/bugs/1716/
// 2139: // 2139:
// Shortcut keys are ignored. // Shortcut keys are ignored.
// https://sourceforge.net/p/keepass/feature-requests/2139/ // https://sourceforge.net/p/keepass/feature-requests/2139/
@@ -560,7 +563,7 @@ namespace ModernKeePassLib.Utility
{ {
// Mono's WriteRecentlyUsedFiles method also loads the // Mono's WriteRecentlyUsedFiles method also loads the
// XML file using XmlDocument // XML file using XmlDocument
XmlDocument xd = new XmlDocument(); XmlDocument xd = XmlUtilEx.CreateXmlDocument();
xd.Load(strFile); xd.Load(strFile);
} }
catch(Exception) // The XML file is invalid catch(Exception) // The XML file is invalid

View File

@@ -301,6 +301,28 @@ namespace ModernKeePassLib.Utility
return ("\\u" + sh.ToString(NumberFormatInfo.InvariantInfo) + "?"); return ("\\u" + sh.ToString(NumberFormatInfo.InvariantInfo) + "?");
} }
public static string RtfFix(string strRtf)
{
if(strRtf == null) { Debug.Assert(false); return string.Empty; }
string str = strRtf;
// Workaround for .NET bug: the Rtf property of a RichTextBox
// can return an RTF text starting with "{\\urtf", but
// setting such an RTF text throws an exception (the setter
// checks for the RTF text to start with "{\\rtf");
// https://sourceforge.net/p/keepass/discussion/329221/thread/7788872f/
// https://www.microsoft.com/en-us/download/details.aspx?id=10725
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb774284.aspx
// https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/RichTextBox.cs
const string p = "{\\urtf"; // Typically "{\\urtf1\\ansi\\ansicpg65001"
if(str.StartsWith(p) && (str.Length > p.Length) &&
char.IsDigit(str[p.Length]))
str = str.Remove(2, 1); // Remove the 'u'
return str;
}
/// <summary> /// <summary>
/// Convert a string to a HTML sequence representing that string. /// Convert a string to a HTML sequence representing that string.
/// </summary> /// </summary>
@@ -324,7 +346,7 @@ namespace ModernKeePassLib.Utility
if(bNbsp) str = str.Replace(" ", @"&nbsp;"); // Before <br /> if(bNbsp) str = str.Replace(" ", @"&nbsp;"); // Before <br />
str = NormalizeNewLines(str, false); str = NormalizeNewLines(str, false);
str = str.Replace("\n", @"<br />" + Environment.NewLine); str = str.Replace("\n", @"<br />" + MessageService.NewLine);
return str; return str;
} }
@@ -497,40 +519,40 @@ namespace ModernKeePassLib.Utility
{ {
string strText = string.Empty; string strText = string.Empty;
if(excp.Message != null) if(!string.IsNullOrEmpty(excp.Message))
strText += excp.Message + Environment.NewLine; strText += excp.Message + MessageService.NewLine;
#if !KeePassLibSD #if !KeePassLibSD
if(excp.Source != null) if(!string.IsNullOrEmpty(excp.Source))
strText += excp.Source + Environment.NewLine; strText += excp.Source + MessageService.NewLine;
#endif #endif
if(excp.StackTrace != null) if(!string.IsNullOrEmpty(excp.StackTrace))
strText += excp.StackTrace + Environment.NewLine; strText += excp.StackTrace + MessageService.NewLine;
#if !KeePassLibSD #if !KeePassLibSD
#if !ModernKeePassLib && !KeePassRT #if !ModernKeePassLib && !KeePassRT
if(excp.TargetSite != null) if(excp.TargetSite != null)
strText += excp.TargetSite.ToString() + Environment.NewLine; strText += excp.TargetSite.ToString() + MessageService.NewLine;
#endif #endif
if(excp.Data != null) if(excp.Data != null)
{ {
strText += Environment.NewLine; strText += MessageService.NewLine;
foreach(DictionaryEntry de in excp.Data) foreach(DictionaryEntry de in excp.Data)
strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" + strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" +
Environment.NewLine; MessageService.NewLine;
} }
#endif #endif
if(excp.InnerException != null) if(excp.InnerException != null)
{ {
strText += Environment.NewLine + "Inner:" + Environment.NewLine; strText += MessageService.NewLine + "Inner:" + MessageService.NewLine;
if(excp.InnerException.Message != null) if(!string.IsNullOrEmpty(excp.InnerException.Message))
strText += excp.InnerException.Message + Environment.NewLine; strText += excp.InnerException.Message + MessageService.NewLine;
#if !KeePassLibSD #if !KeePassLibSD
if(excp.InnerException.Source != null) if(!string.IsNullOrEmpty(excp.InnerException.Source))
strText += excp.InnerException.Source + Environment.NewLine; strText += excp.InnerException.Source + MessageService.NewLine;
#endif #endif
if(excp.InnerException.StackTrace != null) if(!string.IsNullOrEmpty(excp.InnerException.StackTrace))
strText += excp.InnerException.StackTrace + Environment.NewLine; strText += excp.InnerException.StackTrace + MessageService.NewLine;
#if !KeePassLibSD #if !KeePassLibSD
#if !ModernKeePassLib && !KeePassRT #if !ModernKeePassLib && !KeePassRT
if(excp.InnerException.TargetSite != null) if(excp.InnerException.TargetSite != null)
@@ -539,10 +561,10 @@ namespace ModernKeePassLib.Utility
if(excp.InnerException.Data != null) if(excp.InnerException.Data != null)
{ {
strText += Environment.NewLine; strText += MessageService.NewLine;
foreach(DictionaryEntry de in excp.InnerException.Data) foreach(DictionaryEntry de in excp.InnerException.Data)
strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" + strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" +
Environment.NewLine; MessageService.NewLine;
} }
#endif #endif
} }
@@ -1140,32 +1162,58 @@ namespace ModernKeePassLib.Utility
return str; return str;
} }
private static char[] m_vNewLineChars = null;
public static void NormalizeNewLines(ProtectedStringDictionary dict, public static void NormalizeNewLines(ProtectedStringDictionary dict,
bool bWindows) bool bWindows)
{ {
if(dict == null) { Debug.Assert(false); return; } if(dict == null) { Debug.Assert(false); return; }
if(m_vNewLineChars == null) List<string> lKeys = dict.GetKeys();
m_vNewLineChars = new char[]{ '\r', '\n' }; foreach(string strKey in lKeys)
List<string> vKeys = dict.GetKeys();
foreach(string strKey in vKeys)
{ {
ProtectedString ps = dict.Get(strKey); ProtectedString ps = dict.Get(strKey);
if(ps == null) { Debug.Assert(false); continue; } if(ps == null) { Debug.Assert(false); continue; }
string strValue = ps.ReadString(); char[] v = ps.ReadChars();
if(strValue.IndexOfAny(m_vNewLineChars) < 0) continue; if(!IsNewLineNormalized(v, bWindows))
dict.Set(strKey, new ProtectedString(ps.IsProtected, dict.Set(strKey, new ProtectedString(ps.IsProtected,
NormalizeNewLines(strValue, bWindows))); NormalizeNewLines(ps.ReadString(), bWindows)));
MemUtil.ZeroArray<char>(v);
} }
} }
internal static bool IsNewLineNormalized(char[] v, bool bWindows)
{
if(v == null) { Debug.Assert(false); return true; }
if(bWindows)
{
int iFreeCr = -2; // Must be < -1 (for test "!= (i - 1)")
for(int i = 0; i < v.Length; ++i)
{
char ch = v[i];
if(ch == '\r')
{
if(iFreeCr >= 0) return false;
iFreeCr = i;
}
else if(ch == '\n')
{
if(iFreeCr != (i - 1)) return false;
iFreeCr = -2; // Consume \r
}
}
return (iFreeCr < 0); // Ensure no \r at end
}
return (Array.IndexOf<char>(v, '\r') < 0);
}
public static string GetNewLineSeq(string str) public static string GetNewLineSeq(string str)
{ {
if(str == null) { Debug.Assert(false); return Environment.NewLine; } if(str == null) { Debug.Assert(false); return MessageService.NewLine; }
int n = str.Length, nLf = 0, nCr = 0, nCrLf = 0; int n = str.Length, nLf = 0, nCr = 0, nCrLf = 0;
char chLast = char.MinValue; char chLast = char.MinValue;
@@ -1187,7 +1235,7 @@ namespace ModernKeePassLib.Utility
nLf -= nCrLf; nLf -= nCrLf;
int nMax = Math.Max(nCrLf, Math.Max(nCr, nLf)); int nMax = Math.Max(nCrLf, Math.Max(nCr, nLf));
if(nMax == 0) return Environment.NewLine; if(nMax == 0) return MessageService.NewLine;
if(nCrLf == nMax) return "\r\n"; if(nCrLf == nMax) return "\r\n";
return ((nLf == nMax) ? "\n" : "\r"); return ((nLf == nMax) ? "\n" : "\r");
@@ -1319,7 +1367,7 @@ namespace ModernKeePassLib.Utility
try try
{ {
byte[] pbPlain = StrUtil.Utf8.GetBytes(strPlainText); byte[] pbPlain = StrUtil.Utf8.GetBytes(strPlainText);
byte[] pbEnc = ProtectedData.Protect(pbPlain, m_pbOptEnt, byte[] pbEnc = CryptoUtil.ProtectData(pbPlain, m_pbOptEnt,
DataProtectionScope.CurrentUser); DataProtectionScope.CurrentUser);
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT) #if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
@@ -1340,7 +1388,7 @@ namespace ModernKeePassLib.Utility
try try
{ {
byte[] pbEnc = Convert.FromBase64String(strCipherText); byte[] pbEnc = Convert.FromBase64String(strCipherText);
byte[] pbPlain = ProtectedData.Unprotect(pbEnc, m_pbOptEnt, byte[] pbPlain = CryptoUtil.UnprotectData(pbEnc, m_pbOptEnt,
DataProtectionScope.CurrentUser); DataProtectionScope.CurrentUser);
return StrUtil.Utf8.GetString(pbPlain, 0, pbPlain.Length); return StrUtil.Utf8.GetString(pbPlain, 0, pbPlain.Length);

View File

@@ -0,0 +1,65 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Text;
using ModernKeePassLib.Delegates;
namespace KeePassLib.Utility
{
public static class TypeOverridePool
{
private static Dictionary<Type, GFunc<object>> g_d =
new Dictionary<Type, GFunc<object>>();
public static void Register(Type t, GFunc<object> f)
{
if(t == null) throw new ArgumentNullException("t");
if(f == null) throw new ArgumentNullException("f");
g_d[t] = f;
}
public static void Unregister(Type t)
{
if(t == null) throw new ArgumentNullException("t");
g_d.Remove(t);
}
public static bool IsRegistered(Type t)
{
if(t == null) throw new ArgumentNullException("t");
return g_d.ContainsKey(t);
}
public static T CreateInstance<T>()
where T : new()
{
GFunc<object> f;
if(g_d.TryGetValue(typeof(T), out f))
return (T)(f());
return new T();
}
}
}

View File

@@ -299,7 +299,7 @@ namespace ModernKeePassLib.Utility
else // Unhide else // Unhide
{ {
fa &= ~FileAttributes.Hidden; fa &= ~FileAttributes.Hidden;
if((long)fa == 0) fa |= FileAttributes.Normal; if((long)fa == 0) fa = FileAttributes.Normal;
} }
File.SetAttributes(strFile, fa); File.SetAttributes(strFile, fa);
@@ -635,13 +635,12 @@ namespace ModernKeePassLib.Utility
return false; return false;
} }
#if !ModernKeePassLib
public static string GetTempPath() public static string GetTempPath()
{ {
string strDir; string strDir;
if(NativeLib.IsUnix()) if(NativeLib.IsUnix())
strDir = NativeMethods.GetUserRuntimeDir(); strDir = NativeMethods.GetUserRuntimeDir();
#if KeePassRT #if ModernKeePassLib
else strDir = Windows.Storage.ApplicationData.Current.TemporaryFolder.Path; else strDir = Windows.Storage.ApplicationData.Current.TemporaryFolder.Path;
#else #else
else strDir = Path.GetTempPath(); else strDir = Path.GetTempPath();
@@ -655,7 +654,6 @@ namespace ModernKeePassLib.Utility
return strDir; return strDir;
} }
#endif
#if !ModernKeePassLib && !KeePassLibSD #if !ModernKeePassLib && !KeePassLibSD
// Structurally mostly equivalent to UrlUtil.GetFileInfos // Structurally mostly equivalent to UrlUtil.GetFileInfos
@@ -779,5 +777,17 @@ namespace ModernKeePassLib.Utility
return str; return str;
} }
public static char GetDriveLetter(string strPath)
{
if(strPath == null) throw new ArgumentNullException("strPath");
Debug.Assert(default(char) == '\0');
if(strPath.Length < 3) return '\0';
if((strPath[1] != ':') || (strPath[2] != '\\')) return '\0';
char ch = char.ToUpperInvariant(strPath[0]);
return (((ch >= 'A') && (ch <= 'Z')) ? ch : '\0');
}
} }
} }

View File

@@ -0,0 +1,127 @@
/*
KeePass Password Safe - The Open-Source Password Manager
Copyright (C) 2003-2018 Dominik Reichl <dominik.reichl@t-online.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Windows.Data.Xml.Dom;
namespace ModernKeePassLib.Utility
{
public static class XmlUtilEx
{
public static XmlDocument CreateXmlDocument()
{
XmlDocument d = new XmlDocument();
// .NET 4.5.2 and newer do not resolve external XML resources
// by default; for older .NET versions, we explicitly
// prevent resolving
#if !ModernKeePassLib
d.XmlResolver = null; // Default in old .NET: XmlUrlResolver object
#endif
return d;
}
public static XmlReaderSettings CreateXmlReaderSettings()
{
XmlReaderSettings xrs = new XmlReaderSettings();
xrs.CloseInput = false;
xrs.IgnoreComments = true;
xrs.IgnoreProcessingInstructions = true;
xrs.IgnoreWhitespace = true;
#if KeePassUAP || ModernKeePassLib
xrs.DtdProcessing = DtdProcessing.Prohibit;
#else
// Also see PrepMonoDev.sh script
xrs.ProhibitDtd = true; // Obsolete in .NET 4, but still there
// xrs.DtdProcessing = DtdProcessing.Prohibit; // .NET 4 only
xrs.ValidationType = ValidationType.None;
xrs.XmlResolver = null;
#endif
return xrs;
}
public static XmlReader CreateXmlReader(Stream s)
{
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
return XmlReader.Create(s, CreateXmlReaderSettings());
}
public static XmlWriterSettings CreateXmlWriterSettings(bool isVersionGreaterThan4 = false)
{
XmlWriterSettings xws = new XmlWriterSettings();
xws.CloseOutput = isVersionGreaterThan4;
xws.Encoding = StrUtil.Utf8;
xws.Indent = true;
xws.IndentChars = "\t";
xws.NewLineOnAttributes = false;
#if ModernKeePassLib
// This is needed for Argon2Kdf write
xws.Async = true;
#endif
return xws;
}
public static XmlWriter CreateXmlWriter(Stream s, bool isVersionGreaterThan4 = false)
{
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
return XmlWriter.Create(s, CreateXmlWriterSettings(isVersionGreaterThan4));
}
public static void Serialize<T>(Stream s, T t)
{
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
XmlSerializer xs = new XmlSerializer(typeof(T));
using(XmlWriter xw = CreateXmlWriter(s))
{
xs.Serialize(xw, t);
}
}
public static T Deserialize<T>(Stream s)
{
if(s == null) { Debug.Assert(false); throw new ArgumentNullException("s"); }
XmlSerializer xs = new XmlSerializer(typeof(T));
T t = default(T);
using(XmlReader xr = CreateXmlReader(s))
{
t = (T)xs.Deserialize(xr);
}
return t;
}
}
}