diff --git a/src/Exceptions/KeyStorageException.cs b/src/Exceptions/KeyStorageException.cs
new file mode 100644
index 0000000..63774d8
--- /dev/null
+++ b/src/Exceptions/KeyStorageException.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace KeePassWinHello
+{
+ [Serializable]
+ public class KeyStorageException : KeePassWinHelloException
+ {
+ public override bool IsPresentable { get { return true; } }
+
+ public KeyStorageException(string message) : base(message) { }
+ public KeyStorageException(string message, Exception inner) : base(message, inner) { }
+ protected KeyStorageException() { }
+ protected KeyStorageException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/KeePassWinHello.csproj b/src/KeePassWinHello.csproj
index 4157642..4e96bc3 100644
--- a/src/KeePassWinHello.csproj
+++ b/src/KeePassWinHello.csproj
@@ -61,6 +61,7 @@
+
@@ -124,4 +125,4 @@
-
\ No newline at end of file
+
diff --git a/src/KeyManagement/KeyManager.cs b/src/KeyManagement/KeyManager.cs
index d55eed8..dacc792 100644
--- a/src/KeyManagement/KeyManager.cs
+++ b/src/KeyManagement/KeyManager.cs
@@ -198,6 +198,10 @@ private void Lock(bool isDbLocking, string dbPath, CompositeKey databaseMasterKe
{
// it's OK
}
+ catch (KeyStorageException ex)
+ {
+ _uiContextManager.CurrentContext.ShowError(ex);
+ }
}
public void RevokeAll()
diff --git a/src/KeyManagement/Storage/KeyWindowsStorage.cs b/src/KeyManagement/Storage/KeyWindowsStorage.cs
index 1dca357..a863b44 100644
--- a/src/KeyManagement/Storage/KeyWindowsStorage.cs
+++ b/src/KeyManagement/Storage/KeyWindowsStorage.cs
@@ -11,6 +11,7 @@ internal class KeyWindowsStorage : IKeyStorage
{
#region Credential Manager API
private const int ERROR_NOT_FOUND = 0x490;
+ private const int ERROR_NO_SUCH_LOGON_SESSION = 0x520;
private const int CRED_TYPE_GENERIC = 0x1;
@@ -96,7 +97,7 @@ public void AddOrUpdate(string dbPath, ProtectedKey protectedKey)
Marshal.Copy(data, 0, ncred.CredentialBlob, data.Length);
ncred.CredentialBlobSize = (uint)data.Length;
- CredWrite(ref ncred, 0).ThrowOnError("CredWrite");
+ WriteCredential(ref ncred);
}
finally
{
@@ -111,6 +112,38 @@ public void AddOrUpdate(string dbPath, ProtectedKey protectedKey)
}
}
+ private static void WriteCredential(ref CREDENTIAL credential)
+ {
+ if (CredWrite(ref credential, 0).Result)
+ return;
+
+ int errorCode = Marshal.GetLastWin32Error();
+ if (errorCode == ERROR_NO_SUCH_LOGON_SESSION)
+ throw new KeyStorageException(GetNoLogonSessionMessage());
+
+ throw new EnviromentErrorException("CredWrite", errorCode);
+ }
+
+ private static string GetNoLogonSessionMessage()
+ {
+ string message = "Windows Credential Manager cannot save the database key because Windows reported that the current logon session is not available. The database key was not saved persistently.";
+ if (IsCurrentProcessElevatedSafe())
+ message += " This can happen when KeePass is running as Administrator; restart KeePass without elevation to use Credential Manager storage.";
+ return message;
+ }
+
+ private static bool IsCurrentProcessElevatedSafe()
+ {
+ try
+ {
+ return UAC.IsCurrentProcessElevated;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
public bool ContainsKey(string dbPath)
{
IntPtr ncredPtr = IntPtr.Zero;