Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accessing entities using EntityFramework on encrypted database failed [DNET832] #767

Closed
firebird-automations opened this issue Jun 29, 2018 · 3 comments

Comments

@firebird-automations
Copy link

Submitted by: Michael Wresche (michawresche)

Duplicates DNET821

At first:
- We have an encrypted Firebird database which will be accessed from our application by using Entity Framework
- The encryption plugin uses a file-based key so the connection string does not contain any encryption information.
- Accessing the encrypted database by using FbConnection, FbCommand, etc. works fine
- If the database is not encrypted and the encryption plugin is not configured in Firebird, Entity Framework as well as FbConnection works fine
- If we access the encrypted database using Entity Framework, we got the following exception (Unfortunately in german): Das Objekt des Typs "FirebirdSql.Data.Client.Managed.Version13.CryptKeyCallbackReponse" kann nicht in Typ "FirebirdSql.Data.Client.Managed.GenericResponse" umgewandelt werden. You can find the callstack of the exception at the end of the bug description.

I have debugged the issue with the latest sources on GitHub and was able to find the root cause (hopefully): The method GdsConnection.ProcessOperation returned an CryptKeyCallbackResponse which will be casted to GenericResponse in Version10.GdsDatabase.ReadGenericResponse and this fails.

After some short debugging sessions in order to find out what happened when I access the encrypted database directly (without Entity Framework), I came to the following conclusion:

Entity Framework invoked FbProviderServices.GetDbProviderManifestToken() with a connection which is not yet open. Because of the connection state the requested server version will be determined by using the FbServerProperties class (FbProviderServices Line 130). This class uses the FbService to communicate with the database. The FbService uses the GdsServiceManager and this class creates an instance of the Version10.GdsDatabase class which is used to communicate with the database. In my opinion, the instance of GdsDatabase should be created by using the appropriate factory method. Anyway, it seems that Version10.GdsDatabase is not able to communicate with encrypted databases. I came to the conclusion that encryption is only compatible with Version13. I made the following two changes in order to get working solution:

1. I changed the constructor of GdsServiceManager to create a Version13.GdsDatabase class.
2. In class Version13.GdsDatabase, I override ReadGenericResponse:
public override GenericResponse ReadGenericResponse()
{
byte[] cryptKey = new byte[2096];

		var response = ReadResponse\(\);
		while \(response is CryptKeyCallbackReponse cryptResponse\)
		\{
			XdrStream\.Write\(IscCodes\.op\_crypt\_key\_callback\);
			XdrStream\.WriteBuffer\(cryptKey\);
			XdrStream\.Flush\(\);
			response = ReadResponse\(\);
		\}

		return response as GenericResponse;
	\}

I know that this solution is just a hack but I don't know the architecture of the source code and I found no helpful source code comments. With the described changes, I was able to access the database.

I was wondering why no other user has reported a similar behavior.

I do my best to add all required information. If you need further information, feel free to contact me.

The callstack of the exception:

FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Client.Managed.Version10.GdsDatabase.ReadGenericResponse() Line 602
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Client\Managed\Version10\GdsDatabase.cs(602)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Client.Managed.Version10.GdsServiceManager.Attach(FirebirdSql.Data.Common.ServiceParameterBuffer spb, string dataSource, int port, string service) Line 73
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Client\Managed\Version10\GdsServiceManager.cs(73)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbService.Open() Line 114
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbService.cs(114)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbService.QueryService(byte[] items) Line 341
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbService.cs(341)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbService.ProcessQuery(byte[] items, System.Action<bool, object> queryResponseAction) Line 231
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbService.cs(231)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbService.Query(byte[] items, System.Action<bool, object> resultAction) Line 200
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbService.cs(200)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbService.Query(byte[] items) Line 159
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbService.cs(159)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbServerProperties.GetInfo(byte[] items) Line 90
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbServerProperties.cs(90)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbServerProperties.GetInfo(int item) Line 85
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbServerProperties.cs(85)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbServerProperties.GetString(int item) Line 75
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbServerProperties.cs(75)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbServerProperties.GetServerVersion() Line 40
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbServerProperties.cs(40)
EntityFramework.Firebird.dll!EntityFramework.Firebird.FbProviderServices.GetDbProviderManifestToken(System.Data.Common.DbConnection connection) Line 130
at C:\Users\michael.wresche\Source\Repos\FirebirdSql.Data.FirebirdClient\Provider\src\EntityFramework.Firebird\FbProviderServices.cs(130)
EntityFramework.dll!System.Data.Entity.Core.Common.DbProviderServices.GetProviderManifestToken(System.Data.Common.DbConnection connection)
EntityFramework.dll!System.Data.Entity.Utilities.DbProviderServicesExtensions.GetProviderManifestTokenChecked(System.Data.Entity.Core.Common.DbProviderServices providerServices, System.Data.Common.DbConnection connection)
mscorlib.dll!System.Collections.Concurrent.ConcurrentDictionary<System.Tuple<System.Type, string, string>, string>.GetOrAdd(System.Tuple<System.Type, string, string> key, System.Func<System.Tuple<System.Type, string, string>, string> valueFactory)
EntityFramework.dll!System.Data.Entity.Utilities.DbConnectionExtensions.GetProviderInfo(System.Data.Common.DbConnection connection, out System.Data.Entity.Core.Common.DbProviderManifest providerManifest)
EntityFramework.dll!System.Data.Entity.DbModelBuilder.Build(System.Data.Common.DbConnection providerConnection)
EntityFramework.dll!System.Data.Entity.Internal.LazyInternalContext.CreateModel(System.Data.Entity.Internal.LazyInternalContext internalContext)
EntityFramework.dll!System.Data.Entity.Internal.RetryLazy<System.Data.Entity.Internal.LazyInternalContext, System.Data.Entity.Infrastructure.DbCompiledModel>.GetValue(System.Data.Entity.Internal.LazyInternalContext input)
EntityFramework.dll!System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
EntityFramework.dll!System.Data.Entity.Internal.InternalContext.Initialize()
EntityFramework.dll!System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(System.Type entityType)
EntityFramework.dll!System.Data.Entity.Internal.Linq.InternalSet<OTTO.IBA.Entities.DBVersion>.Initialize()
EntityFramework.dll!System.Data.Entity.Internal.Linq.InternalSet<OTTO.IBA.Entities.DBVersion>.AsNoTracking()
EntityFramework.dll!System.Data.Entity.Infrastructure.DbQuery<OTTO.IBA.Entities.DBVersion>.AsNoTracking()
OTTO.IBA.Datenbankzugriff.EntityFramework.dll!OTTO.IBA.Datenbankzugriff.EntityFramework.DAOs.DBVersionDAO.GetAktuelleDBVersion() Line 45
at C:\Projekte\Otto_IBA2\Development\Sources\App\Datenbankzugriff\OTTO.IBA.Datenbankzugriff.EntityFramework\DAOs\DBVersionDAO.cs(45)

@firebird-automations
Copy link
Author

Modified by: @cincuranet

Link: This issue duplicates DNET821 [ DNET821 ]

@firebird-automations
Copy link
Author

Commented by: @cincuranet

Thanks for very detailed bug report. Thumbs up! This is already tracked in DNET821.

As a workaround you can hardcode the token using IManifestTokenResolver or by using DbProviderInfo and manually invoking DbModelBuilder.Build and using pre-compiled model, sadly none of these is really straightforward.

@firebird-automations
Copy link
Author

Modified by: @cincuranet

status: Open [ 1 ] => Closed [ 6 ]

resolution: Fixed [ 1 ]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants