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
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModernKeePass.LibTest", "ModernKeePassLib.Test\ModernKeePass.LibTest.csproj", "{0A4279CF-2A67-4868-9906-052E50C25F3B}"
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}"
EndProject
Global
@@ -89,22 +87,6 @@ Global
{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.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.Build.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\NavigateToUrlAction.cs" />
<Compile Include="Actions\SetupFocusAction.cs" />
<Compile Include="Aop\DatabaseChangedProxy.cs" />
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="Aop\DatabaseChanged.cs" />
<Compile Include="Exceptions\DatabaseOpenedException.cs" />
<Compile Include="Interfaces\ILicenseService.cs" />
<Compile Include="Interfaces\IProxyInvocationHandler.cs" />

View File

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

View File

@@ -352,6 +352,13 @@ namespace ModernKeePassLib.Collections
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)
{
if(tArray == null) throw new ArgumentNullException("tArray");

View File

@@ -223,48 +223,43 @@ namespace ModernKeePassLib.Cryptography
pb = DiagnosticsExt.GetProcessEntropy();
MemUtil.Write(ms, pb);
#elif !KeePassLibSD
Process p = null;
try
{
p = Process.GetCurrentProcess();
using(Process p = Process.GetCurrentProcess())
{
pb = MemUtil.Int64ToBytes(p.Handle.ToInt64());
MemUtil.Write(ms, pb);
pb = MemUtil.Int32ToBytes(p.HandleCount);
MemUtil.Write(ms, pb);
pb = MemUtil.Int32ToBytes(p.Id);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.NonpagedSystemMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PagedMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PagedSystemMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PeakPagedMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PeakVirtualMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PeakWorkingSet64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PrivateMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.StartTime.ToBinary());
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.VirtualMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.WorkingSet64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.Handle.ToInt64());
MemUtil.Write(ms, pb);
pb = MemUtil.Int32ToBytes(p.HandleCount);
MemUtil.Write(ms, pb);
pb = MemUtil.Int32ToBytes(p.Id);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.NonpagedSystemMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PagedMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PagedSystemMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PeakPagedMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PeakVirtualMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PeakWorkingSet64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.PrivateMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.StartTime.ToBinary());
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.VirtualMemorySize64);
MemUtil.Write(ms, pb);
pb = MemUtil.Int64ToBytes(p.WorkingSet64);
MemUtil.Write(ms, pb);
// Not supported in Mono 1.2.6:
// pb = MemUtil.UInt32ToBytes((uint)p.SessionId);
// MemUtil.Write(ms, pb);
// Not supported in Mono 1.2.6:
// pb = MemUtil.UInt32ToBytes((uint)p.SessionId);
// MemUtil.Write(ms, pb);
}
}
catch(Exception) { Debug.Assert(NativeLib.IsUnix()); }
finally
{
try { if(p != null) p.Dispose(); }
catch(Exception) { Debug.Assert(false); }
}
#endif
#endif

View File

@@ -35,6 +35,42 @@ namespace ModernKeePassLib.Cryptography
{
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)
{
if(pbData == null) throw new ArgumentNullException("pbData");
@@ -165,5 +201,37 @@ namespace ModernKeePassLib.Cryptography
return null;
}
#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()
{
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(!MonoWorkarounds.IsRequired(1468)) return false; // Can be turned off

View File

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

View File

@@ -75,7 +75,7 @@ namespace ModernKeePassLib.Cryptography
#endif
#endif
try { return IsPopularPasswordPriv(vPassword, out uDictSize); }
try { return IsPopularPasswordPriv(vPassword, out uDictSize); }
catch(Exception) { Debug.Assert(false); }
uDictSize = 0;

View File

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

View File

@@ -22,6 +22,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
@@ -55,6 +56,25 @@ namespace ModernKeePassLib.Cryptography
/// </summary>
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();
TestFipsComplianceProblems(); // Must be the first test
@@ -76,10 +96,6 @@ namespace ModernKeePassLib.Cryptography
TestStrUtil();
TestUrlUtil();
Debug.Assert((int)PwIcon.World == 1);
Debug.Assert((int)PwIcon.Warning == 2);
Debug.Assert((int)PwIcon.BlackBerry == 68);
#if KeePassUAP
SelfTestEx.Perform();
#endif
@@ -944,6 +960,14 @@ namespace ModernKeePassLib.Cryptography
if(ps.ReadString() != str)
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
}
@@ -1041,6 +1065,30 @@ namespace ModernKeePassLib.Cryptography
throw new InvalidOperationException("StrUtil-Case1");
if(string.Equals(@"a<b", @"a>b", StrUtil.CaseIgnoreCmp))
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
}

View File

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

View File

@@ -65,11 +65,8 @@ namespace ModernKeePassLib.Keys
/// </summary>
public KcpUserAccount()
{
// Test if ProtectedData is supported -- throws an exception
// when running on an old system (Windows 98 / ME).
byte[] pbDummyData = new byte[128];
ProtectedData.Protect(pbDummyData, m_pbEntropy,
DataProtectionScope.CurrentUser);
if(!CryptoUtil.IsProtectedDataSupported)
throw new PlatformNotSupportedException(); // Windows 98/ME
byte[] pbKey = LoadUserKey(false);
if(pbKey == null) pbKey = CreateUserKey();
@@ -88,17 +85,17 @@ namespace ModernKeePassLib.Keys
// m_pbKeyData = null;
// }
private static string GetUserKeyFilePath(bool bCreate)
{
private static string GetUserKeyFilePath(bool bCreate)
{
#if ModernKeePassLib
string strUserDir = Windows.Storage.ApplicationData.Current.RoamingFolder.Path;
string strUserDir = Windows.Storage.ApplicationData.Current.RoamingFolder.Path;
#else
string strUserDir = Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData);
#endif
strUserDir = UrlUtil.EnsureTerminatingSeparator(strUserDir, false);
strUserDir += PwDefs.ShortProductName;
strUserDir = UrlUtil.EnsureTerminatingSeparator(strUserDir, false);
strUserDir += PwDefs.ShortProductName;
#if !ModernKeePassLib
@@ -115,9 +112,9 @@ namespace ModernKeePassLib.Keys
byte[] pbKey = null;
#if !KeePassLibSD
try
{
string strFilePath = GetUserKeyFilePath(false);
try
{
string strFilePath = GetUserKeyFilePath(false);
#if ModernKeePassLib
var fileStream = StorageFile.GetFileFromPathAsync(strFilePath).GetAwaiter().GetResult().OpenStreamForReadAsync().GetAwaiter().GetResult();
@@ -128,7 +125,7 @@ namespace ModernKeePassLib.Keys
byte[] pbProtectedKey = File.ReadAllBytes(strFilePath);
#endif
pbKey = ProtectedData.Unprotect(pbProtectedKey, m_pbEntropy,
pbKey = CryptoUtil.UnprotectData(pbProtectedKey, m_pbEntropy,
DataProtectionScope.CurrentUser);
}
catch(Exception)
@@ -138,7 +135,7 @@ namespace ModernKeePassLib.Keys
}
#endif
return pbKey;
return pbKey;
}
private static byte[] CreateUserKey()
@@ -148,9 +145,9 @@ namespace ModernKeePassLib.Keys
#else
string strFilePath = GetUserKeyFilePath(true);
byte[] pbRandomKey = CryptoRandom.Instance.GetRandomBytes(64);
byte[] pbProtectedKey = ProtectedData.Protect(pbRandomKey,
m_pbEntropy, DataProtectionScope.CurrentUser);
byte[] pbRandomKey = CryptoRandom.Instance.GetRandomBytes(64);
byte[] pbProtectedKey = CryptoUtil.ProtectData(pbRandomKey,
m_pbEntropy, DataProtectionScope.CurrentUser);
#if ModernKeePassLib
var fileStream = StorageFile.GetFileFromPathAsync(strFilePath).GetAwaiter().GetResult().OpenStreamForWriteAsync().GetAwaiter().GetResult();
fileStream.Write(pbProtectedKey, 0, (int)fileStream.Length);

View File

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

View File

@@ -2,7 +2,7 @@
<package >
<metadata>
<id>ModernKeePassLib</id>
<version>2.38.2</version>
<version>2.39.1</version>
<title>ModernKeePassLib</title>
<authors>Geoffroy Bonneville</authors>
<owners>Geoffroy Bonneville</owners>
@@ -10,7 +10,7 @@
<projectUrl>https://github.com/wismna/ModernKeePass</projectUrl>
<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>
<releaseNotes>Deactivation of buggy custom icon implementation</releaseNotes>
<releaseNotes>Implementation of KeePass library version 2.39.1</releaseNotes>
<copyright>Copyright © 2018 Geoffroy Bonneville</copyright>
<tags>KeePass KeePassLib Portable PCL NetStandard</tags>
<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; }
}
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;
public static ulong MonoVersion
{
@@ -195,19 +177,21 @@ namespace ModernKeePassLib.Native
t = DesktopType.Xfce;
else if(strXdg.Equals("MATE", sc))
t = DesktopType.Mate;
else if(strXdg.Equals("X-Cinnamon", sc))
else if(strXdg.Equals("X-Cinnamon", sc)) // Mint 18.3
t = DesktopType.Cinnamon;
else if(strXdg.Equals("Pantheon", sc)) // Elementary OS
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
t = DesktopType.Kde;
else if(strXdg.Equals("GNOME", sc))
{
if(strGdm.Equals("cinnamon", sc)) // Mint 13
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); }
}
@@ -243,6 +227,7 @@ namespace ModernKeePassLib.Native
RunProcessDelegate fnRun = delegate()
{
Process pToDispose = null;
try
{
ProcessStartInfo psi = new ProcessStartInfo();
@@ -258,6 +243,7 @@ namespace ModernKeePassLib.Native
if(!string.IsNullOrEmpty(strParams)) psi.Arguments = strParams;
Process p = Process.Start(psi);
pToDispose = p;
if(strStdInput != null)
{
@@ -274,9 +260,11 @@ namespace ModernKeePassLib.Native
p.WaitForExit();
else if((f & AppRunFlags.GCKeepAlive) != AppRunFlags.None)
{
pToDispose = null; // Thread disposes it
Thread th = new Thread(delegate()
{
try { p.WaitForExit(); }
try { p.WaitForExit(); p.Dispose(); }
catch(Exception) { Debug.Assert(false); }
});
th.Start();
@@ -289,6 +277,11 @@ namespace ModernKeePassLib.Native
#else
catch(Exception) { }
#endif
finally
{
try { if(pToDispose != null) pToDispose.Dispose(); }
catch(Exception) { Debug.Assert(false); }
}
return null;
};

View File

@@ -31,6 +31,14 @@ namespace ModernKeePassLib.Native
{
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_HIDDEN = 0x00000008;
@@ -47,10 +55,9 @@ namespace ModernKeePassLib.Native
internal static bool TransformKey(IntPtr pBuf256, IntPtr pKey256,
UInt64 uRounds)
{
if(Marshal.SizeOf(typeof(IntPtr)) == 8)
return TransformKey64(pBuf256, pKey256, uRounds);
else
if(IntPtr.Size == 4)
return TransformKey32(pBuf256, pKey256, uRounds);
return TransformKey64(pBuf256, pKey256, uRounds);
}
[DllImport("KeePassNtv32.dll", EntryPoint = "TransformKeyTimed")]
@@ -66,10 +73,9 @@ namespace ModernKeePassLib.Native
internal static bool TransformKeyTimed(IntPtr pBuf256, IntPtr pKey256,
ref UInt64 puRounds, UInt32 uSeconds)
{
if(Marshal.SizeOf(typeof(IntPtr)) == 8)
return TransformKeyTimed64(pBuf256, pKey256, ref puRounds, uSeconds);
else
if(IntPtr.Size == 4)
return TransformKeyTimed32(pBuf256, pKey256, ref puRounds, uSeconds);
return TransformKeyTimed64(pBuf256, pKey256, ref puRounds, uSeconds);
} */
#if !KeePassUAP
@@ -86,10 +92,9 @@ namespace ModernKeePassLib.Native
internal static bool TransformKey(IntPtr pBuf256, IntPtr pKey256,
UInt64 uRounds)
{
if(NativeLib.PointerSize == 8)
return TransformKey64(pBuf256, pKey256, uRounds);
else
if(IntPtr.Size == 4)
return TransformKey32(pBuf256, pKey256, uRounds);
return TransformKey64(pBuf256, pKey256, uRounds);
}
[DllImport("KeePassLibC32.dll", EntryPoint = "TransformKeyBenchmark256")]
@@ -100,9 +105,9 @@ namespace ModernKeePassLib.Native
internal static UInt64 TransformKeyBenchmark(UInt32 uTimeMs)
{
if(NativeLib.PointerSize == 8)
return TransformKeyBenchmark64(uTimeMs);
return TransformKeyBenchmark32(uTimeMs);
if(IntPtr.Size == 4)
return TransformKeyBenchmark32(uTimeMs);
return TransformKeyBenchmark64(uTimeMs);
}
#endif
@@ -116,11 +121,64 @@ namespace ModernKeePassLib.Native
internal static bool TfShowLangBar(uint dwFlags)
{
if(Marshal.SizeOf(typeof(IntPtr)) == 8)
return TF_ShowLangBar64(dwFlags);
return TF_ShowLangBar32(dwFlags);
if(IntPtr.Size == 4) return TF_ShowLangBar32(dwFlags);
return TF_ShowLangBar64(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)
[DllImport("ShlWApi.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]

View File

@@ -1,6 +1,6 @@
/*
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
it under the terms of the GNU General Public License as published by
@@ -27,7 +27,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("wismna")]
[assembly: AssemblyProduct("ModernKeePassLib")]
[assembly: AssemblyCopyright("Copyright © 2017 Geoffroy Bonneville")]
[assembly: AssemblyCopyright("Copyright © 2018 Geoffroy Bonneville")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -40,5 +40,5 @@ using System.Runtime.InteropServices;
#endif
// Assembly version information
[assembly: AssemblyVersion("2.37.0.2000")]
[assembly: AssemblyFileVersion("2.37.0.2000")]
[assembly: AssemblyVersion("2.39.1.*")]
[assembly: AssemblyFileVersion("2.39.1.0")]

View File

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

View File

@@ -55,18 +55,18 @@ namespace ModernKeePassLib
/// e.g. 2.19 = 0x02130000.
/// It is highly recommended to use <c>FileVersion64</c> instead.
/// </summary>
public const uint Version32 = 0x02260000;
public const uint Version32 = 0x02270100;
/// <summary>
/// Version, encoded as 64-bit unsigned integer
/// (component-wise, 16 bits per component).
/// </summary>
public const ulong FileVersion64 = 0x0002002600000000UL;
public const ulong FileVersion64 = 0x0002002700010000UL;
/// <summary>
/// Version, encoded as string.
/// </summary>
public const string VersionString = "2.38";
public const string VersionString = "2.39.1";
public const string Copyright = @"Copyright © 2003-2018 Dominik Reichl";
@@ -219,6 +219,15 @@ namespace ModernKeePassLib
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

View File

@@ -782,45 +782,49 @@ namespace ModernKeePassLib
}
/// <summary>
/// Approximate the total size of this entry in bytes (including
/// strings, binaries and history entries).
/// Approximate the total size (in process memory) of this entry
/// in bytes (including strings, binaries and history entries).
/// </summary>
/// <returns>Size in bytes.</returns>
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)
{
uSize += (ulong)kvpStr.Key.Length;
uSize += (ulong)kvpStr.Value.Length;
}
cc += (ulong)kvpStr.Key.Length + (ulong)kvpStr.Value.Length;
cb += (ulong)m_listBinaries.UCount * 65;
foreach(KeyValuePair<string, ProtectedBinary> kvpBin in m_listBinaries)
{
uSize += (ulong)kvpBin.Key.Length;
uSize += kvpBin.Value.Length;
cc += (ulong)kvpBin.Key.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)
{
uSize += (ulong)a.WindowName.Length;
uSize += (ulong)a.Sequence.Length;
}
cc += (ulong)a.WindowName.Length + (ulong)a.Sequence.Length;
cb += (ulong)m_listHistory.UCount * 8;
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)
uSize += (ulong)strTag.Length;
cc += (ulong)strTag.Length;
cb += (ulong)m_dCustomData.Count * 16;
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)

View File

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

View File

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

View File

@@ -19,6 +19,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Text;
@@ -32,19 +33,25 @@ using ModernKeePassLib.Utility;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Streams;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Resources;
namespace ModernKeePassLib.Serialization
{
public sealed class FileTransactionEx
public sealed class FileTransactionEx : IDisposable
{
private bool m_bTransacted;
private IOConnectionInfo m_iocBase;
private IOConnectionInfo m_iocBase; // Null means disposed
private IOConnectionInfo m_iocTemp;
private IOConnectionInfo m_iocTxfMidFallback = null; // Null <=> TxF not used
private bool m_bMadeUnhidden = false;
private List<IOConnectionInfo> m_lToDelete = new List<IOConnectionInfo>();
private const string StrTempSuffix = ".tmp";
private const string StrTxfTempPrefix = PwDefs.ShortProductName + "_TxF_";
private const string StrTxfTempSuffix = ".tmp";
private static Dictionary<string, bool> g_dEnabled =
new Dictionary<string, bool>(StrUtil.CaseIgnoreComparer);
@@ -56,26 +63,23 @@ namespace ModernKeePassLib.Serialization
set { g_bExtraSafe = value; }
}
public FileTransactionEx(IOConnectionInfo iocBaseFile)
public FileTransactionEx(IOConnectionInfo iocBaseFile) :
this(iocBaseFile, true)
{
Initialize(iocBaseFile, true);
}
public FileTransactionEx(IOConnectionInfo iocBaseFile, bool bTransacted)
{
Initialize(iocBaseFile, bTransacted);
}
private void Initialize(IOConnectionInfo iocBaseFile, bool bTransacted)
{
if(iocBaseFile == null) throw new ArgumentNullException("iocBaseFile");
m_bTransacted = bTransacted;
m_iocBase = iocBaseFile.CloneDeep();
if(m_iocBase.IsLocalFile())
m_iocBase.Path = UrlUtil.GetShortestAbsolutePath(m_iocBase.Path);
string strPath = m_iocBase.Path;
#if !ModernKeePassLib
if(m_iocBase.IsLocalFile())
{
try
@@ -93,10 +97,23 @@ namespace ModernKeePassLib.Serialization
if((long)(fa & FileAttributes.ReparsePoint) != 0)
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); }
}
#if !ModernKeePassLib
// Prevent transactions for FTP URLs under .NET 4.0 in order to
// 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
@@ -118,90 +135,152 @@ namespace ModernKeePassLib.Serialization
{
m_iocTemp = m_iocBase.CloneDeep();
m_iocTemp.Path += StrTempSuffix;
TxfPrepare(); // Adjusts m_iocTemp
}
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()
{
if(!m_bTransacted) m_bMadeUnhidden = UrlUtil.UnhideFile(m_iocTemp.Path);
else // m_bTransacted
{
try { IOConnection.DeleteFile(m_iocTemp); }
catch(Exception) { }
}
if(m_iocBase == null) { Debug.Assert(false); throw new ObjectDisposedException(null); }
if(!m_bTransacted) m_bMadeUnhidden |= UrlUtil.UnhideFile(m_iocTemp.Path);
return IOConnection.OpenWrite(m_iocTemp);
}
public void CommitWrite()
{
if(m_bTransacted) CommitWriteTransaction();
else // !m_bTransacted
if(m_iocBase == null) { Debug.Assert(false); throw new ObjectDisposedException(null); }
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()
{
bool bMadeUnhidden = UrlUtil.UnhideFile(m_iocBase.Path);
#if (!ModernKeePassLib && !KeePassLibSD)
FileSecurity bkSecurity = null;
bool bEfsEncrypted = false;
#endif
if(g_bExtraSafe)
{
if(!IOConnection.FileExists(m_iocTemp))
throw new FileNotFoundException(m_iocTemp.Path +
Environment.NewLine + KLRes.FileSaveFailed);
MessageService.NewLine + KLRes.FileSaveFailed);
}
if(IOConnection.FileExists(m_iocBase))
{
#if (!ModernKeePassLib && !KeePassLibSD)
if(m_iocBase.IsLocalFile())
{
try
{
FileAttributes faBase = File.GetAttributes(m_iocBase.Path);
bEfsEncrypted = ((long)(faBase & FileAttributes.Encrypted) != 0);
bool bMadeUnhidden = UrlUtil.UnhideFile(m_iocBase.Path);
DateTime tCreation = File.GetCreationTimeUtc(m_iocBase.Path);
bkSecurity = File.GetAccessControl(m_iocBase.Path);
#if !ModernKeePassLib
// '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;
File.SetCreationTimeUtc(m_iocTemp.Path, tCreation);
}
catch(Exception) { Debug.Assert(false); }
}
bool bEfsEncrypted = false;
byte[] pbSec = null;
#endif
DateTime? otCreation = null;
IOConnection.DeleteFile(m_iocBase);
}
IOConnection.RenameFile(m_iocTemp, m_iocBase);
#if (!ModernKeePassLib && !KeePassLibSD)
if(m_iocBase.IsLocalFile())
bool bBaseExists = IOConnection.FileExists(m_iocBase);
if(bBaseExists && m_iocBase.IsLocalFile())
{
// FileAttributes faBase = FileAttributes.Normal;
try
{
if(bEfsEncrypted)
{
try { File.Encrypt(m_iocBase.Path); }
catch(Exception) { Debug.Assert(false); }
}
if(bkSecurity != null)
File.SetAccessControl(m_iocBase.Path, bkSecurity);
}
catch(Exception) { Debug.Assert(false); }
}
#if !ModernKeePassLib
FileAttributes faBase = File.GetAttributes(m_iocBase.Path);
bEfsEncrypted = ((long)(faBase & FileAttributes.Encrypted) != 0);
try { if(bEfsEncrypted) File.Decrypt(m_iocBase.Path); } // For TxF
catch(Exception) { Debug.Assert(false); }
#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()); }
if(bMadeUnhidden) UrlUtil.HideFile(m_iocBase.Path, true); // Hide again
// if((long)(faBase & FileAttributes.ReadOnly) != 0)
// throw new UnauthorizedAccessException();
}
if(!TxfMove())
{
if(bBaseExists) IOConnection.DeleteFile(m_iocBase);
IOConnection.RenameFile(m_iocTemp, m_iocBase);
}
else { Debug.Assert(pbSec != null); } // TxF success => NTFS => has ACL
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)
{
try { File.Encrypt(m_iocBase.Path); }
catch(Exception) { Debug.Assert(false); }
}
// File.SetAccessControl(m_iocBase.Path, secPrev);
// Directly calling File.SetAccessControl with the previous
// FileSecurity object does not work; the binary form
// 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
}
catch(Exception) { Debug.Assert(false); }
if(bMadeUnhidden) UrlUtil.HideFile(m_iocBase.Path, true);
}
// For plugins
@@ -213,5 +292,155 @@ namespace ModernKeePassLib.Serialization
g_dEnabled[strPrefix] = obTransacted.Value;
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;
if(m_strProxyPort.Length > 0)
strInfo += ":" + m_strProxyPort;
MessageService.ShowWarning(strInfo, ex.Message);
MessageService.ShowWarning(strInfo, ex);
}
#endif
@@ -642,7 +642,7 @@ namespace ModernKeePassLib.Serialization
#endif
}
public static bool FileExists(IOConnectionInfo ioc)
public static bool FileExists(IOConnectionInfo ioc)
{
return FileExists(ioc, false);
}

View File

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

View File

@@ -87,6 +87,7 @@ namespace ModernKeePassLib.Serialization
m_format = fmt;
m_slLogger = slLogger;
m_xmlWriter = null;
PwGroup pgRoot = (pgDataSource ?? m_pwDatabase.RootGroup);
UTF8Encoding encNoBom = StrUtil.Utf8;
@@ -203,44 +204,25 @@ namespace ModernKeePassLib.Serialization
throw new ArgumentOutOfRangeException("fmt");
}
#if ModernKeePassLib || KeePassUAP
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;
m_xmlWriter = XmlUtilEx.CreateXmlWriter(sXml, m_uFileVersion >= FileVersion32_4);
WriteDocument(pgRoot);
m_xmlWriter.Flush();
m_xmlWriter.Dispose();
}
finally
{
CommonCleanUpWrite(lStreams, sHashing);
if(pbCipherKey != null) MemUtil.ZeroByteArray(pbCipherKey);
if(pbHmacKey64 != null) MemUtil.ZeroByteArray(pbHmacKey64);
CommonCleanUpWrite(lStreams, sHashing);
}
}
private void CommonCleanUpWrite(List<Stream> lStreams, HashingStreamEx sHashing)
{
if(m_xmlWriter != null) { m_xmlWriter.Dispose(); m_xmlWriter = null; }
CloseStreams(lStreams);
Debug.Assert(lStreams.Contains(sHashing)); // sHashing must be closed
@@ -249,7 +231,6 @@ namespace ModernKeePassLib.Serialization
CleanUpInnerRandomStream();
m_xmlWriter = null;
m_pbHashOfHeader = null;
}

View File

@@ -403,8 +403,8 @@ namespace ModernKeePassLib.Serialization
ICipherEngine iCipher = CipherPool.GlobalPool.GetCipher(pu);
if(iCipher == null) // CryptographicExceptions are translated to "file corrupted"
throw new Exception(KLRes.FileUnknownCipher +
Environment.NewLine + KLRes.FileNewVerOrPlgReq +
Environment.NewLine + "UUID: " + pu.ToHexString() + ".");
MessageService.NewParagraph + KLRes.FileNewVerOrPlgReq +
MessageService.NewParagraph + "UUID: " + pu.ToHexString() + ".");
ICipherEngine2 iCipher2 = (iCipher as ICipherEngine2);
if(iCipher2 != null)
@@ -469,14 +469,14 @@ namespace ModernKeePassLib.Serialization
Debug.Assert((lStreams.IndexOf(lStreams[i]) == i) &&
(lStreams.LastIndexOf(lStreams[i]) == i));
try { lStreams[i].Dispose(); }
// Unnecessary exception from CryptoStream with
// RijndaelManagedTransform when a stream hasn't been
// read completely (e.g. incorrect master key)
try { lStreams[i].Dispose(); }
// Unnecessary exception from CryptoStream with
// RijndaelManagedTransform when a stream hasn't been
// read completely (e.g. incorrect master key)
#if !ModernKeePassLib
catch(CryptographicException) { }
catch(CryptographicException) { }
#endif
catch (Exception) { Debug.Assert(false); }
catch(Exception) { Debug.Assert(false); }
}
// Do not clear the list
@@ -539,8 +539,8 @@ namespace ModernKeePassLib.Serialization
FileStream fs = new FileStream(strPath, FileMode.Create,
FileAccess.Write, FileShare.None);
byte[] pbData = pb.ReadData();
fs.Write(pbData, 0, pbData.Length);
fs.Close();
try { File.WriteAllBytes(strPath, pbData); }
finally { if(pb.IsProtected) MemUtil.ZeroByteArray(pbData); }
#endif
}
}

View File

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

View File

@@ -496,11 +496,21 @@ namespace ModernKeePassLib.Utility
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)
{
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)
{
return ((u << nBits) | (u >> (32 - nBits)));

View File

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

View File

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

View File

@@ -227,7 +227,7 @@ namespace ModernKeePassLib.Utility
"Unicode (UTF-8)", StrUtil.Utf8, 1, new byte[] { 0xEF, 0xBB, 0xBF }));
#else
#if !KeePassLibSD
Encoding.Default.EncodingName,
Encoding.Default.EncodingName,
#else
Encoding.Default.WebName,
#endif
@@ -301,6 +301,28 @@ namespace ModernKeePassLib.Utility
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>
/// Convert a string to a HTML sequence representing that string.
/// </summary>
@@ -324,7 +346,7 @@ namespace ModernKeePassLib.Utility
if(bNbsp) str = str.Replace(" ", @"&nbsp;"); // Before <br />
str = NormalizeNewLines(str, false);
str = str.Replace("\n", @"<br />" + Environment.NewLine);
str = str.Replace("\n", @"<br />" + MessageService.NewLine);
return str;
}
@@ -497,40 +519,40 @@ namespace ModernKeePassLib.Utility
{
string strText = string.Empty;
if(excp.Message != null)
strText += excp.Message + Environment.NewLine;
if(!string.IsNullOrEmpty(excp.Message))
strText += excp.Message + MessageService.NewLine;
#if !KeePassLibSD
if(excp.Source != null)
strText += excp.Source + Environment.NewLine;
if(!string.IsNullOrEmpty(excp.Source))
strText += excp.Source + MessageService.NewLine;
#endif
if(excp.StackTrace != null)
strText += excp.StackTrace + Environment.NewLine;
if(!string.IsNullOrEmpty(excp.StackTrace))
strText += excp.StackTrace + MessageService.NewLine;
#if !KeePassLibSD
#if !ModernKeePassLib && !KeePassRT
if(excp.TargetSite != null)
strText += excp.TargetSite.ToString() + Environment.NewLine;
strText += excp.TargetSite.ToString() + MessageService.NewLine;
#endif
if(excp.Data != null)
{
strText += Environment.NewLine;
strText += MessageService.NewLine;
foreach(DictionaryEntry de in excp.Data)
strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" +
Environment.NewLine;
MessageService.NewLine;
}
#endif
if(excp.InnerException != null)
{
strText += Environment.NewLine + "Inner:" + Environment.NewLine;
if(excp.InnerException.Message != null)
strText += excp.InnerException.Message + Environment.NewLine;
strText += MessageService.NewLine + "Inner:" + MessageService.NewLine;
if(!string.IsNullOrEmpty(excp.InnerException.Message))
strText += excp.InnerException.Message + MessageService.NewLine;
#if !KeePassLibSD
if(excp.InnerException.Source != null)
strText += excp.InnerException.Source + Environment.NewLine;
if(!string.IsNullOrEmpty(excp.InnerException.Source))
strText += excp.InnerException.Source + MessageService.NewLine;
#endif
if(excp.InnerException.StackTrace != null)
strText += excp.InnerException.StackTrace + Environment.NewLine;
if(!string.IsNullOrEmpty(excp.InnerException.StackTrace))
strText += excp.InnerException.StackTrace + MessageService.NewLine;
#if !KeePassLibSD
#if !ModernKeePassLib && !KeePassRT
if(excp.InnerException.TargetSite != null)
@@ -539,10 +561,10 @@ namespace ModernKeePassLib.Utility
if(excp.InnerException.Data != null)
{
strText += Environment.NewLine;
strText += MessageService.NewLine;
foreach(DictionaryEntry de in excp.InnerException.Data)
strText += @"'" + de.Key + @"' -> '" + de.Value + @"'" +
Environment.NewLine;
MessageService.NewLine;
}
#endif
}
@@ -1140,32 +1162,58 @@ namespace ModernKeePassLib.Utility
return str;
}
private static char[] m_vNewLineChars = null;
public static void NormalizeNewLines(ProtectedStringDictionary dict,
bool bWindows)
{
if(dict == null) { Debug.Assert(false); return; }
if(m_vNewLineChars == null)
m_vNewLineChars = new char[]{ '\r', '\n' };
List<string> vKeys = dict.GetKeys();
foreach(string strKey in vKeys)
List<string> lKeys = dict.GetKeys();
foreach(string strKey in lKeys)
{
ProtectedString ps = dict.Get(strKey);
if(ps == null) { Debug.Assert(false); continue; }
string strValue = ps.ReadString();
if(strValue.IndexOfAny(m_vNewLineChars) < 0) continue;
dict.Set(strKey, new ProtectedString(ps.IsProtected,
NormalizeNewLines(strValue, bWindows)));
char[] v = ps.ReadChars();
if(!IsNewLineNormalized(v, bWindows))
dict.Set(strKey, new ProtectedString(ps.IsProtected,
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)
{
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;
char chLast = char.MinValue;
@@ -1187,7 +1235,7 @@ namespace ModernKeePassLib.Utility
nLf -= nCrLf;
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";
return ((nLf == nMax) ? "\n" : "\r");
@@ -1319,7 +1367,7 @@ namespace ModernKeePassLib.Utility
try
{
byte[] pbPlain = StrUtil.Utf8.GetBytes(strPlainText);
byte[] pbEnc = ProtectedData.Protect(pbPlain, m_pbOptEnt,
byte[] pbEnc = CryptoUtil.ProtectData(pbPlain, m_pbOptEnt,
DataProtectionScope.CurrentUser);
#if (!ModernKeePassLib && !KeePassLibSD && !KeePassRT)
@@ -1340,7 +1388,7 @@ namespace ModernKeePassLib.Utility
try
{
byte[] pbEnc = Convert.FromBase64String(strCipherText);
byte[] pbPlain = ProtectedData.Unprotect(pbEnc, m_pbOptEnt,
byte[] pbPlain = CryptoUtil.UnprotectData(pbEnc, m_pbOptEnt,
DataProtectionScope.CurrentUser);
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
{
fa &= ~FileAttributes.Hidden;
if((long)fa == 0) fa |= FileAttributes.Normal;
if((long)fa == 0) fa = FileAttributes.Normal;
}
File.SetAttributes(strFile, fa);
@@ -635,13 +635,12 @@ namespace ModernKeePassLib.Utility
return false;
}
#if !ModernKeePassLib
public static string GetTempPath()
{
string strDir;
if(NativeLib.IsUnix())
strDir = NativeMethods.GetUserRuntimeDir();
#if KeePassRT
#if ModernKeePassLib
else strDir = Windows.Storage.ApplicationData.Current.TemporaryFolder.Path;
#else
else strDir = Path.GetTempPath();
@@ -655,7 +654,6 @@ namespace ModernKeePassLib.Utility
return strDir;
}
#endif
#if !ModernKeePassLib && !KeePassLibSD
// Structurally mostly equivalent to UrlUtil.GetFileInfos
@@ -779,5 +777,17 @@ namespace ModernKeePassLib.Utility
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;
}
}
}