Firebird ADO.NET Provider

Posted by Hugh Ang at 1/20/2007 11:58:00 AM

Recently I have installed open source database Firebird and its ADO.NET provider. I was really impressed and thought this would definitely be my choice of database for future personal projects. I did run into a few things though.





1) The sample on the web site mentioned that you can use the ADO.NET factory model as in the following:


 

            DbProviderFactory factory = DbProviderFactories.GetFactory("FirebirdSql.Data.FirebirdClient");

            using (DbConnection conn = factory.CreateConnection())

            {

                FbConnectionStringBuilder sb = new FbConnectionStringBuilder();

                // code omiited for brevity

            }



In order for this to work, you will need to modify the system.data section of the machine.config file:


 

  <system.data>

    <DbProviderFactories>

      <add name="Odbc Data Provider" invariant="System.Data.Odbc" description=".Net Framework Data Provider for Odbc" type="System.Data.Odbc.OdbcFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />

      <add name="OleDb Data Provider" invariant="System.Data.OleDb" description=".Net Framework Data Provider for OleDb" type="System.Data.OleDb.OleDbFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />

      <add name="OracleClient Data Provider" invariant="System.Data.OracleClient" description=".Net Framework Data Provider for Oracle" type="System.Data.OracleClient.OracleClientFactory, System.Data.OracleClient, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />

      <add name="SqlClient Data Provider" invariant="System.Data.SqlClient" description=".Net Framework Data Provider for SqlServer" type="System.Data.SqlClient.SqlClientFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />

      <add name="SQL Server CE Data Provider" invariant="Microsoft.SqlServerCe.Client" description=".NET Framework Data Provider for Microsoft SQL Server 2005 Mobile Edition" type="Microsoft.SqlServerCe.Client.SqlCeClientFactory, Microsoft.SqlServerCe.Client, Version=9.0.242.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" />

      <add name="Firebird Data Provider" invariant="FirebirdSql.Data.FirebirdClient" description="Firebird" type="FirebirdSql.Data.FirebirdClient.FirebirdClientFactory, FirebirdSql.Data.FirebirdClient, Version=2.0.1.0, Culture=neutral, PublicKeyToken=3750abcc3150b00c" />

    </DbProviderFactories>

  </system.data>





2) The Firebird 2.0 Release note says that security feature is enhanced with password hashing. I was interested in seeing how the ADO.NET client sends credentials to the server to get authenticated so I started investigation using three of my favorite tools: Reflector, Windbg and SOS (if I had had an additional machine I could have used network monitor or something similar to sniff the packets of course). With Reflector, it's not difficult to find that the FirebirdSql.Data.Client.Gds.GdsDatabase.Attach() is the last method on the call chain to send the parameters from connection string to the database server:


 

        public void Attach(DatabaseParameterBuffer dpb, string dataSource, int port, string database)

        {

            lock (this)

            {

                try

                {

                    this.connection.Connect(dataSource, port, this.packetSize, this.charset);

                    this.Identify(database);

                    this.Send.Write(0x13);

                    this.Send.Write(0);

                    this.Send.Write(database);

                    this.Send.WriteBuffer(dpb.ToArray());

                    this.Send.Flush();

                    try

                    {

                        GdsGenericResponse response1 = (GdsGenericResponse)this.ReadResponse();

                        this.handle = response1.ObjectHandle;

                    }

                    catch (IscException)

                    {

                        try

                        {

                            this.connection.Disconnect();

                        }

                        catch

                        {

                        }

                        throw;

                    }

                }

                catch (IOException)

                {

                    this.connection.Disconnect();

                    throw new IscException(0x14000197);

                }

                this.serverVersion = this.GetServerVersion();

            }





There doesn't seem to be hashing or encryption code so I started debugging. In Windbg, I set a break point at the Attach() function:


0:003> !bpmd FirebirdSql.Data.FirebirdClient.dll FirebirdSql.Data.Client.Gds.GdsDatabase.Attach
Found 1 methods...
MethodDesc = 00a28c90
Setting breakpoint: bp 00D62FA0 [FirebirdSql.Data.Client.Gds.GdsDatabase.Attach(FirebirdSql.Data.Common.DatabaseParameterBuffer, System.String, Int32, System.String)]


After the break point is hit, I print out the stack trace and the parameters passed:

0:000> !clrstack -p
OS Thread Id: 0xadc (0)
ESP EIP
0012f3c8 00d62fa0 FirebirdSql.Data.Client.Gds.GdsDatabase.Attach(FirebirdSql.Data.Common.DatabaseParameterBuffer, System.String, Int32, System.String)
PARAMETERS:
this = 0x013aa6c0
dpb = 0x013aa70c
dataSource = 0x013a9260
port = 0x00000bea
database = 0x013a906c

0012f3d8 00d61f8c FirebirdSql.Data.FirebirdClient.FbConnectionInternal.Connect()
PARAMETERS:
this = 0x013aa67c

0012f408 00d61c32 FirebirdSql.Data.FirebirdClient.FbConnection.Open()
PARAMETERS:
this = 0x013a02c0

0012f438 00d601d3 FbTest.Program.GetJobCode()
0012f478 00d600a9 FbTest.Program.Main(System.String[])
PARAMETERS:
args = 0x01381ccc

0012f69c 79e88f63 [GCFrame: 0012f69c]




Let's just focus on the paramaeters passed to the Attach function, starting from "database":

0:000> !do 0x013a906c
Name: System.String
MethodTable: 790fa3e0
EEClass: 790fa340
Size: 156(0x9c) bytes
(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: C:\Program Files\Firebird\Firebird_2_0\examples\empbuild\EMPLOYEE.FDB
Fields:
MT Field Offset Type VT Attr Value Name
790fed1c 4000096 4 System.Int32 0 instance 70 m_arrayLength
790fed1c 4000097 8 System.Int32 0 instance 69 m_stringLength
790fbefc 4000098 c System.Char 0 instance 43 m_firstChar
790fa3e0 4000099 10 System.String 0 shared static Empty
>> Domain:Value 0014eb80:790d6584 <<>> Domain:Value 0014eb80:013813dc <<




So that's just the database file path. "port" is an integer:

0:000> ?0x00000bea
Evaluate expression: 3050 = 00000bea




which shows that it uses tcp port number 3050 to communicate with the server. Next we move on to "dataSource":

0:000> !do 0x013a9260
Name: System.String
MethodTable: 790fa3e0
EEClass: 790fa340
Size: 36(0x24) bytes
(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: localhost
Fields:
MT Field Offset Type VT Attr Value Name
790fed1c 4000096 4 System.Int32 0 instance 10 m_arrayLength
790fed1c 4000097 8 System.Int32 0 instance 9 m_stringLength
790fbefc 4000098 c System.Char 0 instance 6c m_firstChar
790fa3e0 4000099 10 System.String 0 shared static Empty
>> Domain:Value 0014eb80:790d6584 <<>> Domain:Value 0014eb80:013813dc <<




which is server name "localhost". What's left now is the "dpb" parameter:

0:000> !do 013aa70c
Name: FirebirdSql.Data.Common.DatabaseParameterBuffer
MethodTable: 00a2835c
EEClass: 01078910
Size: 16(0x10) bytes
(C:\WINDOWS\assembly\GAC_MSIL\FirebirdSql.Data.FirebirdClient\2.0.1.0__3750abcc3150b00c\FirebirdSql.Data.FirebirdClient.dll)
Fields:
MT Field Offset Type VT Attr Value Name
791098d8 40000c1 4 ...m.IO.MemoryStream 0 instance 013aa71c stream
79104f64 40000c2 8 System.Boolean 0 instance 0 isLittleEndian




Keep dumping the "stream" object referenced by "dpb":


0:000> !do 013aa71c
Name: System.IO.MemoryStream
MethodTable: 791098d8
EEClass: 79109858
Size: 52(0x34) bytes
(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
Fields:
MT Field Offset Type VT Attr Value Name
790f9c18 4000184 4 System.Object 0 instance 00000000 __identity
79177788 4001b1d 8 ...ream+ReadDelegate 0 instance 00000000 _readDelegate
79177818 4001b1e c ...eam+WriteDelegate 0 instance 00000000 _writeDelegate
7910ccf4 4001b1f 10 ...ng.AutoResetEvent 0 instance 00000000 _asyncActiveEvent
790fed1c 4001b20 14 System.Int32 0 instance 1 _asyncActiveCount
790fe3c8 4001b1c 55c System.IO.Stream 0 shared static Null
>> Domain:Value 0014eb80:0139e07c <<
79124418 4001bc7 18 System.Byte[] 0 instance 013aa75c _buffer
790fed1c 4001bc8 1c System.Int32 0 instance 0 _origin
790fed1c 4001bc9 20 System.Int32 0 instance 44 _position
790fed1c 4001bca 24 System.Int32 0 instance 44 _length
790fed1c 4001bcb 28 System.Int32 0 instance 256 _capacity
79104f64 4001bcc 2c System.Boolean 0 instance 1 _expandable
79104f64 4001bcd 2d System.Boolean 0 instance 1 _writable
79104f64 4001bce 2e System.Boolean 0 instance 1 _exposable
79104f64 4001bcf 2f System.Boolean 0 instance 1 _isOpen




The "_buffer" should hold what's interesting to us:

0:000> db 013aa75c
013aa75c 18 44 12 79 00 01 00 00-01 3a 04 78 0a 00 00 3f .D.y.....:.x...?
013aa76c 04 03 00 00 00 30 04 4e-6f 6e 65 39 04 00 00 00 .....0.None9....
013aa77c 0f 1c 06 53 59 53 44 42-41 1d 09 6d 61 73 74 65 ...SYSDBA..maste
013aa78c 72 6b 65 79 00 00 00 00-00 00 00 00 00 00 00 00 rkey............
013aa79c 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
013aa7ac 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
013aa7bc 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
013aa7cc 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................



Sure enough, we can see that the user id and password are in the buffer. And password is not hashed.

4 comments:

Anonymous said...

AFAIK hashing password was inmproved for security DB, during creating user, etc. Not for the protocol. If you want to use secure connection you have to use some 3rd party SW.

JC

Anonymous said...

And just note for the first point. You don't have to modify machine.config. You can do this change in app.config too.

Hugh Ang said...

jiri, Thank you for the feedback. Regarding the config, I thought that since the Firebird provider is already installed into the GAC, it makes sense to include in the MSI the modification to the machine.config file so individual apps don't have to be worried about this in their app.config file.

Hugh

Anonymous said...
This comment has been removed by a blog administrator.