From 4da3997b49c8ee3e9196e315d26f90090edafff9 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Tue, 31 Jan 2017 17:38:53 +0300 Subject: [PATCH] Fixed CORE-5472: Problems with dbcrypt key transfer --- builds/install/misc/firebird.conf.in | 31 + doc/Using_OO_API.html | 5296 +++++++++-------- examples/dbcrypt/CryptApplication.cpp | 59 +- examples/dbcrypt/CryptKeyHolder.cpp | 9 +- examples/dbcrypt/DbCrypt.cpp | 2 + src/auth/AuthDbg.cpp | 3 + src/auth/AuthDbg.h | 1 + .../SecureRemotePassword/server/SrpServer.cpp | 15 +- src/auth/SecurityDatabase/LegacyServer.cpp | 1 + src/common/classes/GetPlugins.h | 2 +- src/common/classes/RefCounted.h | 6 +- src/common/config/config.cpp | 22 +- src/common/config/config.h | 15 +- src/common/db_alias.cpp | 6 +- src/common/db_alias.h | 2 +- src/common/security.cpp | 4 +- src/common/security.h | 4 +- src/include/fb_api_proto.h | 4 + src/include/firebird/FirebirdInterface.idl | 4 + src/include/firebird/IdlFbInterfaces.h | 64 +- src/jrd/CryptoManager.cpp | 142 +- src/jrd/CryptoManager.h | 28 +- src/jrd/Database.h | 2 +- src/jrd/event.cpp | 2 +- src/jrd/event_proto.h | 4 +- src/jrd/ext.cpp | 2 +- src/jrd/extds/IscDS.cpp | 30 +- src/jrd/extds/IscDS.h | 3 + src/jrd/extds/ValidatePassword.cpp | 2 +- src/jrd/jrd.cpp | 16 +- src/jrd/svc.cpp | 6 +- src/jrd/trace/TraceManager.cpp | 2 +- src/lock/lock.cpp | 4 +- src/lock/lock_proto.h | 6 +- src/remote/client/interface.cpp | 18 +- src/remote/inet.cpp | 129 +- src/remote/inet_proto.h | 6 +- src/remote/os/win32/wnet.cpp | 8 +- src/remote/os/win32/wnet_proto.h | 4 +- src/remote/os/win32/xnet.cpp | 10 +- src/remote/os/win32/xnet_proto.h | 4 +- src/remote/protocol.cpp | 4 +- src/remote/protocol.h | 5 + src/remote/remot_proto.h | 2 +- src/remote/remote.cpp | 13 +- src/remote/remote.h | 9 +- src/remote/server/os/posix/inet_server.cpp | 2 +- src/remote/server/server.cpp | 292 +- src/utilities/gsec/gsec.cpp | 6 +- src/yvalve/PluginManager.cpp | 4 +- src/yvalve/why.cpp | 4 +- 51 files changed, 3639 insertions(+), 2680 deletions(-) diff --git a/builds/install/misc/firebird.conf.in b/builds/install/misc/firebird.conf.in index 022d796e91..4f10d12f32 100644 --- a/builds/install/misc/firebird.conf.in +++ b/builds/install/misc/firebird.conf.in @@ -466,6 +466,37 @@ # #KeyHolderPlugin = +# ---------------------------- +# +# Ability to use encrypted security database +# +# If one relies on network encryption feature with crypt key generated +# by authentication plugin (like SRP does) to transfer database crypt +# keys over the wire then use of encrypted security databases is a kind of +# vicious circle. In order to send DB crypt key over the wire in secure way +# wire transfers should be already encrypted but this requires wire crypt key +# from authentication plugin which needs to open security database for hash +# validation which in turn requires DB crypt key. Luckily in most cases there +# is no big need to encrypt security database - it protects itself quite well +# if you use high quality passwords. But in some cases it's desired to have +# security database encrypted, for example if one wants to use self security +# database feature for encrypted database. In that case special care should be +# taken to encrypt that key before passing it to server using callback. Make +# sure your keys are well encrypted before enabling this parameter. Take into +# account that with CryptSecurityDatabase=TRUE unencrypted by firebird protocol +# key transfer may take place even with not encrypted security database. +# This feature is not supported by legacy authentication plugin - if you care +# about security please never use legacy authentication. +# +# Type: boolean +# +# Per-database configurable. +# +######################################################################### +# Please understand what are you doing before enabling this feature !!! # +######################################################################### +# +#CryptSecurityDatabase = false # ---------------------------- # diff --git a/doc/Using_OO_API.html b/doc/Using_OO_API.html index 02ae9c4b04..ce36b2e009 100644 --- a/doc/Using_OO_API.html +++ b/doc/Using_OO_API.html @@ -1,1723 +1,2018 @@ - - - - - - - - - - - - - - - -

-Firebird interfaces.

-

Firebird's OO API is based -on use of interfaces. That interfaces, though looking in some aspects -like OLE2 interfaces (some of them have addRef() and release() -methods) are non standard and have features, missing in other widely -used types of interfaces. First of all Firebird interfaces are -language independentthat + p { margin-bottom: 0.08in; color: #000000 } + h1 { color: #000000 } + + + +

+Firebird +interfaces.

+

Firebird's +OO API is based on use of interfaces. That interfaces, though looking +in some aspects like OLE2 interfaces (some of them have addRef() and +release() methods) are non standard and have features, missing in +other widely used types of interfaces. First of all Firebird +interfaces are language +independentthat means that to define/use them one need not use language specific -constructions like class in -C++, interface may be defined using any language having concepts of -array and pointer to procedure/function. Next interfaces are -versioned -– i.e. we support different versions of same -interface. Binary layout of interfaces is designed to support that -features very efficient (there is no need in additional virtual calls -like in OLE2/COM with it's QueryInterface) -but +constructions like class +in C++, interface may be +defined using any language having concepts of array and pointer to +procedure/function. Next interfaces are versioned +– i.e. we support different +versions of same interface. Binary layout of interfaces is designed +to support that features very efficient (there is no need in +additional virtual calls like in OLE2/COM with it's QueryInterface) +but it's not convenient for direct use from most languages. Therefore language-specific wrappers should better be designed for different languages making use of API easier. Currently we have wrappers for C++ and Pascal, Java is coming soon. From end-user POV calls from C++ and Pascal have absolutely no difference, though some additional language-specific features present in C++ (like ability to turn off -automatic status check after API calls) are missing in Pascal.

-


-

-

Typically +automatic status check after API calls) are missing in Pascal.

+


+ +

+

Typically database API is used to access data stored in database. Firebird OO API certainly performs this task but in addition it supports writing -your own pluginsmodules, -making it possible to enhance Firebird capabilities according to your -needs. Therefore this document contains 2 big parts – accessing -databases and writing plugins. Certainly some interfaces (like status -vector) are used in both parts of API, they will be discussed in data -access part and freely referenced later when discussing plugins. -Therefore even if you plan to write some plugin you should better -start with the first part of this document. Moreover a lot of plugins -need to access databases themselves and data access API is typically -needed for it.

-


-

-

Firebird +your own plugins +– modules, making it +possible to enhance Firebird capabilities according to your needs. +Therefore this document contains 2 big parts – accessing databases +and writing plugins. Certainly some interfaces (like status vector) +are used in both parts of API, they will be discussed in data access +part and freely referenced later when discussing plugins. Therefore +even if you plan to write some plugin you should better start with +the first part of this document. Moreover a lot of plugins need to +access databases themselves and data access API is typically needed +for it.

+


+ +

+

Firebird installation package contains a number of live samples of use of OO API – they are in examples/interfaces (database access) and -examples/dbcrypt (plugin performing fictitious -database encryption) +examples/dbcrypt (plugin performing fictitious +database encryption) directories. It's supposed that the reader is familiar with ISC API -used in Firebird since interbase times.

-


-

-

This +used in Firebird since interbase times.

+


+ +

+

This document does not pretend to be a full Firebird 3 documentation – it just describes new object oriented API, and a reader should be familiar with main Firebird concepts, knowledge about ISC API is also much welcome. For example – when describing how to work with services there is no explanation what is service and why is it -needed, only description of how to obtain IService +needed, only description of how to obtain IService interface and how to use it. Also pay attention that samples of code do not use a lot of powerful features of C++. Not used reference counted pointers, not used other RAII holders, not used templates (except one present in firebird public headers), etc. Primary goal is to make this text usable not only to C++ people because our API is oriented to support not only C++ but other, more simple languages -too.

-


-

-

-Accessing databases.

-

Creating database and attaching to existing -database.

-

First of all we need to -get access to IMaster interface. IMaster is primary Firebird -interface, required to access all the rest of interfaces. Therefore -there is a special way of accessing it – the only one needed to use -OO API plain function called fb_get_master_interface(). This function -has no parameters and always succeeds. There is one and only one -instance of IMaster per Firebird client library, therefore one need -not care about releasing memory, used by master interface. A simplest -way to access it from your program is to have appropriate global or -static variable:

-

static IMaster* -master = fb_get_master_interface();

-


-

-

For a lot of methods, used -in Firebird API, first parameter is IStatus interface. It's a -logical replacement of ISC_STATUS_ARRAY, but works separately with -errors and warnings (not mixing them in same array), can contain -unlimited number of errors inside and (this will be important if you -plan to implement IStatus yourself) always keeps strings, referenced -by it, inside interface. Typically you need at least one instance of -IStatus to call other methods. You obtain it from IMaster:

-

IStatus* -st = master->getStatus();

-

If method getStatus() -fails for some reason (OOM for example) it returns NULL – obviously -we can't use generic error reporting method which is based on use of -IStatus here.

-


-

-

Now we are going to deal -with first interface, directly related to database calls. This is -IProvider – interface called this way cause -it's exactly that interface that must be implemented by any provider -in Firebird. Firebird client library also has it's own implementation -of IProvider, which must be used to start any database activity. To -obtain it we call IMaster's method:

-

IProvider* prov = -master->getDispatcher();

-


-

-

When attaching to existing -database or moreover creating new one it's often necessary to pass a -lot of additional parameters (logon/password, page size for new -database, etc.) to API call. Having separate language-level -parameters is close to unreal – we will have to modify a call too -often to add new parameters, and number of them will be very big no -matter of the fact that typically one needs to pass not too much of -them. Therefore to pass additional parameters special in-memory data -structure, called database parameters block -(DPB) is used. Format of it is well defined, and it's -possible to build DPB byte after byte. But it's much easier to use -special interface IXpbBuilder, +too.

+


+ +

+

+Accessing +databases.

+

Creating database and +attaching to existing database.

+

First +of all we need to get access to IMaster interface. IMaster is +primary Firebird interface, required to access all the rest of +interfaces. Therefore there is a special way of accessing it – the +only one needed to use OO API plain function called +fb_get_master_interface(). This function has no parameters and always +succeeds. There is one and only one instance of IMaster per Firebird +client library, therefore one need not care about releasing memory, +used by master interface. A simplest way to access it from your +program is to have appropriate global or static variable:

+

static +IMaster* master = fb_get_master_interface();

+


+ +

+

For +a lot of methods, used in Firebird API, first parameter is IStatus +interface. It's a logical replacement of ISC_STATUS_ARRAY, but works +separately with errors and warnings (not mixing them in same array), +can contain unlimited number of errors inside and (this will be +important if you plan to implement IStatus yourself) always keeps +strings, referenced by it, inside interface. Typically you need at +least one instance of IStatus to call other methods. You obtain it +from IMaster:

+

IStatus* +st = master->getStatus();

+

If +method getStatus() fails for some reason (OOM for example) it returns +NULL – obviously we can't use generic error reporting method which +is based on use of IStatus here.

+


+ +

+

Now +we are going to deal with first interface, directly related to +database calls. This is IProvider – +interface called this way cause it's exactly that interface that must +be implemented by any provider in Firebird. Firebird client library +also has it's own implementation of IProvider, which must be used to +start any database activity. To obtain it we call IMaster's method:

+

IProvider* +prov = master->getDispatcher();

+


+ +

+

When +attaching to existing database or moreover creating new one it's +often necessary to pass a lot of additional parameters +(logon/password, page size for new database, etc.) to API call. +Having separate language-level parameters is close to unreal – we +will have to modify a call too often to add new parameters, and +number of them will be very big no matter of the fact that typically +one needs to pass not too much of them. Therefore to pass additional +parameters special in-memory data structure, called database +parameters block (DPB) +is used. Format of it is well defined, and it's possible to build DPB +byte after byte. But it's much easier to use special interface +IXpbBuilder, which simplifies creation of various parameters blocks. To obtain an instance of IXpbBuilder you must know one more generic-use interface -of firebird API – IUtil. +of firebird API – IUtil. It's a kind of placeholder for the calls that do not fit well in -other places. So we do

-

IUtil* utl = -master->getUtilInterface();

-

IXpbBuilder* dpb = -utl->getXpbBuilder(&status, IXpbBuilder::DPB, NULL, 0);

-

This creates empty -parameters' block builder of DPB type. Now adding required parameter -to it is trivial:

-

dpb->insertInt(&status, -isc_dpb_page_size, 4 * 1024);

-

will make firebird to -create new database with pagesize equal to 4Kb and meaning of

-

dpb->insertString(&status, -isc_dpb_user_name, “sysdba”);

-

dpb->insertString(&status, -isc_dpb_password, “masterkey”);

-

is (I hope) obvious.

-


-

-

The -following is C++ specific: We are almost ready to call +other places. So we do

+

IUtil* +utl = master->getUtilInterface();

+

IXpbBuilder* +dpb = utl->getXpbBuilder(&status, IXpbBuilder::DPB, NULL, 0);

+

This +creates empty parameters' block builder of DPB type. Now adding +required parameter to it is trivial:

+

dpb->insertInt(&status, +isc_dpb_page_size, 4 * 1024);

+

will +make firebird to create new database with pagesize equal to 4Kb and +meaning of

+

dpb->insertString(&status, +isc_dpb_user_name, “sysdba”);

+

dpb->insertString(&status, +isc_dpb_password, “masterkey”);

+

is +(I hope) obvious.

+


+ +

+

The +following is C++ specific: We are almost ready to call createDatabase() method of IProvider, but before it a few words about -concept of Status Wrapper should be said. Status wrapper is +concept of Status Wrapper should be said. Status wrapper is not an interface, it's very thin envelope for IStatus interface. It helps to customize behavior of C++ API (change a way how errors, returned in IStatus interface, are processed). For the first time we -recommend use of ThrowStatusWrapper, which raises C++ -exception each time an error is returned in IStatus.

-

ThrowStatusWrapper -status(st);

-


-

-

Now we may create new -empty database:

-

IAttachment* +recommend use of ThrowStatusWrapper, which raises C++ +exception each time an error is returned in IStatus.

+

ThrowStatusWrapper +status(st);

+


+ +

+

Now +we may create new empty database:

+

IAttachment* att = prov->createDatabase(&status, "fbtests.fdb", -dpb->getBufferLength(&status), dpb->getBuffer(&status));

-

printf("Database -fbtests.fdb created\n");

-

Pay attention that we do -not check status after the call to createDatabase(), because in case -of error C++ or Pascal exception will be raised (therefore it's very -good idea to have try/catch/except syntax in your program). We also -use two new functions from IXpbBuilder – getBufferLength() and -getBuffer(), which extract data from interface in native parameters -block format. As you can see there is no need to check explicitly for -status of functions, returning intermediate results.

-


-

-

Detaching from just -created database is trivial:

-

att->detach(&status);

-


-

-

Now it remains to enclose -all operators into try block and write a handler in catch -block. When using ThrowStatusWrapper you should always catch defined -in C++ API exception class FbException, in Pascal you must also work -with class FbException. Exception handler block in simplest case may -look this way:

-

catch (const -FbException& error)

-

{

-

char buf[256];

-

utl->formatStatus(buf, -sizeof(buf), error.getStatus());

-

fprintf(stderr, "%s\n", -buf);

-

}

-

Pay attention that here we -use one more function from IUtil – -formatStatus(). It returns in buffer text, describing an error -(warning), stored in IStatus parameter.

-


-

-

To attach to existing -database just use attachDatabase() method of IProvider instead -createDatabase(). All parameters are the same for both methods.

-

att = -prov->attachDatabase(&status, "fbtests.fdb", 0, -NULL);

-

This sample is using no -additional DPB parameters. Take into account that without -logon/password any remote connection will fail if no trusted +dpb->getBufferLength(&status), dpb->getBuffer(&status));

+

printf("Database +fbtests.fdb created\n");

+

Pay +attention that we do not check status after the call to +createDatabase(), because in case of error C++ or Pascal exception +will be raised (therefore it's very good idea to have +try/catch/except syntax in your program). We also use two new +functions from IXpbBuilder – getBufferLength() and getBuffer(), +which extract data from interface in native parameters block format. +As you can see there is no need to check explicitly for status of +functions, returning intermediate results.

+


+ +

+

Detaching +from just created database is trivial:

+

att->detach(&status);

+


+ +

+

Now +it remains to enclose all operators into try block and write a +handler in catch block. When using ThrowStatusWrapper you should +always catch defined in C++ API exception class FbException, in +Pascal you must also work with class FbException. Exception handler +block in simplest case may look this way:

+

catch +(const FbException& error)

+

{

+

char +buf[256];

+

utl->formatStatus(buf, +sizeof(buf), error.getStatus());

+

fprintf(stderr, +"%s\n", buf);

+

}

+

Pay +attention that here we use one more function from IUtil +– formatStatus(). It returns in buffer text, describing an error +(warning), stored in IStatus parameter.

+


+ +

+

To +attach to existing database just use attachDatabase() method of +IProvider instead createDatabase(). All parameters are the same for +both methods.

+

att += prov->attachDatabase(&status, "fbtests.fdb", 0, +NULL);

+

This +sample is using no additional DPB parameters. Take into account that +without logon/password any remote connection will fail if no trusted authorization plugin is configured. Certainly login info may be also provided in environment (in ISC_USER and ISC_PASSWORD variables) like -it was before.

-


-

-

Our examples contain -complete samples, dedicated except others to creating databases – -01.create.cpp and 01.create.pas. When samples are present it will be -very useful to build and try to run appropriate samples when reading -this document.

-


-

-

Working with transactions.

-

Only creating empty -databases is definitely not enough to work with RDBMS. We want to be -able to create various objects (like tables and so on) in database -and insert data in that tables. Any operation within database is -performed by firebird under transaction control. Therefore first of -all we must learn to start transaction. Here we do not discuss -distributed transactions (supported by IDtc +it was before.

+


+ +

+

Our +examples contain complete samples, dedicated except others to +creating databases – 01.create.cpp and 01.create.pas. When samples +are present it will be very useful to build and try to run +appropriate samples when reading this document.

+


+ +

+

Working with transactions.

+

Only +creating empty databases is definitely not enough to work with RDBMS. +We want to be able to create various objects (like tables and so on) +in database and insert data in that tables. Any operation within +database is performed by firebird under transaction control. +Therefore first of all we must learn to start transaction. Here we do +not discuss distributed transactions (supported by IDtc interface) to avoid unneeded to most users overcomplication. Starting of non-distributed transaction is very simple and done via attachment -interface:

-

ITransaction* -tra = att->startTransaction(&status, 0, NULL);

-

In this sample default -transaction parameters are used – no TPB is passed to -startTransaction() method. If you need non-default parameters you may -create appropriate IXpbBuilder, add -required items to it:

-

IXpbBuilder* -tpb = utl->getXpbBuilder(&status, IXpbBuilder::TPB, NULL, 0);

-

tpb->insertTag(&status, -isc_tpb_read_committed);

-

and pass resulting TPB to -startTransaction():

-

ITransaction* tra = -att->startTransaction(&status, tpb->getBufferLength(&status), -tpb->getBuffer(&status));

-


-

-

Transaction interface is -used as a parameter in a lot of other API calls but itself it does -not perform any actions except commit/rollback transaction, may be -retaining:

-

tra->commit(&status);

-


-

-

You may take a look at how -to start and commit transaction in examples 01.create.cpp and -01.create.pas.

-


-

-

Executing SQL operator without input parameters and -returned rows.

-

With started transaction -we are ready to execute our first SQL operators. Used for it -execute() method in IAttachment is rather -universal and may be also used to execute SQL operators with input -and output parameters (which is typical for EXECUTE PROCEDURE -statement), but right now we will use the simple most form of it. -Both DDL and DML operators can be executed:

-

att->execute(&status, +interface:

+

ITransaction* +tra = att->startTransaction(&status, 0, NULL);

+

In +this sample default transaction parameters are used – no TPB is +passed to startTransaction() method. If you need non-default +parameters you may create appropriate IXpbBuilder, +add required items to it:

+

IXpbBuilder* +tpb = utl->getXpbBuilder(&status, IXpbBuilder::TPB, NULL, 0);

+

tpb->insertTag(&status, +isc_tpb_read_committed);

+

and +pass resulting TPB to startTransaction():

+

ITransaction* +tra = att->startTransaction(&status, +tpb->getBufferLength(&status), tpb->getBuffer(&status));

+


+ +

+

Transaction +interface is used as a parameter in a lot of other API calls but +itself it does not perform any actions except commit/rollback +transaction, may be retaining:

+

tra->commit(&status);

+


+ +

+

You +may take a look at how to start and commit transaction in examples +01.create.cpp and 01.create.pas.

+


+ +

+

Executing SQL operator +without input parameters and returned rows.

+

With +started transaction we are ready to execute our first SQL operators. +Used for it execute() method in IAttachment +is rather universal and may be also used to execute SQL operators +with input and output parameters (which is typical for EXECUTE +PROCEDURE statement), but right now we will use the simple most form +of it. Both DDL and DML operators can be executed:

+

att->execute(&status, tra, 0, "create table dates_table (d1 date)", -SQL_DIALECT_V6, NULL, NULL, NULL, NULL);

-

tra->commitRetaining(&status);

-

att->execute(&status, +SQL_DIALECT_V6, NULL, NULL, NULL, NULL);

+

tra->commitRetaining(&status);

+

att->execute(&status, tra, 0, "insert into dates_table values (CURRENT_DATE)", -SQL_DIALECT_V6, NULL, NULL, NULL, NULL);

-

As you can see transaction -interface is a required parameter for execute() method (must be NULL -only if you execute START TRANSACTION statement). Next follows length -of SQL operator (may be zero causing use of C rules to determine -string length), text of operator and SQL dialect that should be used -for it. The following for NULLs stand for metadata descriptions and -buffers of input parameters and output data. Complete description of -this method is provided in IAttachment -interface.

-


-

-

You may take a look at how -to start and commit transaction in examples 01.create.cpp and -01.create.pas.

-


-

-

Executing SQL operator with input parameters.

-

There are 2 ways to -execute statement with input parameters. Choice of correct method -depends upon do you need to execute it more than once and do you know -in advance format of parameters. When that format is known and -statement is needed to be run only once single call to +SQL_DIALECT_V6, NULL, NULL, NULL, NULL);

+

As +you can see transaction interface is a required parameter for +execute() method (must be NULL only if you execute START TRANSACTION +statement). Next follows length of SQL operator (may be zero causing +use of C rules to determine string length), text of operator and SQL +dialect that should be used for it. The following for NULLs stand for +metadata descriptions and buffers of input parameters and output +data. Complete description of this method is provided in IAttachment +interface.

+


+ +

+

You +may take a look at how to start and commit transaction in examples +01.create.cpp and 01.create.pas.

+


+ +

+

Executing SQL operator +with input parameters.

+

There +are 2 ways to execute statement with input parameters. Choice of +correct method depends upon do you need to execute it more than once +and do you know in advance format of parameters. When that format is +known and statement is needed to be run only once single call to IAttachment::execute() may be used. In other cases SQL statement should be prepared first and after it executed, may be many times -with different parameters.

-


-

-

To prepare SQL statement -for execution use prepare() method of IAttachment -interface:

-

IStatement* +with different parameters.

+


+ +

+

To +prepare SQL statement for execution use prepare() method of +IAttachment interface:

+

IStatement* stmt = att->prepare(&status, tra, 0, “UPDATE department SET -budget = ? * budget + budget WHERE dept_no = ?”,

-

SQL_DIALECT_V6, -IStatement::PREPARE_PREFETCH_METADATA);

-

If you are not going to -use parameters description from firebird (i.e. you can provide that -information yourself) please use IStatement::PREPARE_PREFETCH_NONE -instead PREPARE_PREFETCH_METADATA – this will save client/server -traffic and server resources a bit.

-


-

-

In ISC API XSQLDA is used -to describe format of statement parameters. New API does not use -XSQLDA – instead interface IMessageMetadata is used. A set of input -parameters (and also a row fetched from cursor) is described in -firebird API in same way and later called message. IMessageMetadata -is passed as a parameter to the methods performing message exchange -between your program and database engine. There are many ways to have -an instance of IMessageMetadata – one can:

- -


-

-

Getting metadata from -prepared statement is very simple – method getInputMetadata() -return interface describing input message (i.e. statement -parameters), interface returned by getOutputMetadata() describes -output message (i.e. row in selected data or values returned by -procedure). In our case we can:

-

IMessageMetadata* -meta = stmt->getInputMetadata(&status);

-


-

-

Or we can build message -metadata ourself. First of all we need builder interface for it:

-

IMetadataBuilder* -builder = master->getMetadataBuilder(&status, 2);

-

Second parameter is -expected number of fields in the message, it can be changed later, -i.e. that's just an optimization.

-

Now it's necessary to set -individual fields characteristics in the builder. An absolute minimum -is field types and length for string fields:

-

builder->setType(&status, -0, SQL_DOUBLE + 1);

-

builder->setType(&status, -1, SQL_TEXT + 1);

-

builder->setLength(&status, -1, 3);

-

New -API is using old constants for SQL types, smallest bit as earlier -stands for nullability. In some case it may also make sense to set -sub-type (for blobs), character set (for text fields) or scale (for -numeric fields). And finally it's time to get an instance of -IMessageMetadata:

-

IMessageMetadata* -meta = builder->getMetadata(&status);

-


-

-

Here -we do not discuss in details own implementation of IMessageMetadata. -If one cares there is a sample 05.user_metadata.cpp.

-

So finally we have -obtained (one or another way) an instance of metadata description of -input parameters. But to work with a message we also need buffer for -it. Buffer size is one of main message metadata characteristics and -it's returned by method in IMessageMetadata:

-

char* buffer = new -char[meta->getMessageLength(&status)];

-

To deal with individual -values inside buffer offset to them should be taken into an account. -IMessageMetadata is aware of offsets for all values in a message, -using it we can create pointers to them:

-

double* percent_inc = -(double*) &buffer[meta->getOffset(&status, 0)];

-

char* dept_no = -&buffer[meta->getOffset(&status, 1)];

-

Also let's do not forget -to set to NOT NULL null flags:

-

short* flag = -(short*)&buffer[meta->getNullOffset(&status, 0)];

-

*flag = 0;

-

flag = (short*) -&buffer[meta->getNullOffset(&status, 1)];

-

*flag = 0;

-


-

-

After finishing with -offsets we are ready to execute statement with some parameters -values:

-

getInputValues(dept_no, -percent_inc);

-

and may execute prepared -statement:

-

stmt->execute(&status, -tra, meta, buffer, NULL, NULL);

-

Two more NULLs in the end -of parameters stand for output message and is used typically for -EXECUTE PROCEDURE statement.

-


-

-

If you do not need to get -metadata from statement and plan to execute it only once you may -choose a simpler way – use method execute() in IAttachment -interface:

-

att->execute(&status, +budget = ? * budget + budget WHERE dept_no = ?”,

+

SQL_DIALECT_V6, +IStatement::PREPARE_PREFETCH_METADATA);

+

If +you are not going to use parameters description from firebird (i.e. +you can provide that information yourself) please use +IStatement::PREPARE_PREFETCH_NONE instead PREPARE_PREFETCH_METADATA – +this will save client/server traffic and server resources a bit.

+


+ +

+

In +ISC API XSQLDA is used to describe format of statement parameters. +New API does not use XSQLDA – instead interface IMessageMetadata is +used. A set of input parameters (and also a row fetched from cursor) +is described in firebird API in same way and later called message. +IMessageMetadata is passed as a +parameter to the methods performing message exchange between your +program and database engine. There are many ways to have an instance +of IMessageMetadata – one can:

+ +


+ +

+

Getting +metadata from prepared statement is very simple – method +getInputMetadata() return interface describing input message (i.e. +statement parameters), interface returned by getOutputMetadata() +describes output message (i.e. row in selected data or values +returned by procedure). In our case we can:

+

IMessageMetadata* +meta = stmt->getInputMetadata(&status);

+


+ +

+

Or +we can build message metadata ourself. First of all we need builder +interface for it:

+

IMetadataBuilder* +builder = master->getMetadataBuilder(&status, 2);

+

Second +parameter is expected number of fields in the message, it can be +changed later, i.e. that's just an optimization.

+

Now +it's necessary to set individual fields characteristics in the +builder. An absolute minimum is field types and length for string +fields:

+

builder->setType(&status, +0, SQL_DOUBLE + 1);

+

builder->setType(&status, +1, SQL_TEXT + 1);

+

builder->setLength(&status, +1, 3);

+

+New API is using old constants +for SQL types, smallest bit as earlier stands for nullability. In +some case it may also make sense to set sub-type (for blobs), +character set (for text fields) or scale (for numeric fields). And +finally it's time to get an instance of IMessageMetadata:

+

IMessageMetadata* +meta = builder->getMetadata(&status);

+


+ +

+

+Here we do not discuss in +details own implementation of IMessageMetadata. If one cares there is +a sample 05.user_metadata.cpp.

+

So +finally we have obtained (one or another way) an instance of metadata +description of input parameters. But to work with a message we also +need buffer for it. Buffer size is one of main message metadata +characteristics and it's returned by method in IMessageMetadata:

+

char* +buffer = new char[meta->getMessageLength(&status)];

+

To +deal with individual values inside buffer offset to them should be +taken into an account. IMessageMetadata is aware of offsets for all +values in a message, using it we can create pointers to them:

+

double* +percent_inc = (double*) &buffer[meta->getOffset(&status, +0)];

+

char* +dept_no = &buffer[meta->getOffset(&status, 1)];

+

Also +let's do not forget to set to NOT NULL null flags:

+

short* +flag = (short*)&buffer[meta->getNullOffset(&status, 0)];

+

*flag += 0;

+

flag += (short*) &buffer[meta->getNullOffset(&status, 1)];

+

*flag += 0;

+


+ +

+

After +finishing with offsets we are ready to execute statement with some +parameters values:

+

getInputValues(dept_no, +percent_inc);

+

and +may execute prepared statement:

+

stmt->execute(&status, +tra, meta, buffer, NULL, NULL);

+

Two +more NULLs in the end of parameters stand for output message and is +used typically for EXECUTE PROCEDURE statement.

+


+ +

+

If +you do not need to get metadata from statement and plan to execute it +only once you may choose a simpler way – use method execute() in +IAttachment interface:

+

att->execute(&status, tra, 0, "UPDATE department SET budget = ? * budget + budget -WHERE dept_no = ?", SQL_DIALECT_V6, meta, buffer, NULL, NULL);

-

In that case you do not -need to use IStatement at all.

-


-

-

An example how to execute -UPDATE with parameters is present in 02.update.cpp, you will also see -how raised in trigger/procedure exception may be caught by C++ -program.

-


-

-

Opening cursor and fetching data from it.

-

The only way to get rows -of data, returned by SELECT operator, in OO API is to use IResultSet -interface. This interface is returned by openCursor() method in both -IAttachment and IStatement. openCursor() is in most aspects alike -execute() and a choice how to open cursor (using prepared statement -or directly from attachment interface) is the same. In the samples -03.select.cpp and 04.print_table.cpp both methods are used. Let's pay -attention at one specific openCursor() feature compared with -execute() - one does not pass buffer for output message into -openCursor(), it will be passed later when data is fetched from -cursor. This makes it possible to open cursor with unknown format of -output message (NULL is passed instead output metadata). Firebird is -using in this case default message format which may be requested from -IResultSet interface:

-

const char* sql = -"select * from ..."; // some select statement

-


-

-

IResultSet* +WHERE dept_no = ?", SQL_DIALECT_V6, meta, buffer, NULL, NULL);

+

In +that case you do not need to use IStatement +at all.

+


+ +

+

An +example how to execute UPDATE with parameters is present in +02.update.cpp, you will also see how raised in trigger/procedure +exception may be caught by C++ program.

+


+ +

+

Opening cursor and +fetching data from it.

+

The +only way to get rows of data, returned by SELECT operator, in OO API +is to use IResultSet interface. This +interface is returned by openCursor() method in both IAttachment and +IStatement. openCursor() is in most aspects alike execute() and a +choice how to open cursor (using prepared statement or directly from +attachment interface) is the same. In the samples 03.select.cpp and +04.print_table.cpp both methods are used. Let's pay attention at one +specific openCursor() feature compared with execute() - one does not +pass buffer for output message into openCursor(), it will be passed +later when data is fetched from cursor. This makes it possible to +open cursor with unknown format of output message (NULL is passed +instead output metadata). Firebird is using in this case default +message format which may be requested from IResultSet interface:

+

const +char* sql = "select * from ..."; // some select statement

+


+ +

+

IResultSet* curs = att->openCursor(&status, tra, 0, sql, SQL_DIALECT_V6, -NULL, NULL, NULL, NULL, 0);

-

IMessageMetadata* -meta = curs->getMetadata(&status);

-

Later this metadata may be -used to allocate buffer for data and parse fetched rows.

-


-

-

As an alternative one can -first prepare statement, get metadata from prepared statement and -after it open cursor. This is preferred way if cursor is likely to be -opened >1 times.

-

IStatement* +NULL, NULL, NULL, NULL, 0);

+

IMessageMetadata* +meta = curs->getMetadata(&status);

+

Later +this metadata may be used to allocate buffer for data and parse +fetched rows.

+


+ +

+

As +an alternative one can first prepare statement, get metadata from +prepared statement and after it open cursor. This is preferred way if +cursor is likely to be opened >1 times.

+

IStatement* stmt = att->prepare(&status, tra, 0, sql, SQL_DIALECT_V6, -Istatement::PREPARE_PREFETCH_METADATA);

-

IMessageMetadata* -meta = stmt->getOutputMetadata(&status);

-

IResultSet* -curs = stmt->openCursor(&status, tra, NULL, NULL, NULL, 0);

-


-

-

We have obtained (one or -another way) an instance of metadata description of output fields (a -row in a set). To work with a message we also need buffer for it:

-

unsigned char* buffer = -new unsigned char[meta->getMessageLength(&status)];

-


-

-

IResultSet has a lot of -various fetch methods but when cursor is not opened with SCROLL -option only fetchNext() works, i.e. one can navigate only forward -record by record. In addition to errors and warnings in status -fetchNext() returns completion code, which may have values RESULT_OK -(when buffer is filled with values for next row) or RESULT_NO_DATA -(when no more rows left in cursor). RESULT_NO_DATA is not error -state, it's normal state after completion of the method, but we know -that data in cursor is over. If status wrapper, not throwing -exception in case of error return is used, one more value – +Istatement::PREPARE_PREFETCH_METADATA);

+

IMessageMetadata* +meta = stmt->getOutputMetadata(&status);

+

IResultSet* +curs = stmt->openCursor(&status, tra, NULL, NULL, NULL, 0);

+


+ +

+

We +have obtained (one or another way) an instance of metadata +description of output fields (a row in a set). To work with a message +we also need buffer for it:

+

unsigned +char* buffer = new unsigned char[meta->getMessageLength(&status)];

+


+ +

+

IResultSet +has a lot of various fetch methods but when cursor is not opened with +SCROLL option only fetchNext() works, i.e. one can navigate only +forward record by record. In addition to errors and warnings in +status fetchNext() returns completion code, which may have values +RESULT_OK (when buffer is filled with values for next row) or +RESULT_NO_DATA (when no more rows left in cursor). RESULT_NO_DATA is +not error state, it's normal state after completion of the method, +but we know that data in cursor is over. If status wrapper, not +throwing exception in case of error return is used, one more value – RESULT_ERROR – may be returned, that means no data in buffer and error vector in status. Method fetchNext() is usually called in a -cycle:

-

while -(curs->fetchNext(&status, buffer) == IStatus::RESULT_OK)

-

{

-

// row processing

-

}

-


-

-

What is done during row -processing depends upon your needs. To access particular field -field's offset should be used:

-

unsigned char* -field_N_ptr = buffer + meta->getOffset(&status, n);

-

where -n is the number of a field in a message. That pointer should be -casted to appropriate type, depending upon field type. For example, -for a VARCHAR field cast to struct vary should be used:

-

vary* v_ptr = (vary*) -(buffer + meta->getOffset(&status, n));

-

Now we may print the value -of a field:

-

printf(“field %s -value is %*.*s\n”, meta->getField(&status, n), -v_ptr->vary_length, v_ptr->vary_length, v_ptr->vary_string);

-


-

-

If you need maximum -performance it will be good idea to cache needed metadata values like -it's done in our samples 03.select.cpp and 04.print_table.cpp.

-


-

-

Using FB_MESSAGE macro for static messages.

-

Working with data using -offsets is rather efficient but requires a lot of code to be written. -In C++ this problem can be solved using templates, but even compared -with them the most convenient way to work with the message is to -represent it in native (for given language) way – structure in -C/C++, record in Pascal, etc. Certainly this works only if format of -a message is known in advance. To help building such structures in -C++ firebird contains special macro FB_MESSAGE.

-


-

-

FB_MESSAGE has 3 arguments -– message (struct) name, type of status wrapper and list of fields. -Usage of first and second is obvious, list of fields contains pairs -(field_type, field_name), where field_type is one of the following:

-

FB_BIGINT

-

FB_BLOB

-

FB_BOOLEAN

-

FB_CHAR(len)

-

FB_DATE

-

FB_DOUBLE

-

FB_FLOAT

-

FB_INTEGER

-

FB_INTL_CHAR(len, charSet)

-

FB_INTL_VARCHAR(len, -charSet)

-

FB_SCALED_BIGINT(x)

-

FB_SCALED_INTEGER(x)

-

FB_SCALED_SMALLINT(x)

-

FB_SMALLINT

-

FB_TIME

-

FB_TIMESTAMP

-

FB_VARCHAR(len)

-

In generated by -preprocessor structure integer and float types are matched with -appropriate C types, date and time – with classes FbDate -and FbTime (all mentioned here classes are in -namespace Firebird), timestamp – with class FbTimestamp, containing -two public data members date and time of appropriate class, char - -with struct FbChar and varchar – with struct -FbVarChar. For each field preprocessor -creates two data members in the message – name -for field/parameter value and nameNull -for NULL indicator. Message constructor has 2 parameters -– pointer to status wrapper and master interface:

-

FB_MESSAGE(Output, -ThrowStatusWrapper,

-

(FB_SMALLINT, -relationId)

-

(FB_CHAR(31), -relationName)

-

(FB_VARCHAR(100), -description)

-

) output(&status, -master);

-


-

-

For static messages use of -FB_MESSAGE is sooner of all the best choice – they can be at the -same time easily passed to execute, openCursor and fetch methods:

-

rs = -att->openCursor(&status, tra, 0, sqlText,

-

SQL_DIALECT_V6, NULL, -NULL, output.getMetadata(), NULL, 0);

-

and used to work with -values of individual fields:

-

while +cycle:

+

while +(curs->fetchNext(&status, buffer) == IStatus::RESULT_OK)

+

{

+

// +row processing

+

}

+


+ +

+

What +is done during row processing depends upon your needs. To access +particular field field's offset should be used:

+

unsigned +char* field_N_ptr = buffer + meta->getOffset(&status, n);

+

+where n is the number of a +field in a message. That pointer should be casted to appropriate +type, depending upon field type. For example, for a VARCHAR field +cast to struct vary should be used:

+

vary* +v_ptr = (vary*) (buffer + meta->getOffset(&status, n));

+

Now +we may print the value of a field:

+

printf(“field +%s value is %*.*s\n”, meta->getField(&status, n), +v_ptr->vary_length, v_ptr->vary_length, v_ptr->vary_string);

+


+ +

+

If +you need maximum performance it will be good idea to cache needed +metadata values like it's done in our samples 03.select.cpp and +04.print_table.cpp.

+


+ +

+

Using FB_MESSAGE macro for +static messages.

+

Working +with data using offsets is rather efficient but requires a lot of +code to be written. In C++ this problem can be solved using +templates, but even compared with them the most convenient way to +work with the message is to represent it in native (for given +language) way – structure in C/C++, record in Pascal, etc. +Certainly this works only if format of a message is known in advance. +To help building such structures in C++ firebird contains special +macro FB_MESSAGE.

+


+ +

+

FB_MESSAGE +has 3 arguments – message (struct) name, type of status wrapper and +list of fields. Usage of first and second is obvious, list of fields +contains pairs (field_type, field_name), where field_type is one of +the following:

+

FB_BIGINT

+

FB_BLOB

+

FB_BOOLEAN

+

FB_CHAR(len)

+

FB_DATE

+

FB_DOUBLE

+

FB_FLOAT

+

FB_INTEGER

+

FB_INTL_CHAR(len, +charSet)

+

FB_INTL_VARCHAR(len, +charSet)

+

FB_SCALED_BIGINT(x)

+

FB_SCALED_INTEGER(x)

+

FB_SCALED_SMALLINT(x)

+

FB_SMALLINT

+

FB_TIME

+

FB_TIMESTAMP

+

FB_VARCHAR(len)

+

In +generated by preprocessor structure integer and float types are +matched with appropriate C types, date and time – with classes +FbDate and FbTime (all +mentioned here classes are in namespace Firebird), timestamp – with +class FbTimestamp, containing two public data members date and time +of appropriate class, char - with struct FbChar +and varchar – with struct FbVarChar. For +each field preprocessor creates two data members in the message – +name for +field/parameter value and nameNull +for NULL indicator. Message +constructor has 2 parameters – pointer to status wrapper and master +interface:

+

FB_MESSAGE(Output, +ThrowStatusWrapper,

+

(FB_SMALLINT, +relationId)

+

(FB_CHAR(31), +relationName)

+

(FB_VARCHAR(100), +description)

+

) +output(&status, master);

+


+ +

+

For +static messages use of FB_MESSAGE is sooner of all the best choice – +they can be at the same time easily passed to execute, openCursor and +fetch methods:

+

rs += att->openCursor(&status, tra, 0, sqlText,

+

SQL_DIALECT_V6, +NULL, NULL, output.getMetadata(), NULL, 0);

+

and +used to work with values of individual fields:

+

while (rs->fetchNext(&status, output.getData()) == -IStatus::RESULT_OK)

-

{

-

printf("%4d +IStatus::RESULT_OK)

+

{

+

printf("%4d %31.31s %*.*s\n", output->relationId, -output->relationName.str,

-

output->descriptionNull -? 0 : output->description.length,

-

output->descriptionNull -? 0 : output->description.length, output->description.str);

-

}

-


-

-

An example of using macro -FB_MESSAGE to work with messages is in the sample 06.fb_message.cpp.

-


-

-

Working with blobs.

-

For blobs in message -buffer firebird stores blob identifier – an 8-byte entity which -should be aligned on 4-byte boundary. Identifier has ISC_QUAD type. -Interface IAttachment has 2 methods to work -with blobs – openBlob() and createBlob(), both returning interface -IBlob and having same set of parameters, but -performing somewhat contrary actions: openBlob() takes blob -identifier from the message and prepares blob for reading but -createBlob() creates new blob, puts it's identifier into message and -prepares blob for writing. -

-


-

-

To work with blobs one -must first of all include blob identifier into the message. If you -get metadata from firebird engine field of appropriate type will be -already present. You just use it's offset (assuming variable -blobFieldNumber contains number of blob field) (and appropriate null -offset to check for nulls or set null flag) to obtain pointer into -the message buffer:

-

ISC_QUAD* blobPtr = -(ISC_QUAD*) &buffer[metadata->getOffset(&status, -blobFieldNumber)];

-

ISC_SHORT* blobNullPtr -= (ISC_SHORT*) &buffer[metadata->getNullOffset(&status, -blobFieldNumber)];

-


-

-

If you use static messages -and FB_MESSAGE macro blob field is declared as having FB_BLOB type:

-

FB_MESSAGE(Msg, -ThrowStatusWrapper,

-

(FB_BLOB, b)

-

) message(&status, -master);

-

ISC_QUAD* blobPtr = -&message->b;

-

ISC_SHORT* blobNullPtr -= &message->bNull;

-


-

-

To create new blob invoke -createBlob() method:

-

IBlob* -blob = att->createBlob(status, tra, blobPtr, 0, NULL);

-

Last two parameters are -required only if you want to use blob filters or use stream blob, -that's out of scope here.

-

Blob interface is ready to -accept data into blob. Use putSegment() method to send data to -engine:

-

void* segmentData;

-

unsigned segmentLength;

-

while -(userFunctionProvidingBlobData(&segmentData, &segmentLength))

-

blob->putSegment(&status, -segmentLength, segmentData);

-

After sending some data to -blob do not forget to close blob interface:

-

blob->close(&status);

-

Make sure that null flag -is not set (not required if you nullified all message buffer before -creating blob):

-

*blobNullPtr = 0;

-

and message, containing -blob, may be used in insert or update statement. After execution of -that statement new blob will be stored in database.

-


-

-

To read a blob begin with -getting containing it's identifier message from firebird engine. This -may be done using fetch() or execute() methods. After it use -openBlob() attachment's method:

-

IBlob* -blob = att->openBlob(status, tra, blobPtr, 0, NULL);

-

Blob interface is ready to -provide blob data. Use getSegment() method to receive data from -engine:

-

char buffer[BUFSIZE];

-

unsigned actualLength;

-

for(;;)

-

{

-

switch +output->relationName.str,

+

output->descriptionNull +? 0 : output->description.length,

+

output->descriptionNull +? 0 : output->description.length, output->description.str);

+

}

+


+ +

+

An +example of using macro FB_MESSAGE to work with messages is in the +sample 06.fb_message.cpp.

+


+ +

+

Working with blobs.

+

For +blobs in message buffer firebird stores blob identifier – an 8-byte +entity which should be aligned on 4-byte boundary. Identifier has +ISC_QUAD type. Interface IAttachment has 2 +methods to work with blobs – openBlob() and createBlob(), both +returning interface IBlob and having same set of +parameters, but performing somewhat contrary actions: openBlob() +takes blob identifier from the message and prepares blob for reading +but createBlob() creates new blob, puts it's identifier into message +and prepares blob for writing. +

+


+ +

+

To +work with blobs one must first of all include blob identifier into +the message. If you get metadata from firebird engine field of +appropriate type will be already present. You just use it's offset +(assuming variable blobFieldNumber contains number of blob field) +(and appropriate null offset to check for nulls or set null flag) to +obtain pointer into the message buffer:

+

ISC_QUAD* +blobPtr = (ISC_QUAD*) &buffer[metadata->getOffset(&status, +blobFieldNumber)];

+

ISC_SHORT* +blobNullPtr = (ISC_SHORT*) &buffer[metadata->getNullOffset(&status, +blobFieldNumber)];

+


+ +

+

If +you use static messages and FB_MESSAGE macro blob field is declared +as having FB_BLOB type:

+

FB_MESSAGE(Msg, +ThrowStatusWrapper,

+

(FB_BLOB, +b)

+

) +message(&status, master);

+

ISC_QUAD* +blobPtr = &message->b;

+

ISC_SHORT* +blobNullPtr = &message->bNull;

+


+ +

+

To +create new blob invoke createBlob() method:

+

IBlob* +blob = att->createBlob(status, tra, blobPtr, 0, NULL);

+

Last +two parameters are required only if you want to use blob filters or +use stream blob, that's out of scope here.

+

Blob +interface is ready to accept data into blob. Use putSegment() method +to send data to engine:

+

void* +segmentData;

+

unsigned +segmentLength;

+

while +(userFunctionProvidingBlobData(&segmentData, &segmentLength))

+

blob->putSegment(&status, +segmentLength, segmentData);

+

After +sending some data to blob do not forget to close blob interface:

+

blob->close(&status);

+

Make +sure that null flag is not set (not required if you nullified all +message buffer before creating blob):

+

*blobNullPtr += 0;

+

and +message, containing blob, may be used in insert or update statement. +After execution of that statement new blob will be stored in +database.

+


+ +

+

To +read a blob begin with getting containing it's identifier message +from firebird engine. This may be done using fetch() or execute() +methods. After it use openBlob() attachment's method:

+

IBlob* +blob = att->openBlob(status, tra, blobPtr, 0, NULL);

+

Blob +interface is ready to provide blob data. Use getSegment() method to +receive data from engine:

+

char +buffer[BUFSIZE];

+

unsigned +actualLength;

+

for(;;)

+

{

+

switch (blob->getSegment(&status, sizeof(buffer), buffer, -&actualLength))

-

{

-

case -IStatus::RESULT_OK:

-

userFunctionAcceptingBlobData(buffer, -actualLength, true);

-

continue;

-

case -IStatus::RESULT_SEGMENT:

-

userFunctionAcceptingBlobData(buffer, -actualLength, false);

-

continue;

-

default:

-

break;

-

}

-

}

-

Last parameter in -userFunctionAcceptingBlobData() is a flag that end of segment is -reached – when getSegment() returns RESULT_SEGMENT completion code -that function is notified (by passing false as last parameter) that -segment was not read completely and continuation is expected at next -call.

-

After finishing with blob -do not forget top close it:

-

blob->close(&status);

-


-

-

Working with events.

-

Events interface was not -completed in FB3, we expect to have something more interesting in -next version. The minimum existing support is as follows: IAttachment -contains call queEvents() which performs almost same functions as -isc_que_events() call. Instead the pair of parameters -FPTR_EVENT_CALLBACK ast and -void* arg, required to +&actualLength))

+

{

+

case +IStatus::RESULT_OK:

+

userFunctionAcceptingBlobData(buffer, +actualLength, true);

+

continue;

+

case +IStatus::RESULT_SEGMENT:

+

userFunctionAcceptingBlobData(buffer, +actualLength, false);

+

continue;

+

default:

+

break;

+

}

+

}

+

Last +parameter in userFunctionAcceptingBlobData() is a flag that end of +segment is reached – when getSegment() returns RESULT_SEGMENT +completion code that function is notified (by passing false as last +parameter) that segment was not read completely and continuation is +expected at next call.

+

After +finishing with blob do not forget top close it:

+

blob->close(&status);

+


+ +

+

Working with events.

+

Events +interface was not completed in FB3, we expect to have something more +interesting in next version. The minimum existing support is as +follows: IAttachment contains call +queEvents() which performs almost same functions as isc_que_events() +call. Instead the pair of parameters FPTR_EVENT_CALLBACK +ast and void* +arg, required to invoke user code when event happens in firebird engine, callback interface IEventCallback is used. This is traditional approach which helps to avoid non-safe casts from void* in user function. Another important difference is that instead event identifier (a kind of -handler) this function returns reference counted interface IEvents +handler) this function returns reference counted interface IEvents having method cancel() used when waiting for event should be stopped. Unlike identifier which is automatically destroyed when event arrives interface can not be automatically destroyed – in case when event is received right before canceling interface call to cancel() would cause segfault when interface is already destroyed. Therefore -interface IEvents must be explicitly released +interface IEvents must be explicitly released after receiving an event. This may be done for example right before -queuing for an event next time:

-

events->release();

-

events = NULL;

-

events = -attachment->queEvents(&status, this, eveLen, eveBuffer);

-

Setting interface pointer -to NULL is useful in case of exception during queEvents. In other -aspects events handling did not change compared with ISC API. Please -use for additional details our sample 08.events.cpp. -

-


-

-

Using services.

-

To begin to use services -one should first of all connect to service manager. This is done -using attachServiceManager() method of IProvider. -This method returns IService interface which -is used later to talk to service. To prepare SPB to attach to service -manager one can use IXpbBuilder:

-

IXpbBuilder* spb1 = -utl->getXpbBuilder(&status, IXpbBuilder::SPB_ATTACH, NULL, 0);

-

spb1->insertString(&status, -isc_spb_user_name, “sysdba”);

-

spb1->insertString(&status, -isc_spb_password, “masterkey”);

-

and proceed with attach:

-

IService* +queuing for an event next time:

+

events->release();

+

events += NULL;

+

events += attachment->queEvents(&status, this, eveLen, eveBuffer);

+

Setting +interface pointer to NULL is useful in case of exception during +queEvents. In other aspects events handling did not change compared +with ISC API. Please use for additional details our sample +08.events.cpp. +

+


+ +

+

Using services.

+

To +begin to use services one should first of all connect to service +manager. This is done using attachServiceManager() method of +IProvider. This method returns IService +interface which is used later to talk to service. To prepare SPB to +attach to service manager one can use IXpbBuilder:

+

IXpbBuilder* +spb1 = utl->getXpbBuilder(&status, IXpbBuilder::SPB_ATTACH, +NULL, 0);

+

spb1->insertString(&status, +isc_spb_user_name, “sysdba”);

+

spb1->insertString(&status, +isc_spb_password, “masterkey”);

+

and +proceed with attach:

+

IService* svc = prov->attachServiceManager(&status, “service_mgr”, -spb1->getBufferLength(&status), spb1->getBuffer(&status));

-


-

-

Using IService one can -perform both available for services actions – start services and -query various information about started utilities and server in -general. When querying information one limitation takes place – -formats of parameter blocks, used by query() method, in Firebird 3 -are not supported by IXpbBuilder. Support will be probably added in -later versions, in Firebird 3 you will have to build and analyze that -blocks manually. Format of that blocks matches old format (used in -ISC API) one to one. -

-


-

-

To start service one -should first of all create appropriate SPB:

-

IXpbBuilder* spb2 = -utl->getXpbBuilder(&status, IXpbBuilder::SPB_START, NULL, 0);

-

and add required items to -it. For example, to print encryption statistics for database employee -the following should be placed into SPB:

-

spb2->insertTag(&status, -isc_action_svc_db_stats);

-

spb2->insertString(&status, -isc_spb_dbname, "employee");

-

spb2->insertInt(&status, -isc_spb_options, isc_spb_sts_encryption);

-

After it service can be -started using start() method of IService -interface:

-

svc->start(&status, -spb2->getBufferLength(&status), spb2->getBuffer(&status));

-


-

-

Many started services -(including mentioned here gstat) return text information during -execution. To display it one should query started service anout that -information line by line. This is done by calling query() method of -IService interface with appropriate send and -receive blocks of parameters. Send block may contain various helper -information (like timeout when querying service) or information to be -passed to stdin of service utility or may be empty in the simplest -case. Receive block must contain list of tags you want to receive -from service. For most of utilities this is single isc_info_svc_line:

-

const unsigned char -receiveItems1[] = {isc_info_svc_line};

-

To query information one -also needs a buffer for that information:

-

unsigned char -results[1024];

-

After that preliminary -steps we are ready to query service in a loop (each line returned in -a single call to query()):

-

do

-

{

-

svc->query(&status, +spb1->getBufferLength(&status), spb1->getBuffer(&status));

+


+ +

+

Using +IService one can perform both available for services actions – +start services and query various information about started utilities +and server in general. When querying information one limitation takes +place – formats of parameter blocks, used by query() method, in +Firebird 3 are not supported by IXpbBuilder. Support will be probably +added in later versions, in Firebird 3 you will have to build and +analyze that blocks manually. Format of that blocks matches old +format (used in ISC API) one to one. +

+


+ +

+

To +start service one should first of all create appropriate SPB:

+

IXpbBuilder* +spb2 = utl->getXpbBuilder(&status, IXpbBuilder::SPB_START, +NULL, 0);

+

and +add required items to it. For example, to print encryption statistics +for database employee the following should be placed into SPB:

+

spb2->insertTag(&status, +isc_action_svc_db_stats);

+

spb2->insertString(&status, +isc_spb_dbname, "employee");

+

spb2->insertInt(&status, +isc_spb_options, isc_spb_sts_encryption);

+

After +it service can be started using start() method of IService +interface:

+

svc->start(&status, +spb2->getBufferLength(&status), spb2->getBuffer(&status));

+


+ +

+

Many +started services (including mentioned here gstat) return text +information during execution. To display it one should query started +service anout that information line by line. This is done by calling +query() method of IService interface with +appropriate send and receive blocks of parameters. Send block may +contain various helper information (like timeout when querying +service) or information to be passed to stdin of service utility or +may be empty in the simplest case. Receive block must contain list of +tags you want to receive from service. For most of utilities this is +single isc_info_svc_line:

+

const +unsigned char receiveItems1[] = {isc_info_svc_line};

+

To +query information one also needs a buffer for that information:

+

unsigned +char results[1024];

+

After +that preliminary steps we are ready to query service in a loop (each +line returned in a single call to query()):

+

do

+

{

+

svc->query(&status, 0, NULL, sizeof(receiveItems1), receiveItems1, sizeof(results), -results);

-

} while -(printInfo(results, sizeof(results)));

-

In this example we suppose -that printInfo() function returns TRUE as long as service returns -results block containing next output line (i.e. till end of data -stream from service). Format of results block varies from service to -service, and some services like gsec produce historical formats that -are not trivial for parse – but this is out of our scope here. A -minimum working sample of printInfo() is present in example -09.service.cpp.

-


-

-

Same query method is used -to retrieve information about server but in this case query function -is not invoked in a loop, i.e. buffer must be big enough to fit all -information at once. This is not too hard – typically such calls do -not return much data. As in previous case begin with receive block -placing required items in it – in our example it's -isc_info_svc_server_version:

-

const unsigned char -receiveItems2[] = {isc_info_svc_server_version};

-

Existing from previous -call results buffer may be reused. No loop is needed here:

-

svc->query(&status, +results);

+

} +while (printInfo(results, sizeof(results)));

+

In +this example we suppose that printInfo() function returns TRUE as +long as service returns results block containing next output line +(i.e. till end of data stream from service). Format of results block +varies from service to service, and some services like gsec produce +historical formats that are not trivial for parse – but this is out +of our scope here. A minimum working sample of printInfo() is present +in example 09.service.cpp.

+


+ +

+

Same +query method is used to retrieve information about server but in this +case query function is not invoked in a loop, i.e. buffer must be big +enough to fit all information at once. This is not too hard – +typically such calls do not return much data. As in previous case +begin with receive block placing required items in it – in our +example it's isc_info_svc_server_version:

+

const +unsigned char receiveItems2[] = {isc_info_svc_server_version};

+

Existing +from previous call results buffer may be reused. No loop is needed +here:

+

svc->query(&status, 0, NULL, sizeof(receiveItems2), receiveItems2, sizeof(results), -results);

-

printInfo(results, -sizeof(results));

-


-

-

After finishing with -services tasks do not forget to close an interface:

-

svc->detach(&status);

-


-

-

-Writing plugins.

-

To write a plugin means to -implement some interfaces and place your implementation into dynamic -library (.dll in windows or .so in linux) later referenced as plugin -module or just module. +results);

+

printInfo(results, +sizeof(results));

+


+ +

+

After +finishing with services tasks do not forget to close an interface:

+

svc->detach(&status);

+


+ +

+

+Writing +plugins.

+

To +write a plugin means to implement some interfaces and place your +implementation into dynamic library (.dll in windows or .so in linux) +later referenced as plugin +module or just +module. In most cases single plugin is place in dynamic library but in common -case. One of that interfaces – IPluginModule +case. One of that interfaces – IPluginModule – is module-wide (as more or less clear from it's name), others are per plugin. Also each plugin module should contain special exported entrypoint firebird_plugin() which name is defined in include file -firebird/Interfaces.h as FB_PLUGIN_ENTRY_POINT.

-

In previous part of this -text we were mostly describing how to use existing interfaces, here -main attention will be paid to implementing interfaces yourself. -Certainly to do it one can and should use already existing interfaces -both generic needed for accessing firebird databases (already -described) and some more interfaces specifically designed for -plugins.

-


-

-

Following text is actively -using example of database encryption plugin +firebird/Interfaces.h as FB_PLUGIN_ENTRY_POINT.

+

In +previous part of this text we were mostly describing how to use +existing interfaces, here main attention will be paid to implementing +interfaces yourself. Certainly to do it one can and should use +already existing interfaces both generic needed for accessing +firebird databases (already described) and some more interfaces +specifically designed for plugins.

+


+ +

+

Following +text is actively using example of database encryption plugin examples/dbcrypt/DbCrypt.cpp. It will be good idea to compile this -sample yourself and learn it when reading later.

-


-

-

Implementation of plugin module.

-

Plugins actively interact -with special firebird component called plugin -manager. In particular plugin manager should -be aware what plugin modules were loaded and must be notified if -operating system tries to unload one of that modules without explicit -plugin manager command (this may happen first of all when using -embedded access – when exit() is called in a program or main -firebird library fbclient -is unloaded). Primary task of IPluginModule interface is -that notification. First of all one must decide - how to detect that -module is going to be unloaded? When dynamic library is unloaded for -some reason a lot of OS-dependent actions is performed and some of -that actions may be used to detect this fact in the program. When -writing plugins distributed with firebird we always use invocation of -destructor of global variable. The big “plus” for this method is -that it is OS independent (though something like atexit() function -maybe also used successfully). But use of destructor makes it -possible to easily concentrate almost everything related with unload -detection in single class implementing at the same time IPluginModule -interface.

-


-

-

Minimum implementation -looks as follows:

-


-

-

class PluginModule : -public IPluginModuleImpl<PluginModule, CheckStatusWrapper>

-

{

-

private:

-

IPluginManager* -pluginManager;

-


-

-

public:

-

PluginModule()

-

: pluginManager(NULL)

-

{ }

-


-

-

~PluginModule()

-

{

-

if (pluginManager)

-

{

-

pluginManager->unregisterModule(this);

-

doClean();

-

}

-

}

-


-

-

void -registerMe(IPluginManager* m)

-

{

-

pluginManager = m;

-

pluginManager->registerModule(this);

-

}

-


-

-

void doClean()

-

{

-

pluginManager = NULL;

-

}

-

};

-


-

-

The only data member is -plugin manager interface IPluginManager. +sample yourself and learn it when reading later.

+


+ +

+

Implementation of plugin +module.

+

Plugins +actively interact with special firebird component called plugin +manager. In +particular plugin manager should be aware what plugin modules were +loaded and must be notified if operating system tries to unload one +of that modules without explicit plugin manager command (this may +happen first of all when using embedded access – when exit() is +called in a program or main firebird library fbclient +is unloaded). Primary task of +IPluginModule interface is that notification. First of all one must +decide - how to detect that module is going to be unloaded? When +dynamic library is unloaded for some reason a lot of OS-dependent +actions is performed and some of that actions may be used to detect +this fact in the program. When writing plugins distributed with +firebird we always use invocation of destructor of global variable. +The big “plus” for this method is that it is OS independent +(though something like atexit() function maybe also used +successfully). But use of destructor makes it possible to easily +concentrate almost everything related with unload detection in single +class implementing at the same time IPluginModule +interface.

+


+ +

+

Minimum +implementation looks as follows:

+


+ +

+

class +PluginModule : public IPluginModuleImpl<PluginModule, +CheckStatusWrapper>

+

{

+

private:

+

IPluginManager* +pluginManager;

+


+ +

+

public:

+

PluginModule()

+

: +pluginManager(NULL)

+

{ +}

+


+ +

+

~PluginModule()

+

{

+

if +(pluginManager)

+

{

+

pluginManager->unregisterModule(this);

+

doClean();

+

}

+

}

+


+ +

+

void +registerMe(IPluginManager* m)

+

{

+

pluginManager += m;

+

pluginManager->registerModule(this);

+

}

+


+ +

+

void +doClean()

+

{

+

pluginManager += NULL;

+

}

+

};

+


+ +

+

The +only data member is plugin manager interface IPluginManager. It's passed to registerModule() function and saved in private variable, at the same time module is registered in plugin manager by the call to registerModule() method with own address as a single -parameter. Variable pluginManager not -only stores pointer to interface, at the same time it serves as a -flag that module is registered. When destructor of registered module -is invoked it notifies plugin manager (yes, this is what for this -class exists!) about unexpected unload by the call to -unregisterModule() passing pointer to itself. When plugin manager is -going to unload module in regular way in first of all calls doClean() -method changing module state to unregistered and this avoiding call -to unregisterModule() when OS performs actual unload.

-


-

-

Implementing plugin's -interface IPluginModule we met with first interface required to -implement plugins – IPluginManager. It -will be actively used later, the rest of internals of this class will -hardly be required to you after copying it to your program. Just +parameter. Variable pluginManager +not only stores +pointer to interface, at the same time it serves as a flag that +module is registered. When destructor of registered module is invoked +it notifies plugin manager (yes, this is what for this class exists!) +about unexpected unload by the call to unregisterModule() passing +pointer to itself. When plugin manager is going to unload module in +regular way in first of all calls doClean() method changing module +state to unregistered and this avoiding call to unregisterModule() +when OS performs actual unload.

+


+ +

+

Implementing +plugin's interface IPluginModule we met with first interface required +to implement plugins – IPluginManager. +It will be actively used later, the rest of internals of this class +will hardly be required to you after copying it to your program. Just don't forget to declare global variable of this type and call -registerMe() function from FB_PLUGIN_ENTRY_POINT.

-


-

-

Core interface of any plugin.

-

Let's start implementing -plugin itself. The type of main interface depends upon plugin type -(which is obvious), but all of them are based on common reference -counted interface IPluginBase which performs common for all plugins -(and very simple) tasks. Each plugin has some (also reference -counted) object which owns this plugin. In order to perform -smart plugin lifetime management any plugin must be able to store -that owner information and report it to plugin manager on request. -That means that each plugin must implement two trivial methods -setOwner() and getOwner() contained in IPluginBase interface. +registerMe() function from FB_PLUGIN_ENTRY_POINT.

+


+ +

+

Core interface of any +plugin.

+

Let's +start implementing plugin itself. The type of main interface depends +upon plugin type (which is obvious), but all of them are based on +common reference counted interface IPluginBase which performs common +for all plugins (and very simple) tasks. Each plugin has some (also +reference counted) object which owns this plugin. In order to +perform smart plugin lifetime management any plugin must be able to +store that owner information and report it to plugin manager on +request. That means that each plugin must implement two trivial +methods setOwner() and getOwner() contained in IPluginBase interface. Type-dependent methods are certainly more interesting - they are -discussed in interfaces description part.

-


-

-

Let's take a look at -typical part of any plugin implementation (here I specially use -non-existent type SomePlugin):

-

class MyPlugin : public -ISomePluginImpl<MyPlugin, CheckStatusWrapper>

-

{

-

public:

-

explicit -MyPlugin(IPluginConfig* cnf) throw()

-

: config(cnf), -refCounter(0), owner(NULL)

-

{

-

config->addRef();

-

}

-

Constructor -gets as parameter plugin configuration interface. If you are going to -have you plugin configured in some way it's good idea to save this -interface in your plugin and use it later. This will let you use -common for all firebird configuration style letting users have -familiar configuration and minimize code written. Certainly when -saving any reference counted interface it's better not forget to add -reference to it. Also set reference counter to 0 and plugin owner to -NULL.

-


-

-

~MyPlugin()

-

{

-

config->release();

-

}

-

Destructor -releases config interface. Pay attention – we do not change -reference counter of our owner cause it owns us, not we own it.

-


-

-

// IRefCounted -implementation

-

int release()

-

{

-

if (--refCounter == 0)

-

{

-

delete this;

-

return 0;

-

}

-

return 1;

-

}

-


-

-

void addRef()

-

{

-

++refCounter;

-

}

-

Absolutely -typical implementation of reference counted object.

-


-

-

// IPluginBase -implementation

-

void -setOwner(IReferenceCounted* o)

-

{

-

owner = o;

-

}

-


-

-

IReferenceCounted* -getOwner()

-

{

-

return owner;

-

}

-

As it -was promised implementation of IPluginBase is trivial.

-


-

-

// ISomePlugin -implementation

-

// … here go various -methods required for particular plugin type

-


-

-

private:

-

IPluginConfig* config;

-

FbSampleAtomic -refCounter;

-

IReferenceCounted* -owner;

-

};

-


-

-

With this sample formal -part of main plugin interface implementation is over. After adding -type-specific methods (and writing probably a lo-o-o-ot of code to -make them useful) interface is ready.

-


-

-

Plugin's factory.

-

One more interface -required for plugin to work is IPluginFactory. +discussed in interfaces description part.

+


+ +

+

Let's +take a look at typical part of any plugin implementation (here I +specially use non-existent type SomePlugin):

+

class +MyPlugin : public ISomePluginImpl<MyPlugin, CheckStatusWrapper>

+

{

+

public:

+

explicit +MyPlugin(IPluginConfig* cnf) throw()

+

: +config(cnf), refCounter(0), owner(NULL)

+

{

+

config->addRef();

+

}

+

+Constructor gets as parameter +plugin configuration interface. If you are going to have you plugin +configured in some way it's good idea to save this interface in your +plugin and use it later. This will let you use common for all +firebird configuration style letting users have familiar +configuration and minimize code written. Certainly when saving any +reference counted interface it's better not forget to add reference +to it. Also set reference counter to 0 and plugin owner to NULL.

+


+ +

+

~MyPlugin()

+

{

+

config->release();

+

}

+

+Destructor releases config +interface. Pay attention – we do not change reference counter of +our owner cause it owns us, not we own it.

+


+ +

+

// +IRefCounted implementation

+

int +release()

+

{

+

if +(--refCounter == 0)

+

{

+

delete +this;

+

return +0;

+

}

+

return +1;

+

}

+


+ +

+

void +addRef()

+

{

+

++refCounter;

+

}

+

+Absolutely typical +implementation of reference counted object.

+


+ +

+

// +IPluginBase implementation

+

void +setOwner(IReferenceCounted* o)

+

{

+

owner += o;

+

}

+


+ +

+

IReferenceCounted* +getOwner()

+

{

+

return +owner;

+

}

+

+As it was promised +implementation of IPluginBase is trivial.

+


+ +

+

// +ISomePlugin implementation

+

// +… here go various methods required for particular plugin type

+


+ +

+

private:

+

IPluginConfig* +config;

+

FbSampleAtomic +refCounter;

+

IReferenceCounted* +owner;

+

};

+


+ +

+

With +this sample formal part of main plugin interface implementation is +over. After adding type-specific methods (and writing probably a +lo-o-o-ot of code to make them useful) interface is ready.

+


+ +

+

Plugin's factory.

+

One +more interface required for plugin to work is IPluginFactory. Factory creates instances of plugin and returns them to plugin -manager. Factory typically looks this way:

-

class Factory : public -IPluginFactoryImpl<Factory, CheckStatusWrapper>

-

{

-

public:

-

IPluginBase* +manager. Factory typically looks this way:

+

class +Factory : public IPluginFactoryImpl<Factory, CheckStatusWrapper>

+

{

+

public:

+

IPluginBase* createPlugin(CheckStatusWrapper* status, IPluginConfig* -factoryParameter)

-

{

-

MyPlugin* p = new -MyPlugin(factoryParameter);

-

p->addRef();

-

return p;

-

}

-

};

-


-

-

Here -attention should be payed to the fact that even in a case when code -in a function may throw exceptions (operator new may throw in a case -when memory exhausted) one need not always manually define try/catch -block – implementation of firebird interfaces does this job for -you, in implementation of IPluginFactory it's placed into template +factoryParameter)

+

{

+

MyPlugin* +p = new MyPlugin(factoryParameter);

+

p->addRef();

+

return +p;

+

}

+

};

+


+ +

+

+Here attention should be payed +to the fact that even in a case when code in a function may throw +exceptions (operator new may throw in a case when memory exhausted) +one need not always manually define try/catch block – +implementation of firebird interfaces does this job for you, in +implementation of IPluginFactory it's placed into template IPluginFactoryImpl. Take into an account that default status wrappers perform meaning-full processing only for FbException. But if you (that definitely makes sense if you work on some big project) define your own wrapper you can handle any type of C++ exception and pass -useful information about it from your plugin.

-


-

-

Plugin module initialization entrypoint.

-

When plugin manager loads -plugin module it invokes module initializing routine – the only -exported from plugin function FB_PLUGIN_ENTRY_POINT. To wrote it's -code one will need two global variables – plugin module and plugin -factory. In our case that is:

-

PluginModule module;

-

Factory factory;

-

If you module contains -more than one plugin you will need a factory per each plugin.

-


-

-

For FB_PLUGIN_ENTRY_POINT -we should not forget that it should be exported from plugin module, -and it requires taking into an account some OS specifics. We do it -using macro FB_DLL_EXPORT defined in +useful information about it from your plugin.

+


+ +

+

Plugin module +initialization entrypoint.

+

When +plugin manager loads plugin module it invokes module initializing +routine – the only exported from plugin function +FB_PLUGIN_ENTRY_POINT. To wrote it's code one will need two global +variables – plugin module and plugin factory. In our case that is:

+

PluginModule +module;

+

Factory +factory;

+

If +you module contains more than one plugin you will need a factory per +each plugin.

+


+ +

+

For +FB_PLUGIN_ENTRY_POINT we should not forget that it should be exported +from plugin module, and it requires taking into an account some OS +specifics. We do it using macro FB_DLL_EXPORT defined in examples/interfaces/ifaceExamples.h. If you are sure you write plugin only for some specific OS you can make this place a bit simpler. In minimum case the function should register module and all factories in -plugin manager:

-

extern "C" void -FB_DLL_EXPORT FB_PLUGIN_ENTRY_POINT(IMaster* master)

-

{

-

IPluginManager* -pluginManager = master->getPluginManager();

-


-

-

module.registerMe(pluginManager);

-

pluginManager->registerPluginFactory(IPluginManager::TYPE_DB_CRYPT, -"DbCrypt_example", &factory);

-

}

-

First of all we call -written by us not long ago PluginModule::registerMe() function which -will saves IPluginManager for future use and registers our plugin -module. Next time to register factory (or factories in case of -multiple plugins per module) takes place. We must pass correct plugin -type (valid types are enumerated in interface IPluginManager) and a -name under which plugin will be registered. In simple case it should -match with the name of dynamic library with plugin module. Following -last rule will help you avoid configuring your plugin manually in -plugins.conf.

-


-

-

Pay attention - unlike -applications plugins should not use fb_get_master_interface() to -obtain IMaster. Instance, passed to FB_PLUGIN_ENTRY_POINT, should be -used instead. If you ever need master interface in your plugin take -care about saving it in this function.

-


-

-


-

-

-Interfaces: from A to Z

-

In this glossary we do not -list interfaces that are not actively used (like IRequest, needed -first of all to support legacy ISC API requests). Same reference may -be made for a number of methods (like compileRequest() in -IAttachment). For interfaces / methods, having direct analogue in old -API, that analogue is provided.

-


-

-

Generic interfaces.

-

Attachment -interface – replaces isc_db_handle:

-
    -
  1. void +plugin manager:

    +

    extern +"C" void FB_DLL_EXPORT FB_PLUGIN_ENTRY_POINT(IMaster* +master)

    +

    {

    +

    IPluginManager* +pluginManager = master->getPluginManager();

    +


    + +

    +

    module.registerMe(pluginManager);

    +

    pluginManager->registerPluginFactory(IPluginManager::TYPE_DB_CRYPT, +"DbCrypt_example", &factory);

    +

    }

    +

    First +of all we call written by us not long ago PluginModule::registerMe() +function which will saves IPluginManager for future use and registers +our plugin module. Next time to register factory (or factories in +case of multiple plugins per module) takes place. We must pass +correct plugin type (valid types are enumerated in interface +IPluginManager) and a name under which plugin will be registered. In +simple case it should match with the name of dynamic library with +plugin module. Following last rule will help you avoid configuring +your plugin manually in plugins.conf.

    +


    + +

    +

    Pay +attention - unlike applications plugins should not use +fb_get_master_interface() to obtain IMaster. Instance, passed to +FB_PLUGIN_ENTRY_POINT, should be used instead. If you ever need +master interface in your plugin take care about saving it in this +function.

    +


    + +

    +


    + +

    +

    +Interfaces: +from A to Z

    +

    In +this glossary we do not list interfaces that are not actively used +(like IRequest, needed first of all to support legacy ISC API +requests). Same reference may be made for a number of methods (like +compileRequest() in IAttachment). For interfaces / methods, having +direct analogue in old API, that analogue is provided.

    +


    + +

    +

    Generic interfaces.

    +

    Attachment +interface – replaces isc_db_handle:

    +
      +
    1. +

      void getInfo(StatusType* status, unsigned itemsLength, const unsigned char* items, unsigned bufferLength, unsigned char* buffer) – - replaces isc_database_info().

      -
    2. ITransaction* + replaces isc_database_info().

      +
    3. +

      ITransaction* startTransaction(StatusType* status, unsigned tpbLength, const unsigned char* tpb) – partially replaces isc_start_multiple(), to - start >1 transaction distributed transactions - coordinator should be used, also possible to join - 2 transactions into single distributed transaction.

      -
    4. ITransaction* + start >1 transaction distributed transactions + coordinator should be used, also possible to join + 2 transactions into single distributed transaction.

      +
    5. +

      ITransaction* reconnectTransaction(StatusType* status, unsigned length, const unsigned char* id) – makes it possible to connect to a transaction in limbo. Id parameter contains transaction number in network format - of given length.

      -
    6. IRequest* + of given length.

      +
    7. +

      IRequest* compileRequest(StatusType* status, unsigned blrLength, const - unsigned char* blr) – support of ISC API.

      -
    8. void + unsigned char* blr) – support of ISC API.

      +
    9. +

      void transactRequest(StatusType* status, ITransaction* transaction, unsigned blrLength, const unsigned char* blr, unsigned inMsgLength, const unsigned char* inMsg, unsigned outMsgLength, unsigned char* - outMsg) – support of ISC API.

      -
    10. IBlob* + outMsg) – support of ISC API.

      +
    11. +

      IBlob* createBlob(StatusType* status, ITransaction* transaction, ISC_QUAD* id, unsigned bpbLength, const unsigned char* bpb) – creates new - blob, stores it's identifier in id, replaces isc_create_blob2().

      -
    12. IBlob* + blob, stores it's identifier in id, replaces isc_create_blob2().

      +
    13. +

      IBlob* openBlob(StatusType* status, ITransaction* transaction, ISC_QUAD* id, unsigned bpbLength, const unsigned char* bpb) – opens existing - blob, replaces isc_open_blob2().

      -
    14. int + blob, replaces isc_open_blob2().

      +
    15. +

      int getSlice(StatusType* status, ITransaction* transaction, ISC_QUAD* id, unsigned sdlLength, const unsigned char* sdl, unsigned paramLength, const unsigned char* param, int sliceLength, unsigned - char* slice) - support of ISC API.

      -
    16. void + char* slice) - support of ISC API.

      +
    17. +

      void putSlice(StatusType* status, ITransaction* transaction, ISC_QUAD* id, unsigned sdlLength, const unsigned char* sdl, unsigned paramLength, const unsigned char* param, int sliceLength, unsigned - char* slice) - support of ISC API.

      -
    18. void + char* slice) - support of ISC API.

      +
    19. +

      void executeDyn(StatusType* status, ITransaction* transaction, unsigned - length, const unsigned char* dyn) - support of ISC API.

      -
    20. IStatement* + length, const unsigned char* dyn) - support of ISC API.

      +
    21. +

      IStatement* prepare(StatusType* status, ITransaction* tra, unsigned stmtLength, const char* sqlStmt, unsigned dialect, unsigned flags) – replaces isc_dsql_prepare(). Additional parameter flags makes it possible to control what information will be preloaded from engine at once (i.e. - in single network packet for remote operation).

      -
    22. ITransaction* + in single network packet for remote operation).

      +
    23. +

      ITransaction* execute(StatusType* status, ITransaction* transaction, unsigned stmtLength, const char* sqlStmt, unsigned dialect, IMessageMetadata* inMetadata, void* inBuffer, IMessageMetadata* outMetadata, void* outBuffer) – executes any SQL statement except returning multiple rows of data. Partial analogue of isc_dsql_execute2() - in and out XSLQDAs replaced with input and output messages with appropriate - buffers.

      -
    24. IResultSet* + buffers.

      +
    25. +

      IResultSet* openCursor(StatusType* status, ITransaction* transaction, unsigned stmtLength, const char* sqlStmt, unsigned dialect, IMessageMetadata* inMetadata, void* inBuffer, IMessageMetadata* outMetadata, const char* cursorName, unsigned cursorFlags) – executes SQL statement - potentially returning multiple rows of data. Returns ResultSet + potentially returning multiple rows of data. Returns ResultSet interface which should be used to fetch that data. Format of output data is defined by outMetadata parameter, leaving it NULL default format may be used. Parameter cursorName specifies name of opened cursor (analogue of isc_dsql_set_cursor_name()). Parameter cursorFlags is needed to open bidirectional cursor setting it's - value to Istatement::CURSOR_TYPE_SCROLLABLE.

      -
    26. IEvents* + value to Istatement::CURSOR_TYPE_SCROLLABLE.

      +
    27. +

      IEvents* queEvents(StatusType* status, IEventCallback* callback, unsigned length, const unsigned char* events) – replaces isc_que_events() call. Instead callback function with void* parameter callback - interface is used.

      -
    28. void + interface is used.

      +
    29. +

      void cancelOperation(StatusType* status, int option) – replaces - fb_cancel_operation().

      -
    30. void ping(StatusType* - status) – check connection status. If test fails the only - operation possible with attachment is to close it.

      -
    31. void + fb_cancel_operation().

      +
    32. +

      void + ping(StatusType* status) – check connection status. If test fails + the only operation possible with attachment is to close it.

      +
    33. +

      void detach(StatusType* status) – replaces isc_detach_database(). On - success releases interface.

      -
    34. void + success releases interface.

      +
    35. +

      void dropDatabase(StatusType* status) - replaces isc_drop_database(). On - success releases interface.

      -
    -


    -

    -


    -

    -

    Blob -interface – replaces isc_blob_handle:

    -
      -
    1. void + success releases interface.

      +
    +


    + +

    +


    + +

    +

    Blob +interface – replaces isc_blob_handle:

    +
      +
    1. +

      void getInfo(StatusType* status, unsigned itemsLength, const unsigned char* items, unsigned bufferLength, unsigned char* buffer) – - replaces isc_blob_info().

      -
    2. int + replaces isc_blob_info().

      +
    3. +

      int getSegment(StatusType* status, unsigned bufferLength, void* buffer, unsigned* segmentLength) – replaces isc_get_segment(). Unlike it never returns isc_segstr_eof and isc_segment errors (that are - actually not errors), instead returns completion - codes IStatus::RESULT_NO_DATA and IStatus::RESULT_SEGMENT, - normal return is IStatus::RESULT_OK.

      -
    4. void + actually not errors), instead returns completion + codes IStatus::RESULT_NO_DATA and IStatus::RESULT_SEGMENT, + normal return is IStatus::RESULT_OK.

      +
    5. +

      void putSegment(StatusType* status, unsigned length, const void* buffer) - – replaces isc_put_segment().

      -
    6. void + – replaces isc_put_segment().

      +
    7. +

      void cancel(StatusType* status) – replaces isc_cancel_blob(). On - success releases interface.

      -
    8. void + success releases interface.

      +
    9. +

      void close(StatusType* status) – replaces isc_close_blob(). On success - releases interface.

      -
    10. int seek(StatusType* - status, int mode, int offset) – replaces isc_seek_blob().

      -
    -


    -

    -


    -

    -

    Config -interface – generic configuration file interface:

    -
      -
    1. IConfigEntry* - find(StatusType* status, const char* name) – find entry by name.

      -
    2. IConfigEntry* + releases interface.

      +
    3. +

      int + seek(StatusType* status, int mode, int offset) – replaces + isc_seek_blob().

      +
    +


    + +

    +


    + +

    +

    Config +interface – generic configuration file interface:

    +
      +
    1. +

      IConfigEntry* + find(StatusType* status, const char* name) – find entry by name.

      +
    2. +

      IConfigEntry* findValue(StatusType* status, const char* name, const char* value) – - find entry by name and value.

      -
    3. IConfigEntry* + find entry by name and value.

      +
    4. +

      IConfigEntry* findPos(StatusType* status, const char* name, unsigned pos) – find - entry by name and position. If configuration file contains lines:

      -
    -

    Db=DBA

    -

    Db=DBB

    -

    Db=DBC

    -

    call to findPos(status, -“Db”, 2) will return entry with value DBB.

    -


    -

    -


    -

    -

    ConfigManager + entry by name and position. If configuration file contains lines:

    +
+

Db=DBA

+

Db=DBB

+

Db=DBC

+

call +to findPos(status, “Db”, 2) will return entry with value DBB.

+


+ +

+


+ +

+

ConfigManager interface – generic interface to access various configuration -objects:

-
    -
  1. const char* - getDirectory(unsigned code) – returns location of appropriate - directory in current firebird instance. See codes for this call a - few lines later.

    -
  2. IFirebirdConf* +objects:

    +
      +
    1. +

      const + char* getDirectory(unsigned code) – returns location of + appropriate directory in current firebird instance. See codes for + this call a few lines later.

      +
    2. +

      IFirebirdConf* getFirebirdConf() - returns interface to access default - configuration values (from firebird.conf).

      -
    3. IFirebirdConf* + configuration values (from firebird.conf).

      +
    4. +

      IFirebirdConf* getDatabaseConf(const char* dbName) - returns interface to access db-specific configuration (takes into an account firebird.conf and - appropriate part of databases.conf).

      -
    5. IConfig* + appropriate part of databases.conf).

      +
    6. +

      IConfig* getPluginConfig(const char* configuredPlugin) – returns interface - to access named plugin configuration.

      -
    7. const char* - getInstallDirectory() - returns directory where firebird is - installed.

      -
    8. const char* - getRootDirectory() - returns root directory of current instance, in - single-instance case usually matches install directory.

      -
    -

    Directory -codes:

    -

    DIR_BIN – bin (utilities -like isql, gbak, gstat)

    -

    DIR_SBIN – sbin (fbguard -and firebird server)

    -

    DIR_CONF – configuration -files (firebird.conf, databases.conf, plugins.conf)

    -

    DIR_LIB – lib (fbclient, -ib_util)

    -

    DIR_INC – include -(ibase.h, firebird/Interfaces.h)

    -

    DIR_DOC - documentation

    -

    DIR_UDF – UDF (ib_udf, -fbudf)

    -

    DIR_SAMPLE - samples

    -

    DIR_SAMPLEDB – samples -database (employee.fdb)

    -

    DIR_HELP – qli help -(help.fdb)

    -

    DIR_INTL – international -libraries (fbintl)

    -

    DIR_MISC – miscellaneous -files (like uninstall manifest and something else)

    -

    DIR_SECDB – where -security database is stored (securityN.fdb)

    -

    DIR_MSG – where messages -file is stored (firebird.msg)

    -

    DIR_LOG – where log file -is stored (firebird.log)

    -

    DIR_GUARD – where -guardian lock is stored (fb_guard)

    -

    DIR_PLUGINS – plugins -directory ([lib]Engine13.{dll|so})

    -


    -

    -


    -

    -

    ConfigEntry + to access named plugin configuration.

    +
  3. +

    const + char* getInstallDirectory() - returns directory where firebird is + installed.

    +
  4. +

    const + char* getRootDirectory() - returns root directory of current + instance, in single-instance case usually matches install directory.

    +
+

Directory +codes:

+

DIR_BIN +– bin (utilities like isql, gbak, gstat)

+

DIR_SBIN +– sbin (fbguard and firebird server)

+

DIR_CONF +– configuration files (firebird.conf, databases.conf, plugins.conf)

+

DIR_LIB +– lib (fbclient, ib_util)

+

DIR_INC +– include (ibase.h, firebird/Interfaces.h)

+

DIR_DOC +- documentation

+

DIR_UDF +– UDF (ib_udf, fbudf)

+

DIR_SAMPLE +- samples

+

DIR_SAMPLEDB +– samples database (employee.fdb)

+

DIR_HELP +– qli help (help.fdb)

+

DIR_INTL +– international libraries (fbintl)

+

DIR_MISC +– miscellaneous files (like uninstall manifest and something else)

+

DIR_SECDB +– where security database is stored (securityN.fdb)

+

DIR_MSG +– where messages file is stored (firebird.msg)

+

DIR_LOG +– where log file is stored (firebird.log)

+

DIR_GUARD +– where guardian lock is stored (fb_guard)

+

DIR_PLUGINS +– plugins directory ([lib]Engine13.{dll|so})

+


+ +

+


+ +

+

ConfigEntry interface – represents an entry (Key = Values with probably -sub-entries) in firebird configuration file:

-
    -
  1. const char* getName() - - returns key name.

    -
  2. const char* - getValue() - returns value as character string.

    -
  3. ISC_INT64 - getIntValue() - treats value as integer and returns it.

    -
  4. FB_BOOLEAN - getBoolValue() - treats value as boolean and returns it.

    -
  5. IConfig* +sub-entries) in firebird configuration file:

    +
      +
    1. +

      const + char* getName() - returns key name.

      +
    2. +

      const + char* getValue() - returns value as character string.

      +
    3. +

      ISC_INT64 + getIntValue() - treats value as integer and returns it.

      +
    4. +

      FB_BOOLEAN + getBoolValue() - treats value as boolean and returns it.

      +
    5. +

      IConfig* getSubConfig(StatusType* status) – treats sub-entries as separate - configuration file and returns Config interface for it.

      -
    -


    -

    -


    -

    -

    Dtc + configuration file and returns Config interface for it.

    +
+


+ +

+


+ +

+

Dtc interface – distributed transactions coordinator. Used to start distributed (working with 2 or more attachments) transaction. Unlike pre-FB3 approach where distributed transaction must be started in this way from the most beginning FB3's distributed transactions coordinator makes it also possible to join already started -transactions into single distributed transaction.

-
    -
  1. ITransaction* +transactions into single distributed transaction.

    +
      +
    1. +

      ITransaction* join(StatusType* status, ITransaction* one, ITransaction* two) – joins 2 independent transactions into distributed transaction. On success both transactions passed to join() are released and pointers - to them should not be used any more.

      -
    2. IDtcStart* - startBuilder(StatusType* status) – returns DtcStart - interface.

      -
    -


    -

    -


    -

    -

    DtcStart + to them should not be used any more.

    +
  2. +

    IDtcStart* + startBuilder(StatusType* status) – returns DtcStart + interface.

    +
+


+ +

+


+ +

+

DtcStart interface – replaces array of struct TEB (passed to isc_start_multiple() in ISC API). This interface accumulates attachments (and probably appropriate TPBs) for which dustributed -transaction should be started.

-
    -
  1. void +transaction should be started.

    +
      +
    1. +

      void addAttachment(StatusType* status, IAttachment* att) – adds - attachment, transaction for it will be started with default TPB.

      -
    2. void + attachment, transaction for it will be started with default TPB.

      +
    3. +

      void addWithTpb(StatusType* status, IAttachment* att, unsigned length, const unsigned char* tpb) - adds attachment and TPB which will be - used to start transaction for this attachment.

      -
    4. ITransaction* + used to start transaction for this attachment.

      +
    5. +

      ITransaction* start(StatusType* status) – start distributed transaction for accumulated attachments. On successful return DtcStart interface is - disposed automatically.

      -
    -


    -

    -


    -

    -

    EventCallback + disposed automatically.

    +
+


+ +

+


+ +

+

EventCallback interface – replaces callback function used in isc_que_events() call. Should be implemented by user to monitor events with -IAttachment::queEvents() method.

-
    -
  1. void +IAttachment::queEvents() method.

    +
      +
    1. +

      void eventCallbackFunction(unsigned length, const unsigned char* events) - – is called each time event happens.

      -
    -


    -

    -


    -

    -

    Events + – is called each time event happens.

    +
+


+ +

+


+ +

+

Events interface – replaces event identifier when working with events -monitoring.

-
    -
  1. void +monitoring.

    +
      +
    1. +

      void cancel(StatusType* status) - cancels events monitoring started by - IAttachment::queEvents().

      -
    -


    -

    -


    -

    -

    FirebirdConf interface – -access to main firebird configuration. Used for both default -configuration, set by firebird.conf, and per-database configuration, -adjusted by databases.conf. In order to speed up access to -configuration values calls accessing actual values use integer key -instead symbolic parameter name. Key is stable during server run -(i.e. plugin can get it once and than use to get configuration value -for different databases). -

    -
      -
    1. unsigned getKey(const - char* name) – get key for parameter name. ~0 (all bits are 1) is - returned in a case when there is no such parameter.

      -
    2. ISC_INT64 - asInteger(unsigned key) – return value of integer parameter.

      -
    3. const char* - asString(unsigned key) - return value of string parameter.

      -
    4. FB_BOOLEAN + IAttachment::queEvents().

      +
    +


    + +

    +


    + +

    +

    FirebirdConf +interface – access to main firebird configuration. Used for both +default configuration, set by firebird.conf, and per-database +configuration, adjusted by databases.conf. In order to speed up +access to configuration values calls accessing actual values use +integer key instead symbolic parameter name. Key is stable during +server run (i.e. plugin can get it once and than use to get +configuration value for different databases). +

    +
      +
    1. +

      unsigned + getKey(const char* name) – get key for parameter name. ~0 (all + bits are 1) is returned in a case when there is no such parameter.

      +
    2. +

      ISC_INT64 + asInteger(unsigned key) – return value of integer parameter.

      +
    3. +

      const + char* asString(unsigned key) - return value of string parameter.

      +
    4. +

      FB_BOOLEAN asBoolean(unsigned key) - return value of boolean parameter. Standard abbreviations (1/true/t/yes/y) are treated as “true”, - all other cases – false.

      -
    -


    -

    -


    -

    -

    Master + all other cases – false.

    +
+


+ +

+


+ +

+

Master interface – main interface from which start all operations with -firebird API.

-
    -
  1. IStatus* getStatus() - - get instance if Status interface.

    -
  2. IProvider* - getDispatcher() - get instance of Provider - interface, implemented by yValve (main provider instance).

    -
  3. IPluginManager* - getPluginManager() - get instance of PluginManager - interface.

    -
  4. ITimerControl* - getTimerControl() - get instance of TimerControl - interface.

    -
  5. IDtc* getDtc() - get - instance of Dtc interface.

    -
  6. IUtil* - getUtilInterface() - get instance of Util - interface.

    -
  7. IConfigManager* - getConfigManager() - get instance of ConfigManager - interface.

    -
-


-

-


-

-

MessageMetadata +firebird API.

+
    +
  1. +

    IStatus* + getStatus() - get instance if Status + interface.

    +
  2. +

    IProvider* + getDispatcher() - get instance of Provider + interface, implemented by yValve (main provider instance).

    +
  3. +

    IPluginManager* + getPluginManager() - get instance of PluginManager + interface.

    +
  4. +

    ITimerControl* + getTimerControl() - get instance of TimerControl + interface.

    +
  5. +

    IDtc* + getDtc() - get instance of Dtc interface.

    +
  6. +

    IUtil* + getUtilInterface() - get instance of Util + interface.

    +
  7. +

    IConfigManager* + getConfigManager() - get instance of ConfigManager + interface.

    +
+


+ +

+


+ +

+

MessageMetadata interface – partial analogue of XSQLDA (does not contain message data, only message format info is present). Used in a calls related -with execution of SQL statements.

-
    -
  1. unsigned +with execution of SQL statements.

    +
      +
    1. +

      unsigned getCount(StatusType* status) – returns number of fields/parameters in a message. In all calls, containing index parameter, it's value - should be: 0 <= index < getCount().

      -
    2. const char* - getField(StatusType* status, unsigned index) – returns field name.

      -
    3. const char* - getRelation(StatusType* status, unsigned index) – returns relation - name (from which given field is selected).

      -
    4. const char* - getOwner(StatusType* status, unsigned index) - returns relation's - owner name.

      -
    5. const char* - getAlias(StatusType* status, unsigned index) - returns field alias.

      -
    6. unsigned + should be: 0 <= index < getCount().

      +
    7. +

      const + char* getField(StatusType* status, unsigned index) – returns field + name.

      +
    8. +

      const + char* getRelation(StatusType* status, unsigned index) – returns + relation name (from which given field is selected).

      +
    9. +

      const + char* getOwner(StatusType* status, unsigned index) - returns + relation's owner name.

      +
    10. +

      const + char* getAlias(StatusType* status, unsigned index) - returns field + alias.

      +
    11. +

      unsigned getType(StatusType* status, unsigned index) - returns field SQL - type.

      -
    12. FB_BOOLEAN + type.

      +
    13. +

      FB_BOOLEAN isNullable(StatusType* status, unsigned index) - returns true if - field is nullable.

      -
    14. int + field is nullable.

      +
    15. +

      int getSubType(StatusType* status, unsigned index) - returns blof field - subtype (0 – binary, 1 – text, etc.).

      -
    16. unsigned + subtype (0 – binary, 1 – text, etc.).

      +
    17. +

      unsigned getLength(StatusType* status, unsigned index) - returns maximum - field length.

      -
    18. int + field length.

      +
    19. +

      int getScale(StatusType* status, unsigned index) - returns scale factor - for numeric field.

      -
    20. unsigned + for numeric field.

      +
    21. +

      unsigned getCharSet(StatusType* status, unsigned index) - returns character - set for character field and text blob.

      -
    22. unsigned + set for character field and text blob.

      +
    23. +

      unsigned getOffset(StatusType* status, unsigned index) - returns offset of field data in message buffer (use it to access data in message - buffer).

      -
    24. unsigned + buffer).

      +
    25. +

      unsigned getNullOffset(StatusType* status, unsigned index) - returns offset - of null indicator for a field in message buffer.

      -
    26. IMetadataBuilder* - getBuilder(StatusType* status) - returns MetadataBuilder - interface initialized with this message metadata.

      -
    27. unsigned + of null indicator for a field in message buffer.

      +
    28. +

      IMetadataBuilder* + getBuilder(StatusType* status) - returns MetadataBuilder + interface initialized with this message metadata.

      +
    29. +

      unsigned getMessageLength(StatusType* status) - returns length of message - buffer (use it to allocate memory for the buffer).

      -
    -


    -

    -


    -

    -

    MetadataBuilder + buffer (use it to allocate memory for the buffer).

    +
+


+ +

+


+ +

+

MetadataBuilder interface – makes it possible to coerce datatypes in existing -message or construct metadata from the beginning.

-
    -
  1. void +message or construct metadata from the beginning.

    +
      +
    1. +

      void setType(StatusType* status, unsigned index, unsigned type) – set - SQL type of a field.

      -
    2. void + SQL type of a field.

      +
    3. +

      void setSubType(StatusType* status, unsigned index, int subType) – set - blof field subtype.

      -
    4. void + blof field subtype.

      +
    5. +

      void setLength(StatusType* status, unsigned index, unsigned length) – - set maximum length of character field.

      -
    6. void + set maximum length of character field.

      +
    7. +

      void setCharSet(StatusType* status, unsigned index, unsigned charSet) – - set character set for character field and text blob.

      -
    8. void + set character set for character field and text blob.

      +
    9. +

      void setScale(StatusType* status, unsigned index, unsigned scale) – set - scale factor for numeric field -

      -
    10. void + scale factor for numeric field +

      +
    11. +

      void truncate(StatusType* status, unsigned count) – truncate message to - contain not more than count fields.

      -
    12. void + contain not more than count fields.

      +
    13. +

      void moveNameToIndex(StatusType* status, const char* name, unsigned index) – reorganize fields in a message – move field “name” - to given position.

      -
    14. void - remove(StatusType* status, unsigned index) – remove field.

      -
    15. unsigned - addField(StatusType* status) – add field.

      -
    16. IMessageMetadata* - getMetadata(StatusType* status) – get MessageMetadata - interface built by this builder.

      -
    -


    -

    -


    -

    -

    OffsetsCallback -interface:

    -
      -
    1. setOffset(StatusType* + to given position.

      +
    2. +

      void + remove(StatusType* status, unsigned index) – remove field.

      +
    3. +

      unsigned + addField(StatusType* status) – add field.

      +
    4. +

      IMessageMetadata* + getMetadata(StatusType* status) – get MessageMetadata + interface built by this builder.

      +
    +


    + +

    +


    + +

    +

    OffsetsCallback +interface:

    +
      +
    1. +

      setOffset(StatusType* status, unsigned index, unsigned offset, unsigned nullOffset) – notifies that offsets for field/parameter number “index” should be set to passed values. Should be implemented by user when - implementing MessageMetadata - interface and using Util::setOffsets().

      -
    -


    -

    -


    -

    -

    PluginConfig + implementing MessageMetadata + interface and using Util::setOffsets().

    +
+


+ +

+


+ +

+

PluginConfig interface – passed to plugin's factory when plugin instance (with -particular configuration) to be created.

-
    -
  1. const char* - getConfigFileName() - recommended file name where configuration for - plugin is expected to be stored.

    -
  2. IConfig* +particular configuration) to be created.

    +
      +
    1. +

      const + char* getConfigFileName() - recommended file name where + configuration for plugin is expected to be stored.

      +
    2. +

      IConfig* getDefaultConfig(StatusType* status) – plugin configuration loaded - with default rules.

      -
    3. IFirebirdConf* + with default rules.

      +
    4. +

      IFirebirdConf* getFirebirdConf(StatusType* status) – master firebird configuration taking into an account per-database settings for a - database with which will work new instance of plugin.

      -
    5. void + database with which will work new instance of plugin.

      +
    6. +

      void setReleaseDelay(StatusType* status, ISC_UINT64 microSeconds) – used by plugin to configure recommended delay during which plugin module will not be unloaded by plugin manager after release of last - plugin instance from that module.

      -
    -


    -

    -


    -

    -

    PluginFactory + plugin instance from that module.

    +
+


+ +

+


+ +

+

PluginFactory interface – should be implemented by plugin author when writing -plugin.

-
    -
  1. IPluginBase* +plugin.

    +
      +
    1. +

      IPluginBase* createPlugin(StatusType* status, IPluginConfig* factoryParameter) – creates new instance of plugin with passed recommended - configuration.

      -
    -


    -

    -


    -

    -

    PluginManager -interface – API of plugin manager.

    -
      -
    1. void + configuration.

      +
    +


    + +

    +


    + +

    +

    PluginManager +interface – API of plugin manager.

    +
      +
    1. +

      void registerPluginFactory(unsigned pluginType, const char* defaultName, IPluginFactory* factory) – registers named factory of plugins of - given type.

      -
    2. void - registerModule(IPluginModule* cleanup) – registers plugin module.

      -
    3. void + given type.

      +
    4. +

      void + registerModule(IPluginModule* cleanup) – registers plugin module.

      +
    5. +

      void unregisterModule(IPluginModule* cleanup) – unregisters plugin - module (in case of unexpected unload by OS).

      -
    6. IPluginSet* + module (in case of unexpected unload by OS).

      +
    7. +

      IPluginSet* getPlugins(StatusType* status, unsigned pluginType, const char* namesList, IFirebirdConf* firebirdConf) – returns PluginSet interface providing access to list of plugins of given type. Names @@ -1725,203 +2020,252 @@ interface – API of plugin manager.

      (NULL) – from configuration setting for given pluginType. If firebirdConf parameter is specified it is used for all configuration purporses (including getting list of plugins and passing to - PluginFactory::createPlugin() method), + PluginFactory::createPlugin() method), if missing (NULL) – default configuration (from firebird.conf) is - used.

      -
    8. IConfig* + used.

      +
    9. +

      IConfig* getConfig(StatusType* status, const char* filename) – returns Config interface for given configuration file name. Can be used by plugins to access configuration files with standard format but - non-default name.

      -
    10. void + non-default name.

      +
    11. +

      void releasePlugin(IPluginBase* plugin) – release given plugin. Should be used for plugins instead simple release() due to need to perform - additional actions with plugin owner before actual release.

      -
    -


    -

    -

    Constants defined by -PluginManager interface (plugin types):

    -

    TYPE_PROVIDER

    -

    TYPE_AUTH_SERVER

    -

    TYPE_AUTH_CLIENT

    -

    TYPE_AUTH_USER_MANAGEMENT

    -

    TYPE_EXTERNAL_ENGINE

    -

    TYPE_TRACE

    -

    TYPE_WIRE_CRYPT

    -

    TYPE_DB_CRYPT

    -

    TYPE_KEY_HOLDER

    -


    -

    -


    -

    -

    PluginModule + additional actions with plugin owner before actual release.

    +
+


+ +

+

Constants +defined by PluginManager interface (plugin types):

+

TYPE_PROVIDER

+

TYPE_AUTH_SERVER

+

TYPE_AUTH_CLIENT

+

TYPE_AUTH_USER_MANAGEMENT

+

TYPE_EXTERNAL_ENGINE

+

TYPE_TRACE

+

TYPE_WIRE_CRYPT

+

TYPE_DB_CRYPT

+

TYPE_KEY_HOLDER

+


+ +

+


+ +

+

PluginModule interface – represents plugin module (dynamic library). Should be implemented by plugin author in each plugin module (one instance per -module).

-
    -
  1. void doClean() - - called by plugin manager before normal unload of plugin module.

    -
-


-

-


-

-

PluginSet interface – -represents set of plugins of given type. Typically used by internal -firebird code but recommended for use in plugins loading other -plugins.

-
    -
  1. const char* getName() - - get name of current plugin in a set.

    -
  2. const char* - getModuleName() - get name of a module of current plugin in a set - (in simple case matches plugin name).

    -
  3. IPluginBase* +module).

    +
      +
    1. +

      void + doClean() - called by plugin manager before normal unload of plugin + module.

      +
    +


    + +

    +


    + +

    +

    PluginSet +interface – represents set of plugins of given type. Typically used +by internal firebird code but recommended for use in plugins loading +other plugins.

    +
      +
    1. +

      const + char* getName() - get name of current plugin in a set.

      +
    2. +

      const + char* getModuleName() - get name of a module of current plugin in a + set (in simple case matches plugin name).

      +
    3. +

      IPluginBase* getPlugin(StatusType* status) – get an instance of current plugin, returned interface should be casted to main interface of plugin of - requested in PluginManager::getPlugins() + requested in PluginManager::getPlugins() type. Returns NULL if set does not contain any more plugins. Reference counter of plugin, returned by this function, is incremented on return – do not forget to use releasePlugin() - method of PluginManager for plugins - returned by this method.

      -
    4. void next(StatusType* - status) – make set to switch to next plugin from the list.

      -
    5. void set(StatusType* - status, const char* list) – reset interface: make it work with - list of plugins provided by list parameter. Type of plugins remains - unchanged.

      -
    -


    -

    -


    -

    -

    Provider -interface – main interface to start database / service access.

    -
      -
    1. IAttachment* + method of PluginManager for plugins + returned by this method.

      +
    2. +

      void + next(StatusType* status) – make set to switch to next plugin from + the list.

      +
    3. +

      void + set(StatusType* status, const char* list) – reset interface: make + it work with list of plugins provided by list parameter. Type of + plugins remains unchanged.

      +
    +


    + +

    +


    + +

    +

    Provider +interface – main interface to start database / service access.

    +
      +
    1. +

      IAttachment* attachDatabase(StatusType* status, const char* fileName, unsigned dpbLength, const unsigned char* dpb) – replaces - isc_attach_database().

      -
    2. IAttachment* + isc_attach_database().

      +
    3. +

      IAttachment* createDatabase(StatusType* status, const char* fileName, unsigned dpbLength, const unsigned char* dpb) – replaces - isc_create_database().

      -
    4. IService* + isc_create_database().

      +
    5. +

      IService* attachServiceManager(StatusType* status, const char* service, unsigned spbLength, const unsigned char* spb) – replaces - isc_service_attach().

      -
    6. void + isc_service_attach().

      +
    7. +

      void shutdown(StatusType* status, unsigned timeout, const int reason) – - replaces fb_shutdown().

      -
    8. void + replaces fb_shutdown().

      +
    9. +

      void setDbCryptCallback(StatusType* status, ICryptKeyCallback* cryptCallback) – sets database encryption callback interface that will be used for following database and service attachments. See … - for details.

      -
    -


    -

    -


    -

    -

    ResultSet + for details.

    +
+


+ +

+


+ +

+

ResultSet interface – replaces (with extended functionality) some functions of isc_stmt_handle. This interface is returned by openCursor() call -in IAttachment or IStatement. +in IAttachment or IStatement. All fetch calls except fetchNext() work only for bidirectional -(opened with CURSOR_TYPE_SCROLLABLE flag) result set.

-
    -
  1. int +(opened with CURSOR_TYPE_SCROLLABLE flag) result set.

    +
      +
    1. +

      int fetchNext(StatusType* status, void* message) – fetch next record, replaces isc_dsql_fetch(). This method (and other fetch methods) - returns completion code + returns completion code Status::RESULT_NO_DATA when EOF is reached, Status::RESULT_OK on - success.

      -
    2. int + success.

      +
    3. +

      int fetchPrior(StatusType* status, void* message) – fetch previous - record.

      -
    4. int + record.

      +
    5. +

      int fetchFirst(StatusType* status, void* message) – fetch first - record.

      -
    6. int - fetchLast(StatusType* status, void* message) – fetch last record.

      -
    7. int + record.

      +
    8. +

      int + fetchLast(StatusType* status, void* message) – fetch last record.

      +
    9. +

      int fetchAbsolute(StatusType* status, int position, void* message) – - fetch record by it's absolute position in result set.

      -
    10. int + fetch record by it's absolute position in result set.

      +
    11. +

      int fetchRelative(StatusType* status, int offset, void* message) – - fetch record by position relative to current.

      -
    12. FB_BOOLEAN - isEof(StatusType* status) – check for EOF.

      -
    13. FB_BOOLEAN - isBof(StatusType* status) – check for BOF.

      -
    14. IMessageMetadata* + fetch record by position relative to current.

      +
    15. +

      FB_BOOLEAN + isEof(StatusType* status) – check for EOF.

      +
    16. +

      FB_BOOLEAN + isBof(StatusType* status) – check for BOF.

      +
    17. +

      IMessageMetadata* getMetadata(StatusType* status) – get metadata for messages in result set, specially useful when result set is opened by - IAttachment::openCursor() call with NULL + IAttachment::openCursor() call with NULL output metadata format parameter (this is the only way to obtain - message format in this case).

      -
    18. void + message format in this case).

      +
    19. +

      void close(StatusType* status) – close result set, releases interface - on success.

      -
    -


    -

    -


    -

    -

    Service -interface – replaces isc_svc_handle.

    -
      -
    1. void + on success.

      +
    +


    + +

    +


    + +

    +

    Service +interface – replaces isc_svc_handle.

    +
      +
    1. +

      void detach(StatusType* status) – close attachment to services manager, - on success releases interface. Replaces isc_service_detach().

      -
    2. void + on success releases interface. Replaces isc_service_detach().

      +
    3. +

      void query(StatusType* status, unsigned sendLength, const unsigned char* sendItems, unsigned receiveLength, const unsigned char* receiveItems, unsigned bufferLength, unsigned char* buffer) – send and request information to/from service, with different receiveItems may be used for both running services and to obtain various - server-wide information. Replaces isc_service_query().

      -
    4. void + server-wide information. Replaces isc_service_query().

      +
    5. +

      void start(StatusType* status, unsigned spbLength, const unsigned char* spb) – start utility in services manager. Replaces - isc_service_start().

      -
    -


    -

    -


    -

    -

    Statement -interface – replaces (partially) isc_stmt_handle.

    -
      -
    1. void + isc_service_start().

      +
    +


    + +

    +


    + +

    +

    Statement +interface – replaces (partially) isc_stmt_handle.

    +
      +
    1. +

      void getInfo(StatusType* status, unsigned itemsLength, const unsigned char* items, unsigned bufferLength, unsigned char* buffer) – - replaces isc_dsql_sql_info().

      -
    2. unsigned + replaces isc_dsql_sql_info().

      +
    3. +

      unsigned getType(StatusType* status) – statement type, currently can be - found only in firebird sources in dsql/dsql.h.

      -
    4. const char* - getPlan(StatusType* status, FB_BOOLEAN detailed) – returns - statement execution plan.

      -
    5. ISC_UINT64 + found only in firebird sources in dsql/dsql.h.

      +
    6. +

      const + char* getPlan(StatusType* status, FB_BOOLEAN detailed) – returns + statement execution plan.

      +
    7. +

      ISC_UINT64 getAffectedRecords(StatusType* status) – returns number of records - affected by statement.

      -
    8. IMessageMetadata* + affected by statement.

      +
    9. +

      IMessageMetadata* getInputMetadata(StatusType* status) – returns parameters - metadata.

      -
    10. IMessageMetadata* + metadata.

      +
    11. +

      IMessageMetadata* getOutputMetadata(StatusType* status) – returns output values - metadata.

      -
    12. ITransaction* + metadata.

      +
    13. +

      ITransaction* execute(StatusType* status, ITransaction* transaction, IMessageMetadata* inMetadata, void* inBuffer, IMessageMetadata* outMetadata, void* outBuffer) – executes any SQL statement except returning multiple rows of data. Partial analogue of isc_dsql_execute2() - in and out XSLQDAs replaced with input and - output messages with appropriate buffers.

      -
    14. IResultSet* + output messages with appropriate buffers.

      +
    15. +

      IResultSet* openCursor(StatusType* status, ITransaction* transaction, IMessageMetadata* inMetadata, void* inBuffer, IMessageMetadata* outMetadata, unsigned flags) – executes SQL statement potentially @@ -1929,352 +2273,450 @@ interface – replaces (partially) isc_stmt_handle.

      should be used to fetch that data. Format of output data is defined by outMetadata parameter, leaving it NULL default format may be used. Parameter flags is needed to open bidirectional cursor setting - it's value to Istatement::CURSOR_TYPE_SCROLLABLE.

      -
    16. void + it's value to Istatement::CURSOR_TYPE_SCROLLABLE.

      +
    17. +

      void setCursorName(StatusType* status, const char* name) – replaces - isc_dsql_set_cursor_name(). -

      -
    18. void free(StatusType* - status) – free statement, releases interface on success.

      -
    19. unsigned - getFlags(StatusType* status) – returns flags + isc_dsql_set_cursor_name(). +

      +
    20. +

      void + free(StatusType* status) – free statement, releases interface on + success.

      +
    21. +

      unsigned + getFlags(StatusType* status) – returns flags describing how this statement should be executed, simplified - replacement of getType() method.

      -
    -


    -

    -


    -

    -

    Constants defined by -Statement interface:

    -


    -

    -

    IAttachment::prepare() -flags:

    -

    -PREPARE_PREFETCH_NONE – constant to pass no flags, 0 -value.

    -

    The following flags may be -OR-ed to get desired set of flags:

    -
      -
    1. PREPARE_PREFETCH_TYPE

      -
    2. PREPARE_PREFETCH_INPUT_PARAMETERS

      -
    3. PREPARE_PREFETCH_OUTPUT_PARAMETERS

      -
    4. PREPARE_PREFETCH_LEGACY_PLAN

      -
    5. PREPARE_PREFETCH_DETAILED_PLAN

      -
    6. PREPARE_PREFETCH_AFFECTED_RECORDS

      -
    7. PREPARE_PREFETCH_FLAGS - (flags returned by getFlags() method)

      -
    -

    Frequently used -combinations of flags:

    -
      -
    1. PREPARE_PREFETCH_METADATA

      -
    2. PREPARE_PREFETCH_ALL -

      -
    -


    -

    -

    -Values returned by getFlags() method: -

    -

    FLAG_HAS_CURSOR – use -openCursor() to execute this statement, not execute()

    -

    FLAG_REPEAT_EXECUTE – -when prepared statement may be executed many times with different -parameters

    -


    -

    -

    -Flags passed to openCursor():

    -

    CURSOR_TYPE_SCROLLABLE – -open bidirectional cursor.

    -


    -

    -


    -

    -

    Status + replacement of getType() method.

    +
+


+ +

+


+ +

+

Constants +defined by Statement interface:

+


+ +

+

IAttachment::prepare() +flags:

+

+PREPARE_PREFETCH_NONE – +constant to pass no flags, 0 value.

+

The +following flags may be OR-ed to get desired set of flags:

+
    +
  1. +

    PREPARE_PREFETCH_TYPE

    +
  2. +

    PREPARE_PREFETCH_INPUT_PARAMETERS

    +
  3. +

    PREPARE_PREFETCH_OUTPUT_PARAMETERS

    +
  4. +

    PREPARE_PREFETCH_LEGACY_PLAN

    +
  5. +

    PREPARE_PREFETCH_DETAILED_PLAN

    +
  6. +

    PREPARE_PREFETCH_AFFECTED_RECORDS

    +
  7. +

    PREPARE_PREFETCH_FLAGS + (flags returned by getFlags() method)

    +
+

Frequently +used combinations of flags:

+
    +
  1. +

    PREPARE_PREFETCH_METADATA

    +
  2. +

    PREPARE_PREFETCH_ALL + +

    +
+


+ +

+

+Values returned by getFlags() +method: +

+

FLAG_HAS_CURSOR +– use openCursor() to execute this statement, not execute()

+

FLAG_REPEAT_EXECUTE +– when prepared statement may be executed many times with different +parameters

+


+ +

+

+Flags passed to openCursor():

+

CURSOR_TYPE_SCROLLABLE +– open bidirectional cursor.

+


+ +

+


+ +

+

Status interface – replaces ISC_STATUS_ARRAY. Functionality is extended – Status has separate access to errors and warnings vectors, can hold vectors of unlimited length, itself stores strings used in vectors avoiding need in circular strings buffer. In C++ Status is always -used under status wrapper, C++ API provides two different wrappers +used under status wrapper, C++ API provides two different wrappers having different behavior when error is returned from API call. Interface is on purpose minimized (methods like convert it to text -are moved to Util interface) in order to simplify -it's implementation by users when needed.

-
    -
  1. void init() - cleanup - interface, set it to initial state.

    -
  2. unsigned getState() - - get current state of interface, returns state - flags, may be OR-ed.

    -
  3. void +are moved to Util interface) in order to simplify +it's implementation by users when needed.

    +
      +
    1. +

      void + init() - cleanup interface, set it to initial state.

      +
    2. +

      unsigned + getState() - get current state of interface, returns state + flags, may be OR-ed.

      +
    3. +

      void setErrors2(unsigned length, const intptr_t* value) – set contents - of errors vector with length explicitly specified in a call.

      -
    4. void + of errors vector with length explicitly specified in a call.

      +
    5. +

      void setWarnings2(unsigned length, const intptr_t* value) – set contents of warnings vector with length explicitly specified in a - call.

      -
    6. void setErrors(const - intptr_t* value) – set contents of errors vector, length is - defined by value context.

      -
    7. void + call.

      +
    8. +

      void + setErrors(const intptr_t* value) – set contents of errors vector, + length is defined by value context.

      +
    9. +

      void setWarnings(const intptr_t* value) – set contents of warnings - vector, length is defined by value context.

      -
    10. const intptr_t* - getErrors() - get errors vector.

      -
    11. const intptr_t* - getWarnings() - get warnings vector.

      -
    12. IStatus* clone() - - create clone of current interface.

      -
    -


    -

    -

    Constants defined by -Status interface:

    -


    -

    -

    Flags -set in the value, returned by getState() method:

    -

    STATE_WARNINGS

    -

    STATE_ERRORS

    -


    -

    -

    Completion -codes:

    -

    RESULT_ERROR

    -

    RESULT_OK

    -

    RESULT_NO_DATA

    -

    RESULT_SEGMENT

    -


    -

    -


    -

    -

    Timer + vector, length is defined by value context.

    +
  4. +

    const + intptr_t* getErrors() - get errors vector.

    +
  5. +

    const + intptr_t* getWarnings() - get warnings vector.

    +
  6. +

    IStatus* + clone() - create clone of current interface.

    +
+


+ +

+

Constants +defined by Status interface:

+


+ +

+

Flags +set in the value, returned by getState() method:

+

STATE_WARNINGS

+

STATE_ERRORS

+


+ +

+

Completion +codes:

+

RESULT_ERROR

+

RESULT_OK

+

RESULT_NO_DATA

+

RESULT_SEGMENT

+


+ +

+


+ +

+

Timer interface – user timer. Callback interface which should be -implemented by user to use firebird timer.

-
    -
  1. void handler() - - method is called when timer rings (or when server is shutting down).

    -
-


-

-


-

-

TimerControl +implemented by user to use firebird timer.

+
    +
  1. +

    void + handler() - method is called when timer rings (or when server is + shutting down).

    +
+


+ +

+


+ +

+

TimerControl interface – very simple and not too precise implementation of timer. Arrived here because existing timers are very much OS dependent and may be used in programs that require to be portable and do not need really high precision timer. Particularly execution of given timer may be delayed if another one has not completed at the -moment when given timer should alarm.

-
    -
  1. void +moment when given timer should alarm.

    +
      +
    1. +

      void start(StatusType* status, ITimer* timer, ISC_UINT64 microSeconds) – - start ITimer to alarm after given delay (in - microseconds, 10-6 seconds). - Timer will be waked up only once after this call.

      -
    2. void stop(StatusType* - status, ITimer* timer) – stop ITimer. It's - not an error to stop not started timer thus avoiding problems with - races between stop() and timer alarm.

      -
    -


    -

    -

    Transaction -interface – replaces isc_tr_handle.

    -
      -
    1. void + start ITimer to alarm after given delay (in + microseconds, 10-6 + seconds). Timer will be waked + up only once after this call.

      +
    2. +

      void + stop(StatusType* status, ITimer* timer) – stop ITimer. + It's not an error to stop not started timer thus avoiding problems + with races between stop() and timer alarm.

      +
    +


    + +

    +

    Transaction +interface – replaces isc_tr_handle.

    +
      +
    1. +

      void getInfo(StatusType* status, unsigned itemsLength, const unsigned char* items, unsigned bufferLength, unsigned char* buffer) – - replaces isc_transaction_info().

      -
    2. void + replaces isc_transaction_info().

      +
    3. +

      void prepare(StatusType* status, unsigned msgLength, const unsigned char* - message) – replaces isc_prepare_transaction2().

      -
    4. void - commit(StatusType* status) – replaces isc_commit_transaction().

      -
    5. void + message) – replaces isc_prepare_transaction2().

      +
    6. +

      void + commit(StatusType* status) – replaces isc_commit_transaction().

      +
    7. +

      void commitRetaining(StatusType* status) – replaces - isc_commit_retaining().

      -
    8. void + isc_commit_retaining().

      +
    9. +

      void rollback(StatusType* status) – replaces - isc_rollback_transaction().

      -
    10. void + isc_rollback_transaction().

      +
    11. +

      void rollbackRetaining(StatusType* status) – replaces - isc_rollback_retaining().

      -
    12. void + isc_rollback_retaining().

      +
    13. +

      void disconnect(StatusType* status) – replaces - fb_disconnect_transaction().

      -
    14. ITransaction* + fb_disconnect_transaction().

      +
    15. +

      ITransaction* join(StatusType* status, ITransaction* transaction) – joins current transaction and passed as parameter transaction into single - distributed transaction (using Dtc). On success + distributed transaction (using Dtc). On success both current transaction and passed as parameter transaction are - released and should not be used any more.

      -
    16. ITransaction* + released and should not be used any more.

      +
    17. +

      ITransaction* validate(StatusType* status, IAttachment* attachment) – this - method is used to support distributed transactions coordinator.

      -
    18. ITransaction* + method is used to support distributed transactions coordinator.

      +
    19. +

      ITransaction* enterDtc(StatusType* status) – this method is used to support - distributed transactions coordinator.

      -
    -


    -

    -


    -

    -

    VersionCallback -interface – callback for Util::getFbVersion().

    -
      -
    1. void + distributed transactions coordinator.

      +
    +


    + +

    +


    + +

    +

    VersionCallback +interface – callback for Util::getFbVersion().

    +
      +
    1. +

      void callback(StatusType* status, const char* text) – called by firebird engine for each line in multiline version report. Makes it possible to print that lines one by one, place them into message box - in any GUI, etc.

      -
    -


    -

    -


    -

    -

    Util -interface – various helper methods required here or there.

    -
      -
    1. void + in any GUI, etc.

      +
    +


    + +

    +


    + +

    +

    Util +interface – various helper methods required here or there.

    +
      +
    1. +

      void getFbVersion(StatusType* status, IAttachment* att, IVersionCallback* callback) – produce long and beautiful report about firebird - version used. It may be seen in ISQL when invoked with -Z switch.

      -
    2. void + version used. It may be seen in ISQL when invoked with -Z switch.

      +
    3. +

      void loadBlob(StatusType* status, ISC_QUAD* blobId, IAttachment* att, ITransaction* tra, const char* file, FB_BOOLEAN txt) – load blob - from file.

      -
    4. void + from file.

      +
    5. +

      void dumpBlob(StatusType* status, ISC_QUAD* blobId, IAttachment* att, ITransaction* tra, const char* file, FB_BOOLEAN txt) – save blob - to file.

      -
    6. void + to file.

      +
    7. +

      void getPerfCounters(StatusType* status, IAttachment* att, const char* countersSet, ISC_INT64* counters) – get statistics for given - attachment.

      -
    8. IAttachment* + attachment.

      +
    9. +

      IAttachment* executeCreateDatabase(StatusType* status, unsigned stmtLength, const char* creatDBstatement, unsigned dialect, FB_BOOLEAN* stmtIsCreateDb) – execute “CREATE DATABASE ...” statement – - ISC trick with NULL statement handle does not work with interfaces.

      -
    10. void + ISC trick with NULL statement handle does not work with interfaces.

      +
    11. +

      void decodeDate(ISC_DATE date, unsigned* year, unsigned* month, unsigned* - day) – replaces isc_decode_sql_date().

      -
    12. void + day) – replaces isc_decode_sql_date().

      +
    13. +

      void decodeTime(ISC_TIME time, unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* fractions) – replaces - isc_decode_sql_time().

      -
    14. ISC_DATE + isc_decode_sql_time().

      +
    15. +

      ISC_DATE encodeDate(unsigned year, unsigned month, unsigned day) – replaces - isc_encode_sql_date().

      -
    16. ISC_TIME + isc_encode_sql_date().

      +
    17. +

      ISC_TIME encodeTime(unsigned hours, unsigned minutes, unsigned seconds, - unsigned fractions) – replaces isc_encode_sql_time().

      -
    18. unsigned + unsigned fractions) – replaces isc_encode_sql_time().

      +
    19. +

      unsigned formatStatus(char* buffer, unsigned bufferSize, IStatus* status) – replaces fb_interpret(). Size of buffer, passed into this method, - should not be less than 50 bytes.

      -
    20. unsigned + should not be less than 50 bytes.

      +
    21. +

      unsigned getClientVersion() – returns integer, containing major version in - byte 0 and minor version in byte 1.

      -
    22. IXpbBuilder* + byte 0 and minor version in byte 1.

      +
    23. +

      IXpbBuilder* getXpbBuilder(StatusType* status, unsigned kind, const unsigned - char* buf, unsigned len) – returns XpbBuilder - interface. Valid kinds are - enumerated in XpbBuilder.

      -
    24. unsigned + char* buf, unsigned len) – returns XpbBuilder + interface. Valid kinds are + enumerated in XpbBuilder.

      +
    25. +

      unsigned setOffsets(StatusType* status, IMessageMetadata* metadata, IOffsetsCallback* callback) – sets valid offsets in - MessageMetadata. Performs calls to - callback in OffsetsCallback for each - field/parameter.

      -
    -


    -

    -


    -

    -

    XpbBuilder -methods:

    -
      -
    1. void - clear(StatusType* status) – reset builder to empty state.

      -
    2. void - removeCurrent(StatusType* status) – removes current clumplet.

      -
    3. void + MessageMetadata. Performs calls to + callback in OffsetsCallback for each + field/parameter.

      +
    +


    + +

    +


    + +

    +

    XpbBuilder +methods:

    +
      +
    1. +

      void + clear(StatusType* status) – reset builder to empty state.

      +
    2. +

      void + removeCurrent(StatusType* status) – removes current clumplet.

      +
    3. +

      void insertInt(StatusType* status, unsigned char tag, int value) – inserts a clumplet with value representing integer in network - format.

      -
    4. void + format.

      +
    5. +

      void insertBigInt(StatusType* status, unsigned char tag, ISC_INT64 value) – inserts a clumplet with value representing integer in network - format.

      -
    6. void + format.

      +
    7. +

      void insertBytes(StatusType* status, unsigned char tag, const void* bytes, unsigned length) - inserts a clumplet with value containing - passed bytes.

      -
    8. void + passed bytes.

      +
    9. +

      void insertTag(StatusType* status, unsigned char tag) – inserts a - clumplet without a value.

      -
    10. FB_BOOLEAN + clumplet without a value.

      +
    11. +

      FB_BOOLEAN isEof(StatusType* status) – checks that there is no current - clumplet.

      -
    12. void - moveNext(StatusType* status) – moves to next clumplet.

      -
    13. void - rewind(StatusType* status) – moves to first clumplet.

      -
    14. FB_BOOLEAN + clumplet.

      +
    15. +

      void + moveNext(StatusType* status) – moves to next clumplet.

      +
    16. +

      void + rewind(StatusType* status) – moves to first clumplet.

      +
    17. +

      FB_BOOLEAN findFirst(StatusType* status, unsigned char tag) – finds first - clumplet with given tag.

      -
    18. FB_BOOLEAN - findNext(StatusType* status) – finds next clumplet with given tag.

      -
    19. unsigned char - getTag(StatusType* status) – returns tag for current clumplet.

      -
    20. unsigned + clumplet with given tag.

      +
    21. +

      FB_BOOLEAN + findNext(StatusType* status) – finds next clumplet with given tag.

      +
    22. +

      unsigned + char getTag(StatusType* status) – returns tag for current + clumplet.

      +
    23. +

      unsigned getLength(StatusType* status) – returns length of current clumplet - value.

      -
    24. int + value.

      +
    25. +

      int getInt(StatusType* status) – returns value of current clumplet as - integer.

      -
    26. ISC_INT64 + integer.

      +
    27. +

      ISC_INT64 getBigInt(StatusType* status) – returns value of current clumplet - as 64-bit integer.

      -
    28. const char* - getString(StatusType* status) – returns value of current clumplet - as pointer to zero-terminated string (pointer is valid till next - call to this method).

      -
    29. const unsigned char* - getBytes(StatusType* status) – returns value of current clumplet - as pointer to unsigned char.

      -
    30. unsigned + as 64-bit integer.

      +
    31. +

      const + char* getString(StatusType* status) – returns value of current + clumplet as pointer to zero-terminated string (pointer is valid till + next call to this method).

      +
    32. +

      const + unsigned char* getBytes(StatusType* status) – returns value of + current clumplet as pointer to unsigned char.

      +
    33. +

      unsigned getBufferLength(StatusType* status) – returns length of parameters - block.

      -
    34. const unsigned char* - getBuffer(StatusType* status) – returns pointer to parameters - block.

      -
    -


    -

    -

    Constants defined by -XpbBuilder interface:

    -


    -

    -

    Valid -builder types:

    -

    DPB

    -

    SPB_ATTACH

    -

    SPB_START

    -

    TPB

    -


    -

    -

    Plugin, encrypting data transferred over the wire.

    -

    Algorithms performing -encryption of data for different purposes are well known for many -years. The only “little” typical problem remaining is where to -get the top secret key to be used by that algorithm. Luckily for -network traffic encryption there is one good solution – unique + block.

    +
  2. +

    const + unsigned char* getBuffer(StatusType* status) – returns pointer to + parameters block.

    +
+


+ +

+

Constants +defined by XpbBuilder interface:

+


+ +

+

Valid +builder types:

+

DPB

+

SPB_ATTACH

+

SPB_START

+

TPB

+


+ +

+

Plugin, encrypting data +transferred over the wire.

+

Algorithms +performing encryption of data for different purposes are well known +for many years. The only “little” typical problem remaining is +where to get the top secret key to be used by that algorithm. Luckily +for network traffic encryption there is one good solution – unique encryption key should be generated by authentication plugin. At least default SRP plugin can produce such a key. And that key is resistant to attacks, including man-in-the-middle. Therefore was chosen a @@ -2282,58 +2724,71 @@ method of providing keys for wire crypt plugin: get it from authentication plugin. (In case when used authentication plugin can not provide a key a pseudo-plugin may be added in AuthClient and AuthServer lists to produce keys, something like two asymmetric -private/public pairs.) -

-


-

-

CryptKey interface is used -to store a key provided by authentication plugin and pass it to wire -crypt plugin. This interface should be used as follows – when -server or client authentication plugin is ready to provide a key it -asks ServerBlock or ClientBlock -to produce new CryptKey interface and stores a key in it. Appropriate -for WireCryptPlugin type of key will -be selected by firebird and passed to that interface.

-
    -
  1. void +private/public pairs.) +

    +


    + +

    +

    CryptKey +interface is used to store a key provided by authentication plugin +and pass it to wire crypt plugin. This interface should be used as +follows – when server or client authentication plugin is ready to +provide a key it asks ServerBlock or +ClientBlock to produce new CryptKey +interface and stores a key in it. Appropriate for WireCryptPlugin +type of key will be selected by firebird and passed to that +interface.

    +
      +
    1. +

      void setSymmetric(StatusType* status, const char* type, unsigned keyLength, const void* key) – make it store symmetric key of given - type.

      -
    2. void + type.

      +
    3. +

      void setAsymmetric(StatusType* status, const char* type, unsigned encryptKeyLength, const void* encryptKey, unsigned decryptKeyLength, const void* decryptKey) – make it store pair of asymmetric keys of - given type.

      -
    4. const void* - getEncryptKey(unsigned* length) – get a key for encryption.

      -
    5. const void* - getDecryptKey(unsigned* length) – get a key for decryption (in - case of symmetric key produces same result as getEncryptKey()).

      -
    -


    -

    -

    WireCryptPlugin + given type.

    +
  2. +

    const + void* getEncryptKey(unsigned* length) – get a key for encryption.

    +
  3. +

    const + void* getDecryptKey(unsigned* length) – get a key for decryption + (in case of symmetric key produces same result as getEncryptKey()).

    +
+


+ +

+

WireCryptPlugin interface is main interface of network crypt plugin. Like any other -such interface it should be implemented by author of the plugin.

-
    -
  1. const char* - getKnownTypes(StatusType* status) – returns whitespace/tab/comma - separated list of acceptable keys.

    -
  2. void +such interface it should be implemented by author of the plugin.

    +
      +
    1. +

      const + char* getKnownTypes(StatusType* status) – returns + whitespace/tab/comma separated list of acceptable keys.

      +
    2. +

      void setKey(StatusType* status, ICryptKey* key) – plugin should use a - key passed to it by this call.

      -
    3. void + key passed to it by this call.

      +
    4. +

      void encrypt(StatusType* status, unsigned length, const void* from, void* - to) – encrypts a packet to be sent over the wire.

      -
    5. void + to) – encrypts a packet to be sent over the wire.

      +
    6. +

      void decrypt(StatusType* status, unsigned length, const void* from, void* - to) – decrypts a packet received from network.

      -
    -


    -

    -

    Server side of authentication plugin.

    -

    Authentication plugin -contains two required parts – client and server and may also + to) – decrypts a packet received from network.

    +
+


+ +

+

Server side of +authentication plugin.

+

Authentication +plugin contains two required parts – client and server and may also contain related third part - user manager. During authentication process firebird client invokes client plugin and sends generated by it data to server, next server invokes server plugin and sends @@ -2342,464 +2797,577 @@ plugins return AUTH_MORE_DATA code. AUTH_SUCCESS returned at server side means successful authentication, AUTH_FAILED at any side – immediate abort of iterative process and failure reported to client, AUTH_CONTINUE means that next plugin from the list of configured -authentication plugins should be tried.

-


-

-

There is no dedicated -sample of authentication plugins but in firebird sources in directory -src/auth one can find AuthDbg plugin using which one can learn on -trivial example (no complex calculations like in Srp and no calls to -crazy WinAPI functions like in AuthSspi) how client and server sides -perform authentication handshake. -

-


-

-

Auth interface does not -contain methods, only some constants defining codes return from -authenticate() method of Client and Server.

-

AUTH_FAILED

-

AUTH_SUCCESS

-

AUTH_MORE_DATA

-

AUTH_CONTINUE

-


-

-

Writer -interface – writes authentication parameters block.

-
    -
  1. void reset() - clear - target block.

    -
  2. void add(StatusType* - status, const char* name) – add login name.

    -
  3. void +authentication plugins should be tried.

    +


    + +

    +

    There +is no dedicated sample of authentication plugins but in firebird +sources in directory src/auth one can find AuthDbg plugin using which +one can learn on trivial example (no complex calculations like in Srp +and no calls to crazy WinAPI functions like in AuthSspi) how client +and server sides perform authentication handshake. +

    +


    + +

    +

    Auth +interface does not contain methods, only some constants defining +codes return from authenticate() method of Client +and Server.

    +

    AUTH_FAILED

    +

    AUTH_SUCCESS

    +

    AUTH_MORE_DATA

    +

    AUTH_CONTINUE

    +


    + +

    +

    Writer +interface – writes authentication parameters block.

    +
      +
    1. +

      void + reset() - clear target block.

      +
    2. +

      void + add(StatusType* status, const char* name) – add login name.

      +
    3. +

      void setType(StatusType* status, const char* value) – set type of added - login (user, group, etc).

      -
    4. void + login (user, group, etc).

      +
    5. +

      void setDb(StatusType* status, const char* value) – set security - database in which authentication was done.

      -
    -


    -

    -

    ServerBlock + database in which authentication was done.

    +
+


+ +

+

ServerBlock interface is used by server side of authentication plugin to exchange -data with client.

-
    -
  1. const char* - getLogin() - get login name passed from client.

    -
  2. const unsigned char* - getData(unsigned* length) – get authentication data passed from - client.

    -
  3. void +data with client.

    +
      +
    1. +

      const + char* getLogin() - get login name passed from client.

      +
    2. +

      const + unsigned char* getData(unsigned* length) – get authentication data + passed from client.

      +
    3. +

      void putData(StatusType* status, unsigned length, const void* data) – - pass authentication data to client.

      -
    4. ICryptKey* + pass authentication data to client.

      +
    5. +

      ICryptKey* newKey(StatusType* status) – create new wire crypt key and add it - to the list of available for wire crypt plugins.

      -
    -


    -

    -

    Server -interface is main interface of server side of authentication plugin. -

    -
      -
    1. int + to the list of available for wire crypt plugins.

      +
    +


    + +

    +

    Server +interface is main interface of server side of authentication plugin. +

    +
      +
    1. +

      int authenticate(StatusType* status, IServerBlock* sBlock, IWriter* writerInterface) – perform single authentication step. Data exchange with client is performed using sBlock interface. When some authentication item is produced it should be added to authentication block using writerInterface. Possible return values are defined in - Auth interface.

      -
    -


    -

    -

    Client side of authentication plugin.

    -

    ClientBlock + Auth interface.

    +
+


+ +

+

Client side of +authentication plugin.

+

ClientBlock interface is used by client side of authentication plugin to exchange -data with server.

-
    -
  1. const char* - getLogin() - get login name if it is present in DPB.

    -
  2. const char* - getPassword() - get password if it is present in DPB.

    -
  3. const unsigned char* - getData(unsigned* length) – get authentication data passed from - server.

    -
  4. void +data with server.

    +
      +
    1. +

      const + char* getLogin() - get login name if it is present in DPB.

      +
    2. +

      const + char* getPassword() - get password if it is present in DPB.

      +
    3. +

      const + unsigned char* getData(unsigned* length) – get authentication data + passed from server.

      +
    4. +

      void putData(StatusType* status, unsigned length, const void* data) – - pass authentication data to server.

      -
    5. ICryptKey* + pass authentication data to server.

      +
    6. +

      ICryptKey* newKey(StatusType* status) - create new wire crypt key and add it to - the list of available for wire crypt plugins.

      -
    -


    -

    -

    Client -interface is main interface of client side of authentication plugin.

    -
      -
    1. int + the list of available for wire crypt plugins.

      +
    +


    + +

    +

    Client +interface is main interface of client side of authentication plugin.

    +
      +
    1. +

      int authenticate(StatusType* status, IClientBlock* cBlock)1. – perform single authentication step. Data exchange with server is performed using cBlock interface. Possible return values are defined in Auth interface. AUTH_SUCCESS is treated by client side as AUTH_MORE_DATA (i.e. client sends generated data to server and waits for an answer - from it).

      -
    -


    -

    -

    User management plugin.

    -

    This plugin is actively -related with server side of authentication – it prepares users' -list for authentication plugin. Not each authentication plugin -requires user manager – some may access list of users created using -non-firebird software (AuthSspi for example). Record describing user -consists of a number of fields and operation which should be -performed like add user, modify user, list user(s), etc. Plugin must -interpret commands received in User interface.

    -


    -

    -

    UserField + from it).

    +
+


+ +

+

User management plugin.

+

This +plugin is actively related with server side of authentication – it +prepares users' list for authentication plugin. Not each +authentication plugin requires user manager – some may access list +of users created using non-firebird software (AuthSspi for example). +Record describing user consists of a number of fields and operation +which should be performed like add user, modify user, list user(s), +etc. Plugin must interpret commands received in User +interface.

+


+ +

+

UserField interface is not used as standalone interface, it's base for -CharUserField and IntUserField. -

-
    -
  1. int entered() - - returns non-zero if a value for a field was entered (assigned).

    -
  2. int specified() - - return non-zero if NULL value was assigned to the field.

    -
  3. void +CharUserField and IntUserField. +

    +
      +
    1. +

      int + entered() - returns non-zero if a value for a field was entered + (assigned).

      +
    2. +

      int + specified() - return non-zero if NULL value was assigned to the + field.

      +
    3. +

      void setEntered(StatusType* status, int newValue) – sets entered flag to 0/non-zero value for a field. There is no method to assign NULL for a field cause it's never needed. NULLs if used are assigned by the code implementing interfaces and therefore having full access to - internals of them.

      -
    -


    -

    -

    CharUserField -interface:

    -
      -
    1. const char* get() - - get field's value as C-string (\0 terminated).

      -
    2. void set(StatusType* - status, const char* newValue) – assigns value to the field. Sets - entered flag to true.

      -
    -


    -

    -

    IntUserField -interface:

    -
      -
    1. int get() - get - field's value.

      -
    2. void set(StatusType* - status, int newValue) – assigns value to the field. Sets entered - flag to true.

      -
    -


    -

    -

    User + internals of them.

    +
+


+ +

+

CharUserField +interface:

+
    +
  1. +

    const + char* get() - get field's value as C-string (\0 terminated).

    +
  2. +

    void + set(StatusType* status, const char* newValue) – assigns value to + the field. Sets entered flag to true.

    +
+


+ +

+

IntUserField +interface:

+
    +
  1. +

    int + get() - get field's value.

    +
  2. +

    void + set(StatusType* status, int newValue) – assigns value to the + field. Sets entered flag to true.

    +
+


+ +

+

User interface is a list of methods accessing fields included into record -about the user. -

-
    -
  1. int operation() - - code of operation (see list - below).

    -
  2. ICharUserField* - userName() - login name.

    -
  3. ICharUserField* - password() - password. Always empty when listing users.

    -
  4. ICharUserField* - firstName() - this and 2 next are components of full user name.

    -
  5. ICharUserField* - lastName()

    -
  6. ICharUserField* - middleName()

    -
  7. ICharUserField* - comment() - comment (from SQL operator COMMENT ON USER IS …).

    -
  8. ICharUserField* +about the user. +

    +
      +
    1. +

      unsigned + operation() - code of operation (see list + below).

      +
    2. +

      ICharUserField* + userName() - login name.

      +
    3. +

      ICharUserField* + password() - password. Always empty when listing users.

      +
    4. +

      ICharUserField* + firstName() - this and 2 next are components of full user name.

      +
    5. +

      ICharUserField* + lastName()

      +
    6. +

      ICharUserField* + middleName()

      +
    7. +

      ICharUserField* + comment() - comment (from SQL operator COMMENT ON USER IS …).

      +
    8. +

      ICharUserField* attributes() - tags in a form tag1=val1, tag2=val2, …, tagN=valN. - Val may be empty – than means that tag will be deleted.

      -
    9. IIntUserField* - active() - changes ACTIVE/INACTIVE setting for user.

      -
    10. IIntUserField* - admin() - sets/drops admin rights for the user.

      -
    11. void + Val may be empty – than means that tag will be deleted.

      +
    12. +

      IIntUserField* + active() - changes ACTIVE/INACTIVE setting for user.

      +
    13. +

      IIntUserField* + admin() - sets/drops admin rights for the user.

      +
    14. +

      void clear(StatusType* status) – sets all fields to not entered and not - specified.

      -
    -


    -

    -

    -Constants defined by User interface – valid codes of -operation.

    -

    OP_USER_ADD – create -user

    -

    OP_USER_MODIFY – alter -user

    -

    OP_USER_DELETE – drop -user

    -

    OP_USER_DISPLAY – show -user

    -

    OP_USER_SET_MAP – turn -on mapping of windows admins to role rdb$admin

    -

    OP_USER_DROP_MAP – turn -off mapping of windows admins to role rdb$admin

    -


    -

    -

    ListUsers + specified.

    +
+


+ +

+

+Constants defined by User +interface – valid codes of operation.

+

OP_USER_ADD +– create user

+

OP_USER_MODIFY +– alter user

+

OP_USER_DELETE +– drop user

+

OP_USER_DISPLAY +– show user

+

OP_USER_SET_MAP +– turn on mapping of windows admins to role rdb$admin

+

OP_USER_DROP_MAP +– turn off mapping of windows admins to role rdb$admin

+


+ +

+

ListUsers interface is callback used by authentication plugin when list users -operation is requested. Plugin fills User +operation is requested. Plugin fills User interface for all items in list of users one by one and for each user -calls list() method of this interface.

-
    -
  1. void list(StatusType* - status, IUser* user) – callback function. Implementation can do - what it wants with received data. For example, it may put data from - user parameter into output stream of listing service or place into - special tables from SEC$ group.

    -
-


-

-

LogonInfo +calls list() method of this interface.

+
    +
  1. +

    void + list(StatusType* status, IUser* user) – callback function. + Implementation can do what it wants with received data. For example, + it may put data from user parameter into output stream of listing + service or place into special tables from SEC$ group.

    +
+


+ +

+

LogonInfo interface contains data passed to user mamngement plugin to attach to -security database with valid credentials. Pres

-
    -
  1. const char* name() - - returns current attachment's login name.

    -
  2. const char* role() - - returns current attachment's active role.

    -
  3. const char* - networkProtocol() - returns current attachment's network protocol. - Currently not used by plugins.

    -
  4. const char* - remoteAddress() - returns current attachment's remote address. - Currently not used by plugins.

    -
  5. const unsigned char* - authBlock(unsigned* length) – returns current attachment's - authentication block. When not NULL overrides login name.

    -
-


-

-

Management -interface is main interface of user management plugin.

-
    -
  1. void +security database with valid credentials. Pres

    +
      +
    1. +

      const + char* name() - returns current attachment's login name.

      +
    2. +

      const + char* role() - returns current attachment's active role.

      +
    3. +

      const + char* networkProtocol() - returns current attachment's network + protocol. Currently not used by plugins.

      +
    4. +

      const + char* remoteAddress() - returns current attachment's remote address. + Currently not used by plugins.

      +
    5. +

      const + unsigned char* authBlock(unsigned* length) – returns current + attachment's authentication block. When not NULL overrides login + name.

      +
    +


    + +

    +

    Management +interface is main interface of user management plugin.

    +
      +
    1. +

      void start(StatusType* status, ILogonInfo* logonInfo) – starts plugin, if needed it attaches to security database to manage may be (it's plugin-dependent solution use it or not) using credentials from - logonInfo. -

      -
    2. int + logonInfo. +

      +
    3. +

      int execute(StatusType* status, IUser* user, IListUsers* callback) – executes a command provided by operation() method of user parameter. If needed callback interface will be used. Parameter callback may - have NULL value for commands not requiring users' listing.

      -
    4. void + have NULL value for commands not requiring users' listing.

      +
    5. +

      void commit(StatusType* status) – commits changes done by calls to - execute() method.

      -
    6. void + execute() method.

      +
    7. +

      void rollback(StatusType* status) – rollbacks changes done by calls to - execute() method.

      -
    -


    -

    -

    Database encryption plugin.

    -

    An ability to encrypt -database was present in firebird since interbase times but -appropriate places in the code were commented. Implementation was -suspicious – crypt key was always sent from the client in DPB, no -attempt was made to hide it from the world and no way was suggested -to encrypt existing database. FB3 solves most of the problems except -probably the worst one – how to manage crypt keys. We suggest a -kind of solution but it requires efforts in plugins, i.e. there is no -beautiful way to work with keys like it is for wire crypt plugins.

    -


    -

    -

    Before starting with own -db crypt plugin one should take into an account the following. We see -two main usages of database encryption – first, it may be needed to -avoid data loss if database server is physically stolen, and second, -it may be used to protect data in database which is sailed as a -source of some information together with special application -accessing that data. Requirements for this usages are quite -different. In first case we may trust database server that it is not -modified to steal keys passed to security plugin – i.e. we expect -that key will not be sent to inappropriate server. In second case -server may be modified in some way to steal keys (if they are passed -from application to plugin via server code) or even data (as the last -resort to dump blocks from the cache where they are in non-encrypted -form). Therefore your plugin should make sure that it's running with -not modified firebird binaries and your application before sending a -key to plugin should make sure it's really required plugin, may be -asking a kind of digital signature from it. Making sure that network -line is encrypted (parsing output of Util::getFbVersion()) + execute() method.

    +
+


+ +

+

Database encryption +plugin.

+

An +ability to encrypt database was present in firebird since interbase +times but appropriate places in the code were commented. +Implementation was suspicious – crypt key was always sent from the +client in DPB, no attempt was made to hide it from the world and no +way was suggested to encrypt existing database. FB3 solves most of +the problems except probably the worst one – how to manage crypt +keys. We suggest a kind of solution but it requires efforts in +plugins, i.e. there is no beautiful way to work with keys like it is +for wire crypt plugins.

+


+ +

+

Before +starting with own db crypt plugin one should take into an account the +following. We see two main usages of database encryption – first, +it may be needed to avoid data loss if database server is physically +stolen, and second, it may be used to protect data in database which +is sailed together with special application accessing that data. +Requirements for this usages are quite different. In first case we +may trust database server that it is not modified to steal keys +passed to security plugin – i.e. we expect that key will not be +sent to inappropriate server. In second case server may be modified +in some way to steal keys (if they are passed from application to +plugin via server code) or even data (as the last resort to dump +blocks from the cache where they are in non-encrypted form). +Therefore your plugin should make sure that it's running with not +modified firebird binaries and your application before sending a key +to plugin should make sure it's really required plugin, may be asking +a kind of digital signature from it. Making sure that network line is +encrypted (parsing output of Util::getFbVersion()) or using some own encryption of a key is also very good idea in case when network access to the server is used. All this job should be done in plugin (and application working with it) i.e. database block encryption algorithm by itself may happen to be easiest part of db -crypt plugin.

-


-

-

CryptKeyCallback +crypt plugin, specially +when some standard library is used for it.

+


+ +

+

CryptKeyCallback interface should be provided by a side sending crypt key to db crypt -plugin or key holder plugin.

-
    -
  1. unsigned +plugin or key holder plugin.

    +
      +
    1. +

      unsigned callback(unsigned dataLength, const void* data, unsigned bufferLength, void* buffer) – when performing callback information is passed in both directions. The source of a key receives dataLength bytes of data and may send up to bufferLength bytes into - buffer returning actual number of bytes placed into buffer.

      -
    -


    -

    -

    DbCryptPlugin -interface is main interface of database crypt plugin.

    -
      -
    1. void + buffer returning actual number of bytes placed into buffer.

      +
    +


    + +

    +

    DbCryptPlugin +interface is main interface of database crypt plugin.

    +
      +
    1. +

      void setKey(StatusType* status, unsigned length, IKeyHolderPlugin** sources, const char* keyName) – is used to provide to db crypt plugin information about encryption key. Firebird never passes keys - for this type of plugin directly. Instead array of KeyHolderPlugins + for this type of plugin directly. Instead array of KeyHolderPlugins of given length is passed to crypt plugin which must get from one of - it CryptKeyCallback interface and + it CryptKeyCallback interface and get a key using it. Parameter keyName is a name of a key like it was - entered in “ALTER DATABASE ENCRYPT …” operator.

      -
    2. void + entered in “ALTER DATABASE ENCRYPT …” operator.

      +
    3. +

      void encrypt(StatusType* status, unsigned length, const void* from, void* - to)

      -
    4. void + to) – encrypts data before writing block to database file.

      +
    5. +

      void decrypt(StatusType* status, unsigned length, const void* from, void* - to)

      -
    -


    -

    -

    Key holder for database encryption plugin.

    -

    This type of plugin is -needed to delineate functionality – db crypt plugin is dealing with -actual encryption, key holder solves questions related with providing -it a key in secure way. Plugin may be received from application or -loaded in some other way (up to using flash device inserted into -server at firebird startup time).

    -


    -

    -

    KeyHolderPlugin -interface is main interface of database crypt key holder plugin.

    -
      -
    1. int + to) – decrypts data after reading block from database file.

      +
    +


    + +

    +

    Key holder for database +encryption plugin.

    +

    This +type of plugin is needed to delineate functionality – db crypt +plugin is dealing with actual encryption, key holder solves questions +related with providing it a key in secure way. Plugin may be received +from application or loaded in some other way (up to using flash +device inserted into server at firebird startup time).

    +


    + +

    +

    KeyHolderPlugin +interface is main interface of database crypt key holder plugin.

    +
      +
    1. +

      int keyCallback(StatusType* status, ICryptKeyCallback* callback) – is - used to pass attachment's CryptKeyCallback - interface (if provided by user with Provider::setDbCryptCallback() + used to pass attachment's CryptKeyCallback + interface (if provided by user with Provider::setDbCryptCallback() call). This call is always performed at database attach moment, and some holders may reject attachment if satisfactory key was not - provided. This makes it possible to let only specific applications - (i.e. available to provide needed crypt key) access database even if - required key was already provided by another attachment.

      -
    2. ICryptKeyCallback* + provided.

      +
    3. +

      ICryptKeyCallback* keyHandle(StatusType* status, const char* keyName) – is intended - to be called by DbCryptPlugin directly + to be called by DbCryptPlugin directly to obtain callback interface for named key from key holder. This makes it possible for open source firebird code to never touch actual keys avoiding ability to steal a key changing firebird code. - After getting CryptKeyCallback + After getting CryptKeyCallback interface crypt plugin starts data exchange using it. Key holder can (for example) check digital signature of crypt plugin before sending a key to it in order to avoid use of modified crypt plugin able to - steal secret key.

      -
    4. FB_BOOLEAN - useOnlyOwnKeys(StatusType* status) – informs firebird engine can a - key, provided by another key holder, be used or not. Makes sense - only for SuperServer – only it can share database crypt keys + steal secret key.

      +
    5. +

      FB_BOOLEAN + useOnlyOwnKeys(StatusType* status) – informs firebird engine + whether a key, provided by another key holder, be used or not. Makes + sense only for SuperServer – only it can share database crypt keys between attachments. Returning FB_TRUE from this method enforces firebird to make sure that this particular key holder (and therefore in turn attachment related to it) provides correct crypt key before - letting it to work with database.

      -
    -


    -

    -

    Non-interface objects used by API (C++ specific -header Message.h).

    -

    Following 3 classes are -used to represent date, time and timestamp (datetime) when using -FB_MESSAGE macro. Members of data structure, representing static -message, correspondint to fields of types FB_DATE / FB_TIME / + letting it to work with database.

    +
  2. +

    ICryptKeyCallback* + chainHandle(StatusType* status) – support of a chain of key holders. In + some cases key has to pass through more than single key holder + before it reaches db crypt plugin. This is needed (for example) to + support execute statement in encrypted database. This is just a + sample – chains are also used in some other cases. Callback + interface, returned by this method, may differ from one returned by + keyHandle() function (see above). Typically is should be able to + duplicate one-to-one keys, received by KeyHolderPlugin when + keyCallback() function is invoked.

    +
+


+ +

+

Non-interface objects used +by API (C++ specific header Message.h).

+

Following +3 classes are used to represent date, time and timestamp (datetime) +when using FB_MESSAGE macro. Members of data structure, representing +static message, correspondint to fields of types FB_DATE / FB_TIME / FB_TIMESTAMP will have a type of one of this classes. Knowing methods / members (which are enough self-descriptive to avoid describing them here) of this classes is needed to access date and time fields in -static messages.

-


-

-

class -FbDate methods:

-

void decode(IUtil* util, -unsigned* year, unsigned* month, unsigned* day)

-

unsigned getYear(IUtil* -util) -

-

unsigned getMonth(IUtil* -util)

-

unsigned getDay(IUtil* -util)

-

void encode(IUtil* util, -unsigned year, unsigned month, unsigned day)

-


-

-

class -FbTime methods:

-

void decode(IUtil* util, -unsigned* hours, unsigned* minutes, unsigned* seconds, unsigned* -fractions)

-

unsigned getHours(IUtil* -util)

-

unsigned getMinutes(IUtil* -util)

-

unsigned getSeconds(IUtil* -util)

-

unsigned -getFractions(IUtil* util)

-

void encode(IUtil* util, -unsigned hours, unsigned minutes, unsigned seconds, unsigned -fractions)

-


-

-

class FbTimestamp members:

-

FbDate date;

-

FbTime time;

-


-

-


-

-

Following two templates -are used in static messages to represent CHAR(N) and VARCHAR(N) -fields. -

-


-

-

template <unsigned N>

-

struct -FbChar

-

{

-

char str[N];

-

};

-


-

-

template <unsigned N>

-

struct -FbVarChar

-

{

-

ISC_USHORT length;

-

char str[N];

-

void set(const char* s);

-

};

-


-

-


-

-
-

This document is currently -missing 2 types of plugins – ExternalEngine and Trace. Information -about them will be made available in next release of it.

-


-

- - \ No newline at end of file +static messages.

+


+ +

+

class +FbDate methods:

+

void +decode(IUtil* util, unsigned* year, unsigned* month, unsigned* day)

+

unsigned +getYear(IUtil* util) +

+

unsigned +getMonth(IUtil* util)

+

unsigned +getDay(IUtil* util)

+

void +encode(IUtil* util, unsigned year, unsigned month, unsigned day)

+


+ +

+

class +FbTime methods:

+

void +decode(IUtil* util, unsigned* hours, unsigned* minutes, unsigned* +seconds, unsigned* fractions)

+

unsigned +getHours(IUtil* util)

+

unsigned +getMinutes(IUtil* util)

+

unsigned +getSeconds(IUtil* util)

+

unsigned +getFractions(IUtil* util)

+

void +encode(IUtil* util, unsigned hours, unsigned minutes, unsigned +seconds, unsigned fractions)

+


+ +

+

class +FbTimestamp members:

+

FbDate +date;

+

FbTime +time;

+


+ +

+


+ +

+

Following +two templates are used in static messages to represent CHAR(N) and +VARCHAR(N) fields. +

+


+ +

+

template +<unsigned N>

+

struct +FbChar

+

{

+

char +str[N];

+

};

+


+ +

+

template +<unsigned N>

+

struct +FbVarChar

+

{

+

ISC_USHORT +length;

+

char +str[N];

+

void +set(const char* s);

+

};

+


+ +

+


+ +

+
+ +

This +document is currently missing 2 types of plugins – ExternalEngine +and Trace. Information about them will be made available in next +release of it.

+


+ +

+ + \ No newline at end of file diff --git a/examples/dbcrypt/CryptApplication.cpp b/examples/dbcrypt/CryptApplication.cpp index 3a2b11d35a..16fff91c83 100644 --- a/examples/dbcrypt/CryptApplication.cpp +++ b/examples/dbcrypt/CryptApplication.cpp @@ -25,6 +25,7 @@ */ #include "../interfaces/ifaceExamples.h" +#include using namespace Firebird; @@ -79,7 +80,7 @@ public: status->dispose(); } - enum Action {NONE, ENC, DEC}; + enum Action {NONE, ENC, DEC, EX_LCL, EX_RMT}; void execute(const char* dbName, const Action a) { @@ -104,18 +105,55 @@ public: throw "startTransaction"; } - if (a == ENC) + switch(a) { + case ENC: att->execute(status, tra, 0, "ALTER DATABASE ENCRYPT WITH \"DbCrypt_example\"", 3, NULL, NULL, NULL, NULL); if (status->getState() & IStatus::STATE_ERRORS) throw "execute"; - } - if (a == DEC) - { + break; + + case DEC: att->execute(status, tra, 0, "ALTER DATABASE DECRYPT", 3, NULL, NULL, NULL, NULL); if (status->getState() & IStatus::STATE_ERRORS) throw "execute"; + break; + + case EX_LCL: + case EX_RMT: + { + FB_MESSAGE(Output, CheckStatusWrapper, + (FB_VARCHAR(31), logon) + ) output(status, master); + + const char* sqlL = "select current_user from rdb$database"; + const char* sqlR = "execute block returns(logon varchar(31)) as begin " + "execute statement 'select current_user from rdb$database' " + "on external 'localhost:employee' as user 'test' password 'test' into :logon; " + "suspend; end"; + const char* sql = a == EX_LCL ? sqlL : sqlR; + + curs = att->openCursor(status, tra, 0, sql, 3, NULL, NULL, output.getMetadata(), NULL, 0); + if (status->getState() & IStatus::STATE_ERRORS) + throw "openCursor"; + + printf("\nExec SQL: %s\nReturns:\n", sql); + while (curs->fetchNext(status, output.getData()) == IStatus::RESULT_OK) + { + unsigned l = output->logonNull ? 0 : output->logon.length; + printf("%*.*s\n", l, l, output->logon.str); + } + printf("done.\n"); + if (status->getState() & IStatus::STATE_ERRORS) + throw "fetchNext"; + + curs->close(status); + if (status->getState() & IStatus::STATE_ERRORS) + throw "close"; + curs = NULL; + break; + } } if (tra) @@ -126,7 +164,7 @@ public: tra = NULL; } - printf("Providing key for crypt plugin - press enter to continue ..."); + printf("\nProviding key for crypt plugin - press enter to continue ..."); getchar(); att->detach(status); @@ -151,13 +189,14 @@ private: IProvider* p; IAttachment* att; ITransaction* tra; + IResultSet* curs; CryptKey key; }; int usage() { - fprintf(stderr, "Usage: CryptApplication [ -e | -d ] { db-name }\n"); + fprintf(stderr, "Usage: cryptAppSample [ -e | -d | -l | -r ] { db-name }\n"); return 2; } @@ -181,6 +220,12 @@ int main(int ac, char** av) case 'd': act = App::DEC; break; + case 'l': + act = App::EX_LCL; + break; + case 'r': + act = App::EX_RMT; + break; default: return usage(); } diff --git a/examples/dbcrypt/CryptKeyHolder.cpp b/examples/dbcrypt/CryptKeyHolder.cpp index cff91c5def..f1c9d0d796 100644 --- a/examples/dbcrypt/CryptKeyHolder.cpp +++ b/examples/dbcrypt/CryptKeyHolder.cpp @@ -77,6 +77,7 @@ public: // IKeyHolderPlugin implementation int keyCallback(CheckStatusWrapper* status, ICryptKeyCallback* callback); ICryptKeyCallback* keyHandle(CheckStatusWrapper* status, const char* keyName); + ICryptKeyCallback* chainHandle(CheckStatusWrapper* status); int release() { @@ -200,8 +201,6 @@ IConfigEntry* CryptKeyHolder::getEntry(CheckStatusWrapper* status, const char* e int CryptKeyHolder::keyCallback(CheckStatusWrapper* status, ICryptKeyCallback* callback) { - status->init(); - if (key != 0) return 1; @@ -258,6 +257,12 @@ ICryptKeyCallback* CryptKeyHolder::keyHandle(CheckStatusWrapper* status, const c return NULL; } +ICryptKeyCallback* CryptKeyHolder::chainHandle(CheckStatusWrapper* status) +{ + return &callbackInterface; +} + + class Factory : public IPluginFactoryImpl { public: diff --git a/examples/dbcrypt/DbCrypt.cpp b/examples/dbcrypt/DbCrypt.cpp index ae20a28380..a8cc249c44 100644 --- a/examples/dbcrypt/DbCrypt.cpp +++ b/examples/dbcrypt/DbCrypt.cpp @@ -236,7 +236,9 @@ void DbCrypt::setKey(CheckStatusWrapper* status, unsigned int length, IKeyHolder return; if (callback && callback->callback(0, NULL, 1, &key) == 1) + { return; + } } key = 0; diff --git a/src/auth/AuthDbg.cpp b/src/auth/AuthDbg.cpp index 27da2387df..94d58be706 100644 --- a/src/auth/AuthDbg.cpp +++ b/src/auth/AuthDbg.cpp @@ -123,6 +123,9 @@ int DebugServer::authenticate(Firebird::CheckStatusWrapper* status, Firebird::IS return AUTH_FAILED; } +void DebugServer::setDbCryptCallback(Firebird::CheckStatusWrapper*, Firebird::ICryptKeyCallback*) +{ /* ignore it */ } + int DebugServer::release() { if (--refCounter == 0) diff --git a/src/auth/AuthDbg.h b/src/auth/AuthDbg.h index a6e00eda9a..3504be0428 100644 --- a/src/auth/AuthDbg.h +++ b/src/auth/AuthDbg.h @@ -54,6 +54,7 @@ public: int authenticate(Firebird::CheckStatusWrapper* status, Firebird::IServerBlock* sBlock, Firebird::IWriter* writerInterface); + void setDbCryptCallback(Firebird::CheckStatusWrapper*, Firebird::ICryptKeyCallback*); int release(); private: diff --git a/src/auth/SecureRemotePassword/server/SrpServer.cpp b/src/auth/SecureRemotePassword/server/SrpServer.cpp index 3e43866d46..34916a3f4a 100644 --- a/src/auth/SecureRemotePassword/server/SrpServer.cpp +++ b/src/auth/SecureRemotePassword/server/SrpServer.cpp @@ -55,7 +55,7 @@ public: : server(NULL), data(getPool()), account(getPool()), clientPubKey(getPool()), serverPubKey(getPool()), verifier(getPool()), salt(getPool()), sessionKey(getPool()), - secDbName(NULL) + secDbName(NULL), cryptCallback(NULL) { LocalStatus ls; CheckStatusWrapper s(&ls); @@ -65,6 +65,7 @@ public: // IServer implementation int authenticate(CheckStatusWrapper* status, IServerBlock* sBlock, IWriter* writerInterface); + void setDbCryptCallback(CheckStatusWrapper* status, ICryptKeyCallback* callback); int release(); private: @@ -82,6 +83,7 @@ private: UCharBuffer sessionKey; RefPtr config; const char* secDbName; + ICryptKeyCallback* cryptCallback; }; int SrpServer::authenticate(CheckStatusWrapper* status, IServerBlock* sb, IWriter* writerInterface) @@ -130,6 +132,12 @@ int SrpServer::authenticate(CheckStatusWrapper* status, IServerBlock* sb, IWrite try { + if (cryptCallback) + { + p->setDbCryptCallback(status, cryptCallback); + status->init(); // ignore possible errors like missing call in provider + } + ClumpletWriter dpb(ClumpletReader::dpbList, MAX_DPB_SIZE); dpb.insertByte(isc_dpb_sec_attach, TRUE); dpb.insertString(isc_dpb_user_name, DBA_USER_NAME, fb_strlen(DBA_USER_NAME)); @@ -287,6 +295,11 @@ int SrpServer::authenticate(CheckStatusWrapper* status, IServerBlock* sb, IWrite return AUTH_CONTINUE; } +void SrpServer::setDbCryptCallback(CheckStatusWrapper* status, ICryptKeyCallback* callback) +{ + cryptCallback = callback; +} + int SrpServer::release() { if (--refCounter == 0) diff --git a/src/auth/SecurityDatabase/LegacyServer.cpp b/src/auth/SecurityDatabase/LegacyServer.cpp index fc270372dd..4898985b3f 100644 --- a/src/auth/SecurityDatabase/LegacyServer.cpp +++ b/src/auth/SecurityDatabase/LegacyServer.cpp @@ -134,6 +134,7 @@ public: // IServer implementation int authenticate(Firebird::CheckStatusWrapper* status, Firebird::IServerBlock* sBlock, Firebird::IWriter* writerInterface); + void setDbCryptCallback(Firebird::CheckStatusWrapper*, Firebird::ICryptKeyCallback*) { } // ignore int release(); private: diff --git a/src/common/classes/GetPlugins.h b/src/common/classes/GetPlugins.h index cfc84dde10..c226b30dfa 100644 --- a/src/common/classes/GetPlugins.h +++ b/src/common/classes/GetPlugins.h @@ -54,7 +54,7 @@ public: } GetPlugins(unsigned int interfaceType, - Config* knownConfig, const char* namesList = NULL) + const Config* knownConfig, const char* namesList = NULL) : masterInterface(), pluginInterface(), pluginSet(NULL), currentPlugin(NULL), ls(*getDefaultMemoryPool()), status(&ls) diff --git a/src/common/classes/RefCounted.h b/src/common/classes/RefCounted.h index 9a97e63e63..674e892f45 100644 --- a/src/common/classes/RefCounted.h +++ b/src/common/classes/RefCounted.h @@ -33,12 +33,12 @@ namespace Firebird class RefCounted { public: - virtual int addRef() + virtual int addRef() const { return ++m_refCnt; } - virtual int release() + virtual int release() const { fb_assert(m_refCnt.value() > 0); const int refCnt = --m_refCnt; @@ -56,7 +56,7 @@ namespace Firebird } private: - AtomicCounter m_refCnt; + mutable AtomicCounter m_refCnt; }; // reference counted object guard diff --git a/src/common/config/config.cpp b/src/common/config/config.cpp index 572133fb06..5f8ef0c7ad 100644 --- a/src/common/config/config.cpp +++ b/src/common/config/config.cpp @@ -69,12 +69,14 @@ public: } } -/* void changeDefaultConfig(Config* newConfig) +/* It was a kind of getting ready for changing config remotely... + + void changeDefaultConfig(Config* newConfig) { defaultConfig = newConfig; } */ - const Firebird::RefPtr& getDefaultConfig() const + Firebird::RefPtr& getDefaultConfig() { return defaultConfig; } @@ -92,7 +94,7 @@ public: } private: - Firebird::RefPtr defaultConfig; + Firebird::RefPtr defaultConfig; ConfigImpl(const ConfigImpl&); void operator=(const ConfigImpl&); @@ -194,7 +196,8 @@ const Config::ConfigEntry Config::entries[MAX_CONFIG_KEY] = {TYPE_BOOLEAN, "IPv6V6Only", (ConfigValue) false}, {TYPE_BOOLEAN, "WireCompression", (ConfigValue) false}, {TYPE_INTEGER, "MaxIdentifierByteLength", (ConfigValue) -1}, - {TYPE_INTEGER, "MaxIdentifierCharLength", (ConfigValue) -1} + {TYPE_INTEGER, "MaxIdentifierCharLength", (ConfigValue) -1}, + {TYPE_BOOLEAN, "CryptSecurityDatabase", (ConfigValue) false} }; /****************************************************************************** @@ -256,7 +259,7 @@ Config::Config(const ConfigFile& file, const Config& base, const Firebird::PathN notifyDatabase = notify; } -void Config::notify() +void Config::notify() const { if (!notifyDatabase.hasData()) return; @@ -264,7 +267,7 @@ void Config::notify() notifyDatabase.erase(); } -void Config::merge(Firebird::RefPtr& config, const Firebird::string* dpbConfig) +void Config::merge(Firebird::RefPtr& config, const Firebird::string* dpbConfig) { if (dpbConfig && dpbConfig->hasData()) { @@ -338,7 +341,7 @@ Config::~Config() * Public interface */ -const Firebird::RefPtr& Config::getDefaultConfig() +const Firebird::RefPtr& Config::getDefaultConfig() { return firebirdConf().getDefaultConfig(); } @@ -810,3 +813,8 @@ int Config::getMaxIdentifierCharLength() const return MIN(MAX(rc, 1), METADATA_IDENTIFIER_CHAR_LEN); } + +bool Config::getCryptSecurityDatabase() const +{ + return get(KEY_ENCRYPT_SECURITY_DATABASE); +} diff --git a/src/common/config/config.h b/src/common/config/config.h index d767a83f8a..835d988c3d 100644 --- a/src/common/config/config.h +++ b/src/common/config/config.h @@ -142,6 +142,7 @@ public: KEY_WIRE_COMPRESSION, KEY_MAX_IDENTIFIER_BYTE_LENGTH, KEY_MAX_IDENTIFIER_CHAR_LENGTH, + KEY_ENCRYPT_SECURITY_DATABASE, MAX_CONFIG_KEY // keep it last }; @@ -174,7 +175,7 @@ private: static const ConfigEntry entries[MAX_CONFIG_KEY]; ConfigValue values[MAX_CONFIG_KEY]; - Firebird::PathName notifyDatabase; + mutable Firebird::PathName notifyDatabase; public: explicit Config(const ConfigFile& file); // use to build default config @@ -184,7 +185,7 @@ public: // Call it when database with given config is created - void notify(); + void notify() const; // Check for missing firebird.conf @@ -199,10 +200,10 @@ public: static const Firebird::PathName* getCommandLineRootDirectory(); // Master config - needed to provide per-database config - static const Firebird::RefPtr& getDefaultConfig(); + static const Firebird::RefPtr& getDefaultConfig(); // Merge config entries from DPB into existing config - static void merge(Firebird::RefPtr& config, const Firebird::string* dpbConfig); + static void merge(Firebird::RefPtr& config, const Firebird::string* dpbConfig); // reports key to be used by the following functions static unsigned int getKeyByName(ConfigName name); @@ -349,6 +350,8 @@ public: int getMaxIdentifierByteLength() const; int getMaxIdentifierCharLength() const; + + bool getCryptSecurityDatabase() const; }; // Implementation of interface to access master configuration file @@ -356,7 +359,7 @@ class FirebirdConf FB_FINAL : public Firebird::RefCntIface > { public: - FirebirdConf(Config* existingConfig) + FirebirdConf(const Config* existingConfig) : config(existingConfig) { } @@ -369,7 +372,7 @@ public: int release(); private: - Firebird::RefPtr config; + Firebird::RefPtr config; }; // Create default instance of IFirebirdConf interface diff --git a/src/common/db_alias.cpp b/src/common/db_alias.cpp index b25628c4d8..3c7d680cf6 100644 --- a/src/common/db_alias.cpp +++ b/src/common/db_alias.cpp @@ -227,7 +227,7 @@ namespace } PathName name; - RefPtr config; + RefPtr config; #ifdef HAVE_ID_BY_NAME Id* id; #endif @@ -417,7 +417,7 @@ static inline bool hasSeparator(const PathName& name) // Search for 'alias' in databases.conf, return its value in 'file' if found. Else set file to alias. // Returns true if alias is found in databases.conf. -static bool resolveAlias(const PathName& alias, PathName& file, RefPtr* config) +static bool resolveAlias(const PathName& alias, PathName& file, RefPtr* config) { PathName correctedAlias = alias; replace_dir_sep(correctedAlias); @@ -492,7 +492,7 @@ static bool setPath(const PathName& filename, PathName& expandedName) // Returns true if alias was found in databases.conf bool expandDatabaseName(Firebird::PathName alias, Firebird::PathName& file, - Firebird::RefPtr* config) + Firebird::RefPtr* config) { try { diff --git a/src/common/db_alias.h b/src/common/db_alias.h index e708184688..7d1cb6c1fe 100644 --- a/src/common/db_alias.h +++ b/src/common/db_alias.h @@ -30,7 +30,7 @@ class Config; bool expandDatabaseName(Firebird::PathName alias, Firebird::PathName& file, - Firebird::RefPtr* config); + Firebird::RefPtr* config); bool notifyDatabaseName(const Firebird::PathName& file); diff --git a/src/common/security.cpp b/src/common/security.cpp index 9430ac55dd..4a4c4cef02 100644 --- a/src/common/security.cpp +++ b/src/common/security.cpp @@ -41,7 +41,7 @@ void raise() namespace Auth { -Get::Get(Config* firebirdConf) +Get::Get(const Config* firebirdConf) : GetPlugins(IPluginManager::TYPE_AUTH_USER_MANAGEMENT, firebirdConf) { if (!hasData()) @@ -50,7 +50,7 @@ Get::Get(Config* firebirdConf) } } -Get::Get(Config* firebirdConf, const char* plugName) +Get::Get(const Config* firebirdConf, const char* plugName) : GetPlugins(IPluginManager::TYPE_AUTH_USER_MANAGEMENT, firebirdConf, plugName) { if (!hasData()) diff --git a/src/common/security.h b/src/common/security.h index 5dee7743b5..2dfa024423 100644 --- a/src/common/security.h +++ b/src/common/security.h @@ -259,8 +259,8 @@ public: class Get : public Firebird::GetPlugins { public: - explicit Get(Config* firebirdConf); - Get(Config* firebirdConf, const char* plugName); + explicit Get(const Config* firebirdConf); + Get(const Config* firebirdConf, const char* plugName); }; int setGsecCode(int code, unsigned int operation); diff --git a/src/include/fb_api_proto.h b/src/include/fb_api_proto.h index eecb3a99bf..7c6b22d471 100644 --- a/src/include/fb_api_proto.h +++ b/src/include/fb_api_proto.h @@ -439,6 +439,9 @@ typedef ISC_STATUS API_ROUTINE prototype_fb_cancel_operation(ISC_STATUS *, isc_db_handle *, USHORT); +typedef ISC_STATUS API_ROUTINE prototype_fb_database_crypt_callback(ISC_STATUS *, + void *); + struct FirebirdApiPointers { prototype_isc_attach_database *isc_attach_database; @@ -519,6 +522,7 @@ struct FirebirdApiPointers prototype_isc_service_query *isc_service_query; prototype_isc_service_start *isc_service_start; prototype_fb_cancel_operation *fb_cancel_operation; + prototype_fb_database_crypt_callback *fb_database_crypt_callback; }; #endif diff --git a/src/include/firebird/FirebirdInterface.idl b/src/include/firebird/FirebirdInterface.idl index 131020db44..39955fa2b9 100644 --- a/src/include/firebird/FirebirdInterface.idl +++ b/src/include/firebird/FirebirdInterface.idl @@ -599,6 +599,8 @@ interface Server : Auth { [notImplemented(Auth::AUTH_FAILED)] int authenticate(Status status, ServerBlock sBlock, Writer writerInterface); +version: // 3.0.1 => 4.0 + void setDbCryptCallback(Status status, CryptKeyCallback cryptCallback); } // .. and corresponding client @@ -737,6 +739,8 @@ version: // 3.0.1 => 4.0 // With returning true here KeyHolder attachment can use only keys, provided by this KeyHolder. // Use of keys, got by database crypt plugin from other attachments, is prohibited. boolean useOnlyOwnKeys(Status status); + // Communication in a chain of key holders - get callback interface for chaining holders + CryptKeyCallback chainHandle(Status status); } diff --git a/src/include/firebird/IdlFbInterfaces.h b/src/include/firebird/IdlFbInterfaces.h index a754b720b9..fe07b69a87 100644 --- a/src/include/firebird/IdlFbInterfaces.h +++ b/src/include/firebird/IdlFbInterfaces.h @@ -2402,6 +2402,7 @@ namespace Firebird struct VTable : public IAuth::VTable { int (CLOOP_CARG *authenticate)(IServer* self, IStatus* status, IServerBlock* sBlock, IWriter* writerInterface) throw(); + void (CLOOP_CARG *setDbCryptCallback)(IServer* self, IStatus* status, ICryptKeyCallback* cryptCallback) throw(); }; protected: @@ -2415,7 +2416,7 @@ namespace Firebird } public: - static const unsigned VERSION = 5; + static const unsigned VERSION = 6; template int authenticate(StatusType* status, IServerBlock* sBlock, IWriter* writerInterface) { @@ -2424,6 +2425,19 @@ namespace Firebird StatusType::checkException(status); return ret; } + + template void setDbCryptCallback(StatusType* status, ICryptKeyCallback* cryptCallback) + { + if (cloopVTable->version < 6) + { + StatusType::setVersionError(status, "IServer", cloopVTable->version, 6); + StatusType::checkException(status); + return; + } + StatusType::clearException(status); + static_cast(this->cloopVTable)->setDbCryptCallback(this, status, cryptCallback); + StatusType::checkException(status); + } }; class IClient : public IAuth @@ -2906,6 +2920,7 @@ namespace Firebird int (CLOOP_CARG *keyCallback)(IKeyHolderPlugin* self, IStatus* status, ICryptKeyCallback* callback) throw(); ICryptKeyCallback* (CLOOP_CARG *keyHandle)(IKeyHolderPlugin* self, IStatus* status, const char* keyName) throw(); FB_BOOLEAN (CLOOP_CARG *useOnlyOwnKeys)(IKeyHolderPlugin* self, IStatus* status) throw(); + ICryptKeyCallback* (CLOOP_CARG *chainHandle)(IKeyHolderPlugin* self, IStatus* status) throw(); }; protected: @@ -2950,6 +2965,20 @@ namespace Firebird StatusType::checkException(status); return ret; } + + template ICryptKeyCallback* chainHandle(StatusType* status) + { + if (cloopVTable->version < 5) + { + StatusType::setVersionError(status, "IKeyHolderPlugin", cloopVTable->version, 5); + StatusType::checkException(status); + return 0; + } + StatusType::clearException(status); + ICryptKeyCallback* ret = static_cast(this->cloopVTable)->chainHandle(this, status); + StatusType::checkException(status); + return ret; + } }; class IDbCryptInfo : public IReferenceCounted @@ -10013,6 +10042,7 @@ namespace Firebird this->setOwner = &Name::cloopsetOwnerDispatcher; this->getOwner = &Name::cloopgetOwnerDispatcher; this->authenticate = &Name::cloopauthenticateDispatcher; + this->setDbCryptCallback = &Name::cloopsetDbCryptCallbackDispatcher; } } vTable; @@ -10034,6 +10064,20 @@ namespace Firebird } } + static void CLOOP_CARG cloopsetDbCryptCallbackDispatcher(IServer* self, IStatus* status, ICryptKeyCallback* cryptCallback) throw() + { + StatusType status2(status); + + try + { + static_cast(self)->Name::setDbCryptCallback(&status2, cryptCallback); + } + catch (...) + { + StatusType::catchException(&status2); + } + } + static void CLOOP_CARG cloopsetOwnerDispatcher(IPluginBase* self, IReferenceCounted* r) throw() { try @@ -10099,6 +10143,7 @@ namespace Firebird } virtual int authenticate(StatusType* status, IServerBlock* sBlock, IWriter* writerInterface) = 0; + virtual void setDbCryptCallback(StatusType* status, ICryptKeyCallback* cryptCallback) = 0; }; template @@ -11246,6 +11291,7 @@ namespace Firebird this->keyCallback = &Name::cloopkeyCallbackDispatcher; this->keyHandle = &Name::cloopkeyHandleDispatcher; this->useOnlyOwnKeys = &Name::cloopuseOnlyOwnKeysDispatcher; + this->chainHandle = &Name::cloopchainHandleDispatcher; } } vTable; @@ -11297,6 +11343,21 @@ namespace Firebird } } + static ICryptKeyCallback* CLOOP_CARG cloopchainHandleDispatcher(IKeyHolderPlugin* self, IStatus* status) throw() + { + StatusType status2(status); + + try + { + return static_cast(self)->Name::chainHandle(&status2); + } + catch (...) + { + StatusType::catchException(&status2); + return static_cast(0); + } + } + static void CLOOP_CARG cloopsetOwnerDispatcher(IPluginBase* self, IReferenceCounted* r) throw() { try @@ -11364,6 +11425,7 @@ namespace Firebird virtual int keyCallback(StatusType* status, ICryptKeyCallback* callback) = 0; virtual ICryptKeyCallback* keyHandle(StatusType* status, const char* keyName) = 0; virtual FB_BOOLEAN useOnlyOwnKeys(StatusType* status) = 0; + virtual ICryptKeyCallback* chainHandle(StatusType* status) = 0; }; template diff --git a/src/jrd/CryptoManager.cpp b/src/jrd/CryptoManager.cpp index 9b725178b0..7aaca51d78 100644 --- a/src/jrd/CryptoManager.cpp +++ b/src/jrd/CryptoManager.cpp @@ -47,7 +47,7 @@ #include "../jrd/Monitoring.h" #include "../jrd/os/pio_proto.h" #include "../common/isc_proto.h" -#include "../common/classes/GetPlugins.h" +#include "../common/classes/auto.h" #include "../common/classes/RefMutex.h" #include "../common/classes/ClumpletWriter.h" #include "../common/sha.h" @@ -69,6 +69,19 @@ namespace { const UCHAR CRYPT_INIT = LCK_EX; const int MAX_PLUGIN_NAME_LEN = 31; + + class ReleasePlugin + { + public: + static void clear(IPluginBase* ptr) + { + if (ptr) + { + PluginManagerInterfacePtr()->releasePlugin(ptr); + } + } + }; + } @@ -270,7 +283,7 @@ namespace Jrd { dbInfo(FB_NEW DbInfo(this)), cryptThreadId(0), cryptPlugin(NULL), - checkPlugin(NULL), + checkFactory(NULL), dbb(*tdbb->getDatabase()), cryptAtt(NULL), slowIO(0), @@ -291,6 +304,7 @@ namespace Jrd { delete stateLock; delete threadLock; + delete checkFactory; dbInfo->destroy(); } @@ -363,7 +377,7 @@ namespace Jrd { else keyName = ""; - loadPlugin(hdr->hdr_crypt_plugin); + loadPlugin(tdbb, hdr->hdr_crypt_plugin); string valid; calcValidation(valid, cryptPlugin); @@ -378,10 +392,10 @@ namespace Jrd { } if (flags & CRYPT_HDR_INIT) - checkDigitalSignature(hdr); + checkDigitalSignature(tdbb, hdr); } - void CryptoManager::loadPlugin(const char* pluginName) + void CryptoManager::loadPlugin(thread_db* tdbb, const char* pluginName) { if (cryptPlugin) { @@ -394,14 +408,14 @@ namespace Jrd { return; } - GetPlugins cryptControl(IPluginManager::TYPE_DB_CRYPT, dbb.dbb_config, pluginName); - if (!cryptControl.hasData()) + AutoPtr cryptControl(FB_NEW Factory(IPluginManager::TYPE_DB_CRYPT, dbb.dbb_config, pluginName)); + if (!cryptControl->hasData()) { (Arg::Gds(isc_no_crypt_plugin) << pluginName).raise(); } // do not assign cryptPlugin directly before key init complete - IDbCryptPlugin* p = cryptControl.plugin(); + IDbCryptPlugin* p = cryptControl->plugin(); FbLocalStatus status; p->setInfo(&status, dbInfo); @@ -416,17 +430,15 @@ namespace Jrd { cryptPlugin = p; cryptPlugin->addRef(); - // May be load second instance to validate keys - if (checkPlugin) - { - PluginManagerInterfacePtr()->releasePlugin(checkPlugin); - checkPlugin = NULL; - } + // remove old factory if present + delete checkFactory; + checkFactory = NULL; + // store new one if (dbb.dbb_config->getServerMode() == MODE_SUPER) { - checkPlugin = cryptControl.makeInstance(); - keyHolderPlugins.validate(checkPlugin, NULL, keyName); + checkFactory = cryptControl.release(); + keyHolderPlugins.validateNewAttachment(tdbb->getAttachment(), keyName); } } @@ -483,7 +495,7 @@ namespace Jrd { } keyName = key; - loadPlugin(plugName.c_str()); + loadPlugin(tdbb, plugName.c_str()); } } } @@ -560,7 +572,7 @@ namespace Jrd { // Load plugin if (newCryptState) { - loadPlugin(plugName.c_str()); + loadPlugin(tdbb, plugName.c_str()); } crypt = newCryptState; @@ -580,6 +592,9 @@ namespace Jrd { hc.deleteWithTag(Ods::HDR_crypt_key); if (keyName.hasData()) hc.insertString(Ods::HDR_crypt_key, keyName); + + if (checkFactory) + keyHolderPlugins.validateExistingAttachments(keyName); } else header->hdr_flags &= ~Ods::hdr_encrypted; @@ -591,7 +606,7 @@ namespace Jrd { header->hdr_flags |= Ods::hdr_crypt_process; process = true; - digitalySignDatabase(hdr); + digitalySignDatabase(tdbb, hdr); hdr.flush(); } catch (const Exception&) @@ -641,13 +656,13 @@ namespace Jrd { { keyHolderPlugins.attach(att, dbb.dbb_config); - IDbCryptPlugin* p = checkPlugin; + Factory* f = checkFactory; lockAndReadHeader(tdbb, CRYPT_HDR_INIT); - if (p && p == checkPlugin) + if (f && f == checkFactory) { - if (!keyHolderPlugins.validate(checkPlugin, att, keyName)) + if (!keyHolderPlugins.validateNewAttachment(att, keyName)) (Arg::Gds(isc_bad_crypt_key) << keyName).raise(); } } @@ -713,7 +728,7 @@ namespace Jrd { crypt = hdr->hdr_flags & Ods::hdr_encrypted ? true : false; // If we are going to start crypt thread, we need plugin to be loaded - loadPlugin(hdr->hdr_crypt_plugin); + loadPlugin(tdbb, hdr->hdr_crypt_plugin); releasingLock = true; LCK_release(tdbb, threadLock); @@ -956,7 +971,7 @@ namespace Jrd { } } - digitalySignDatabase(hdr); + digitalySignDatabase(tdbb, hdr); hdr.flush(); } @@ -1162,7 +1177,7 @@ namespace Jrd { return (crypt ? fb_info_crypt_encrypted : 0) | (process ? fb_info_crypt_process : 0); } - void CryptoManager::KeyHolderPlugins::attach(Attachment* att, Config* config) + void CryptoManager::KeyHolderPlugins::attach(Attachment* att, const Config* config) { MutexLockGuard g(holdersMutex, FB_FUNCTION); @@ -1196,7 +1211,7 @@ namespace Jrd { } } - if ((!pa) && config->getServerMode() == MODE_SUPER) + if (!pa) { pa = &(knownHolders.add()); pa->first = att; @@ -1254,7 +1269,7 @@ namespace Jrd { st.check(); } - bool CryptoManager::KeyHolderPlugins::validateHoldersGroup(PerAttHolders& pa, IDbCryptPlugin* crypt, const MetaName& keyName) + bool CryptoManager::KeyHolderPlugins::validateHoldersGroup(PerAttHolders& pa, const MetaName& keyName) { FbLocalStatus st; fb_assert(holdersMutex.locked()); @@ -1265,15 +1280,14 @@ namespace Jrd { if (!keyHolder->useOnlyOwnKeys(&st)) return true; - crypt->setKey(&st, 1, &keyHolder, keyName.c_str()); - if (st.isSuccess() && mgr->checkValidation(crypt)) + if (validateHolder(keyHolder, keyName)) return true; } - return true; + return false; } - bool CryptoManager::KeyHolderPlugins::validate(IDbCryptPlugin* crypt, Attachment* att, const MetaName& keyName) + bool CryptoManager::KeyHolderPlugins::validateNewAttachment(Attachment* att, const MetaName& keyName) { FbLocalStatus st; MutexLockGuard g(holdersMutex, FB_FUNCTION); @@ -1286,10 +1300,7 @@ namespace Jrd { if (pa.first == att) { bool empty = (pa.second.getCount() == 0); - bool result = empty ? false : validateHoldersGroup(pa, crypt, keyName); - - releaseHolders(pa); - knownHolders.remove(i); + bool result = empty ? false : validateHoldersGroup(pa, keyName); if (empty) break; @@ -1299,25 +1310,45 @@ namespace Jrd { } // Special case - holders not needed at all - crypt->setKey(&st, 0, NULL, keyName.c_str()); - if (st.isSuccess() && mgr->checkValidation(crypt)) - return true; + return validateHolder(NULL, keyName); + } + bool CryptoManager::KeyHolderPlugins::validateHolder(IKeyHolderPlugin* keyHolder, const MetaName& keyName) + { + fb_assert(mgr->checkFactory); + if (!mgr->checkFactory) + return false; + + FbLocalStatus st; + + AutoPtr crypt(mgr->checkFactory->makeInstance()); + crypt->setKey(&st, keyHolder ? 1 : 0, &keyHolder, keyName.c_str()); + + if (st.isSuccess()) + { + try + { + if (mgr->checkValidation(crypt)) + return true; + } + catch (const Exception&) + { } // Ignore possible errors, continue analysis + } return false; } - void CryptoManager::KeyHolderPlugins::validate(IDbCryptPlugin* crypt, const MetaName& keyName) + void CryptoManager::KeyHolderPlugins::validateExistingAttachments(const MetaName& keyName) { FbLocalStatus st; - MutexLockGuard g(holdersMutex, FB_FUNCTION); - fb_assert(mgr->dbb.dbb_sync.isLocked()); // Special case - holders not needed at all - crypt->setKey(&st, 0, NULL, keyName.c_str()); - if (st.isSuccess() && mgr->checkValidation(crypt)) + if (validateHolder(NULL, keyName)) return; - // Loop through whole attathment list of DBB, shutdown attachments missing any holders + // Loop through whole attachments list of DBB, shutdown attachments missing any holders + fb_assert(!mgr->dbb.dbb_sync.isLocked()); + MutexLockGuard g(holdersMutex, FB_FUNCTION); + SyncLockGuard dsGuard(&mgr->dbb.dbb_sync, SYNC_EXCLUSIVE, FB_FUNCTION); for (Attachment* att = mgr->dbb.dbb_attachments; att; att = att->att_next) { for (unsigned i = 0; i < knownHolders.getCount(); ++i) @@ -1326,21 +1357,16 @@ namespace Jrd { goto found; } - att->signalCancel(); + att->signalShutdown(); found:; } // Loop through internal attachments list closing one missing valid holders for (unsigned i = 0; i < knownHolders.getCount(); ++i) { - if (!validateHoldersGroup(knownHolders[i], crypt, keyName)) - knownHolders[i].first->signalCancel(); - - // Cleanup holders list - releaseHolders(knownHolders[i]); + if (!validateHoldersGroup(knownHolders[i], keyName)) + knownHolders[i].first->signalShutdown(); } - - knownHolders.clear(); } void CryptoManager::addClumplet(string& signature, ClumpletReader& block, UCHAR tag) @@ -1354,7 +1380,7 @@ found:; } } - void CryptoManager::calcDigitalSignature(string& signature, const Header& hdr) + void CryptoManager::calcDigitalSignature(thread_db* tdbb, string& signature, const Header& hdr) { /* We use the following items to calculate digital signature (hash of encrypted string) @@ -1383,7 +1409,7 @@ found:; unsigned len = signature.length(); len &= ~(QUANTUM - 1); - loadPlugin(hdr->hdr_crypt_plugin); + loadPlugin(tdbb, hdr->hdr_crypt_plugin); string enc; FbLocalStatus sv; @@ -1395,7 +1421,7 @@ found:; } - void CryptoManager::digitalySignDatabase(CchHdr& hdr) + void CryptoManager::digitalySignDatabase(thread_db* tdbb, CchHdr& hdr) { ClumpletWriter hc(ClumpletWriter::UnTagged, hdr->hdr_page_size); hdr.getClumplets(hc); @@ -1407,7 +1433,7 @@ found:; { wf = true; string signature; - calcDigitalSignature(signature, hdr); + calcDigitalSignature(tdbb, signature, hdr); hc.insertString(Ods::HDR_crypt_checksum, signature); } @@ -1415,7 +1441,7 @@ found:; hdr.setClumplets(hc); } - void CryptoManager::checkDigitalSignature(const Header& hdr) + void CryptoManager::checkDigitalSignature(thread_db* tdbb, const Header& hdr) { if (hdr->hdr_flags & (Ods::hdr_crypt_process | Ods::hdr_encrypted)) { @@ -1426,7 +1452,7 @@ found:; string sig1, sig2; hc.getString(sig1); - calcDigitalSignature(sig2, hdr); + calcDigitalSignature(tdbb, sig2, hdr); if (sig1 != sig2) Arg::Gds(isc_crypt_checksum).raise(); } diff --git a/src/jrd/CryptoManager.h b/src/jrd/CryptoManager.h index d5a4269c22..69fef7d79a 100644 --- a/src/jrd/CryptoManager.h +++ b/src/jrd/CryptoManager.h @@ -36,6 +36,7 @@ #include "../common/classes/objects_array.h" #include "../common/classes/condition.h" #include "../common/classes/MetaName.h" +#include "../common/classes/GetPlugins.h" #include "../common/ThreadStart.h" #include "../jrd/ods.h" #include "../jrd/status.h" @@ -268,6 +269,8 @@ private: class CryptoManager FB_FINAL : public Firebird::PermanentStorage, public BarSync::IBar { public: + typedef Firebird::GetPlugins Factory; + explicit CryptoManager(thread_db* tdbb); ~CryptoManager(); @@ -324,31 +327,34 @@ private: class KeyHolderPlugins { public: + typedef CryptoManager::Factory Factory; + explicit KeyHolderPlugins(Firebird::MemoryPool& p, CryptoManager* m) : knownHolders(p), mgr(m) { } - void attach(Attachment* att, Config* config); + void attach(Attachment* att, const Config* config); void init(Firebird::IDbCryptPlugin* crypt, const Firebird::MetaName& keyName); - bool validate(Firebird::IDbCryptPlugin* crypt, Attachment*, const Firebird::MetaName& keyName); - void validate(Firebird::IDbCryptPlugin* crypt, const Firebird::MetaName& keyName); + bool validateNewAttachment(Attachment*, const Firebird::MetaName& keyName); + void validateExistingAttachments(const Firebird::MetaName& keyName); void detach(Attachment* att); private: Firebird::Mutex holdersMutex; typedef Firebird::Pair>> PerAttHolders; + Firebird::HalfStaticArray > > PerAttHolders; Firebird::ObjectsArray knownHolders; CryptoManager* mgr; - bool validateHoldersGroup(PerAttHolders& pa, Firebird::IDbCryptPlugin* crypt, const Firebird::MetaName& keyName); + bool validateHoldersGroup(PerAttHolders& pa, const Firebird::MetaName& keyName); + bool validateHolder(Firebird::IKeyHolderPlugin* keyHolder, const Firebird::MetaName& keyName); void releaseHolders(PerAttHolders& pa); }; class DbInfo; friend class DbInfo; - class DbInfo FB_FINAL : public Firebird::RefCntIface> + class DbInfo FB_FINAL : public Firebird::RefCntIface > { public: DbInfo(CryptoManager* cm) @@ -383,7 +389,7 @@ private: void doOnTakenWriteSync(thread_db* tdbb); void doOnAst(thread_db* tdbb); - void loadPlugin(const char* pluginName); + void loadPlugin(thread_db* tdbb, const char* pluginName); ULONG getLastPage(thread_db* tdbb); void writeDbHeader(thread_db* tdbb, ULONG runpage); void calcValidation(Firebird::string& valid, Firebird::IDbCryptPlugin* plugin); @@ -394,9 +400,9 @@ private: static const unsigned CRYPT_HDR_NOWAIT = 0x02; void addClumplet(Firebird::string& value, Firebird::ClumpletReader& block, UCHAR tag); - void calcDigitalSignature(Firebird::string& signature, const class Header& hdr); - void digitalySignDatabase(class CchHdr& hdr); - void checkDigitalSignature(const class Header& hdr); + void calcDigitalSignature(thread_db* tdbb, Firebird::string& signature, const class Header& hdr); + void digitalySignDatabase(thread_db* tdbb, class CchHdr& hdr); + void checkDigitalSignature(thread_db* tdbb, const class Header& hdr); BarSync sync; Firebird::MetaName keyName; @@ -407,7 +413,7 @@ private: Firebird::RefPtr dbInfo; Thread::Handle cryptThreadId; Firebird::IDbCryptPlugin* cryptPlugin; - Firebird::IDbCryptPlugin* checkPlugin; + Factory* checkFactory; Database& dbb; Lock* stateLock; Lock* threadLock; diff --git a/src/jrd/Database.h b/src/jrd/Database.h index 962d1078b7..d15554e727 100644 --- a/src/jrd/Database.h +++ b/src/jrd/Database.h @@ -461,7 +461,7 @@ public: BackupManager* dbb_backup_manager; // physical backup manager Firebird::TimeStamp dbb_creation_date; // creation date ExternalFileDirectoryList* dbb_external_file_directory_list; - Firebird::RefPtr dbb_config; + Firebird::RefPtr dbb_config; SharedCounter dbb_shared_counter; CryptoManager* dbb_crypto_manager; diff --git a/src/jrd/event.cpp b/src/jrd/event.cpp index cb6dc33263..7aa94343bd 100644 --- a/src/jrd/event.cpp +++ b/src/jrd/event.cpp @@ -120,7 +120,7 @@ void EventManager::destroy(EventManager* eventMgr) } -EventManager::EventManager(const Firebird::string& id, Firebird::RefPtr conf) +EventManager::EventManager(const Firebird::string& id, Firebird::RefPtr conf) : PID(getpid()), m_process(NULL), m_processOffset(0), diff --git a/src/jrd/event_proto.h b/src/jrd/event_proto.h index 9bfd20e0b9..fa26259724 100644 --- a/src/jrd/event_proto.h +++ b/src/jrd/event_proto.h @@ -51,7 +51,7 @@ public: static void init(Attachment*); static void destroy(EventManager*); - EventManager(const Firebird::string& id, Firebird::RefPtr conf); + EventManager(const Firebird::string& id, Firebird::RefPtr conf); ~EventManager(); void deleteSession(SLONG); @@ -104,7 +104,7 @@ private: SLONG m_processOffset; Firebird::string m_dbId; - Firebird::RefPtr m_config; + Firebird::RefPtr m_config; Firebird::AutoPtr > m_sharedMemory; Firebird::Semaphore m_startupSemaphore; diff --git a/src/jrd/ext.cpp b/src/jrd/ext.cpp index 73c416400c..dae4dfe752 100644 --- a/src/jrd/ext.cpp +++ b/src/jrd/ext.cpp @@ -109,7 +109,7 @@ namespace Jrd } private: - RefPtr config; + RefPtr config; }; } diff --git a/src/jrd/extds/IscDS.cpp b/src/jrd/extds/IscDS.cpp index a290a473ce..b98bc170ac 100644 --- a/src/jrd/extds/IscDS.cpp +++ b/src/jrd/extds/IscDS.cpp @@ -124,12 +124,24 @@ void IscConnection::attach(thread_db* tdbb, const PathName& dbName, const MetaNa FbLocalStatus status; { EngineCallbackGuard guard(tdbb, *this, FB_FUNCTION); + + ICryptKeyCallback* cb = tdbb->getAttachment()->att_crypt_callback; + m_iscProvider.fb_database_crypt_callback(&status, cb); + if (status->getState() & IStatus::STATE_ERRORS) { + raise(&status, tdbb, "crypt_callback"); + } + m_iscProvider.isc_attach_database(&status, m_dbName.length(), m_dbName.c_str(), &m_handle, newDpb.getBufferLength(), reinterpret_cast(newDpb.getBuffer())); - } - if (status->getState() & IStatus::STATE_ERRORS) { - raise(&status, tdbb, "attach"); + if (status->getState() & IStatus::STATE_ERRORS) { + raise(&status, tdbb, "attach"); + } + + m_iscProvider.fb_database_crypt_callback(&status, NULL); + if (status->getState() & IStatus::STATE_ERRORS) { + raise(&status, tdbb, "crypt_callback"); + } } char buff[16]; @@ -1474,6 +1486,15 @@ ISC_STATUS ISC_EXPORT IscProvider::fb_cancel_operation(FbStatusVector* user_stat return notImplemented(user_status); } +ISC_STATUS ISC_EXPORT IscProvider::fb_database_crypt_callback(FbStatusVector* user_status, + void* cb) +{ + if (m_api.fb_database_crypt_callback) + return m_api.fb_database_crypt_callback(IscStatus(user_status), cb); + + return notImplemented(user_status); +} + void IscProvider::loadAPI() { FbLocalStatus status; @@ -1565,7 +1586,8 @@ static FirebirdApiPointers isc_callbacks = PROTO(isc_service_detach), PROTO(isc_service_query), PROTO(isc_service_start), - PROTO(fb_cancel_operation) + PROTO(fb_cancel_operation), + PROTO(fb_database_crypt_callback) }; diff --git a/src/jrd/extds/IscDS.h b/src/jrd/extds/IscDS.h index dd3abb95e5..e330170ba7 100644 --- a/src/jrd/extds/IscDS.h +++ b/src/jrd/extds/IscDS.h @@ -482,6 +482,9 @@ public: virtual ISC_STATUS ISC_EXPORT fb_cancel_operation(Jrd::FbStatusVector*, isc_db_handle*, USHORT); + + virtual ISC_STATUS ISC_EXPORT fb_database_crypt_callback(Jrd::FbStatusVector*, + void*); }; diff --git a/src/jrd/extds/ValidatePassword.cpp b/src/jrd/extds/ValidatePassword.cpp index 315c11b335..ee14bdb04f 100644 --- a/src/jrd/extds/ValidatePassword.cpp +++ b/src/jrd/extds/ValidatePassword.cpp @@ -180,7 +180,7 @@ void validatePassword(thread_db* tdbb, const PathName& file, ClumpletWriter& dpb Arg::Gds loginError(isc_login_error); // Build list of client/server plugins - RefPtr config; + RefPtr config; PathName list; expandDatabaseName(file, list /* unused value */, &config); PathName serverList = config->getPlugins(IPluginManager::TYPE_AUTH_SERVER); diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 4b748d86bd..3f1918b536 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -969,7 +969,7 @@ public: void get(const UCHAR*, USHORT, bool&); - void setBuffers(RefPtr config) + void setBuffers(RefPtr config) { if (dpb_buffers == 0) { @@ -1046,7 +1046,7 @@ static VdnResult verifyDatabaseName(const PathName&, FbStatusVector*, bool); static void unwindAttach(thread_db* tdbb, const Exception& ex, FbStatusVector* userStatus, Jrd::Attachment* attachment, Database* dbb, bool internalFlag); -static JAttachment* initAttachment(thread_db*, const PathName&, const PathName&, RefPtr, bool, +static JAttachment* initAttachment(thread_db*, const PathName&, const PathName&, RefPtr, bool, const DatabaseOptions&, RefMutexUnlock&, IPluginConfig*, JProvider*); static JAttachment* create_attachment(const PathName&, Database*, const DatabaseOptions&, bool newDb); static void prepare_tra(thread_db*, jrd_tra*, USHORT, const UCHAR*); @@ -1056,7 +1056,7 @@ static void release_attachment(thread_db*, Jrd::Attachment*); static void rollback(thread_db*, jrd_tra*, const bool); static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsigned flags = 0); static void getUserInfo(UserId&, const DatabaseOptions&, const char*, const char*, - const RefPtr*, bool, IAttachment*, ICryptKeyCallback*); + const RefPtr*, bool, IAttachment*, ICryptKeyCallback*); static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM); @@ -1343,7 +1343,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch ThreadContextHolder tdbb(user_status); DatabaseOptions options; - RefPtr config; + RefPtr config; bool invalid_client_SQL_dialect = false; PathName org_filename, expanded_name; bool is_alias = false; @@ -2427,7 +2427,7 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch DatabaseOptions options; PathName org_filename, expanded_name; bool is_alias = false; - Firebird::RefPtr config; + Firebird::RefPtr config; try { @@ -5981,7 +5981,7 @@ void DatabaseOptions::get(const UCHAR* dpb, USHORT dpb_length, bool& invalid_cli static JAttachment* initAttachment(thread_db* tdbb, const PathName& expanded_name, - const PathName& alias_name, RefPtr config, bool attach_flag, + const PathName& alias_name, RefPtr config, bool attach_flag, const DatabaseOptions& options, RefMutexUnlock& initGuard, IPluginConfig* pConf, JProvider* provider) { @@ -7126,7 +7126,7 @@ static VdnResult verifyDatabaseName(const PathName& name, FbStatusVector* status if (!securityNameBuffer->hasData()) { - const RefPtr defConf(Config::getDefaultConfig()); + const RefPtr defConf(Config::getDefaultConfig()); securityNameBuffer->assign(defConf->getSecurityDatabase()); expandedSecurityNameBuffer->assign(securityNameBuffer); ISC_expand_filename(expandedSecurityNameBuffer, false); @@ -7166,7 +7166,7 @@ static VdnResult verifyDatabaseName(const PathName& name, FbStatusVector* status **/ static void getUserInfo(UserId& user, const DatabaseOptions& options, - const char* aliasName, const char* dbName, const RefPtr* config, bool creating, + const char* aliasName, const char* dbName, const RefPtr* config, bool creating, IAttachment* iAtt, ICryptKeyCallback* cryptCb) { bool wheel = false; diff --git a/src/jrd/svc.cpp b/src/jrd/svc.cpp index d84ae989e2..98e21eea5f 100644 --- a/src/jrd/svc.cpp +++ b/src/jrd/svc.cpp @@ -729,7 +729,7 @@ Service::Service(const TEXT* service_name, USHORT spb_length, const UCHAR* spb_d if (svc_auth_block.hasData()) { PathName dummy; - RefPtr config; + RefPtr config; expandDatabaseName(svc_expected_db, dummy, &config); string trusted_role; @@ -1310,7 +1310,7 @@ ISC_STATUS Service::query2(thread_db* /*tdbb*/, { // The path to the user security database (security2.fdb) char* pb = reinterpret_cast(buffer); - const RefPtr defConf(Config::getDefaultConfig()); + const RefPtr defConf(Config::getDefaultConfig()); strcpy(pb, defConf->getSecurityDatabase()); if (!(info = INF_put_item(item, static_cast(strlen(pb)), buffer, info, end))) @@ -1764,7 +1764,7 @@ void Service::query(USHORT send_item_length, { // The path to the user security database (security2.fdb) char* pb = reinterpret_cast(buffer); - const RefPtr defConf(Config::getDefaultConfig()); + const RefPtr defConf(Config::getDefaultConfig()); strcpy(pb, defConf->getSecurityDatabase()); if (!(info = INF_put_item(item, static_cast(strlen(pb)), buffer, info, end))) diff --git a/src/jrd/trace/TraceManager.cpp b/src/jrd/trace/TraceManager.cpp index 2a874a46cb..5de23e9740 100644 --- a/src/jrd/trace/TraceManager.cpp +++ b/src/jrd/trace/TraceManager.cpp @@ -285,7 +285,7 @@ void TraceManager::update_session(const TraceSession& session) if (session.ses_auth.hasData()) { PathName dummy; - RefPtr config; + RefPtr config; expandDatabaseName(service->getExpectedDb(), dummy, &config); if (mapUser(false, s_user, t_role, NULL, NULL, NULL, session.ses_auth, "services manager", diff --git a/src/lock/lock.cpp b/src/lock/lock.cpp index 00479a980e..4123d06bef 100644 --- a/src/lock/lock.cpp +++ b/src/lock/lock.cpp @@ -167,7 +167,7 @@ Firebird::GlobalPtr LockManager::g_lmMap; Firebird::GlobalPtr LockManager::g_mapMutex; -LockManager* LockManager::create(const Firebird::string& id, RefPtr conf) +LockManager* LockManager::create(const Firebird::string& id, RefPtr conf) { Firebird::MutexLockGuard guard(g_mapMutex, FB_FUNCTION); @@ -208,7 +208,7 @@ void LockManager::destroy(LockManager* lockMgr) } -LockManager::LockManager(const Firebird::string& id, RefPtr conf) +LockManager::LockManager(const Firebird::string& id, RefPtr conf) : PID(getpid()), m_bugcheck(false), m_sharedFileCreated(false), diff --git a/src/lock/lock_proto.h b/src/lock/lock_proto.h index 2faec49d1d..fe79dc2fc0 100644 --- a/src/lock/lock_proto.h +++ b/src/lock/lock_proto.h @@ -398,7 +398,7 @@ class LockManager : private Firebird::RefCounted, const int PID; public: - static LockManager* create(const Firebird::string&, Firebird::RefPtr); + static LockManager* create(const Firebird::string&, Firebird::RefPtr); static void destroy(LockManager*); bool initializeOwner(Firebird::CheckStatusWrapper*, LOCK_OWNER_T, UCHAR, SRQ_PTR*); @@ -421,7 +421,7 @@ public: void exceptionHandler(const Firebird::Exception& ex, ThreadFinishSync::ThreadRoutine* routine); private: - explicit LockManager(const Firebird::string&, Firebird::RefPtr); + explicit LockManager(const Firebird::string&, Firebird::RefPtr); ~LockManager(); void acquire_shmem(SRQ_PTR); @@ -500,7 +500,7 @@ private: bool m_blockage; Firebird::string m_dbId; - Firebird::RefPtr m_config; + Firebird::RefPtr m_config; // configurations parameters - cached values const ULONG m_acquireSpins; diff --git a/src/remote/client/interface.cpp b/src/remote/client/interface.cpp index 6108d9aa67..1548659784 100644 --- a/src/remote/client/interface.cpp +++ b/src/remote/client/interface.cpp @@ -693,7 +693,8 @@ static Rvnt* add_event(rem_port*); static void add_other_params(rem_port*, ClumpletWriter&, const ParametersSet&); static void add_working_directory(ClumpletWriter&, const PathName&); static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned flags, - ClumpletWriter& pb, const ParametersSet& parSet, PathName& node_name, PathName* ref_db_name); + ClumpletWriter& pb, const ParametersSet& parSet, PathName& node_name, PathName* ref_db_name, + Firebird::ICryptKeyCallback* cryptCb); static void batch_gds_receive(rem_port*, struct rmtque *, USHORT); static void batch_dsql_fetch(rem_port*, struct rmtque *, USHORT); static void clear_queue(rem_port*); @@ -798,7 +799,7 @@ IAttachment* RProvider::attach(CheckStatusWrapper* status, const char* filename, PathName node_name; ClntAuthBlock cBlock(&expanded_name, &newDpb, &dpbParam); - rem_port* port = analyze(cBlock, expanded_name, flags, newDpb, dpbParam, node_name, NULL); + rem_port* port = analyze(cBlock, expanded_name, flags, newDpb, dpbParam, node_name, NULL, cryptCallback); if (!port) { @@ -1420,7 +1421,7 @@ Firebird::IAttachment* RProvider::create(CheckStatusWrapper* status, const char* PathName node_name; ClntAuthBlock cBlock(&expanded_name, &newDpb, &dpbParam); - rem_port* port = analyze(cBlock, expanded_name, flags, newDpb, dpbParam, node_name, NULL); + rem_port* port = analyze(cBlock, expanded_name, flags, newDpb, dpbParam, node_name, NULL, cryptCallback); if (!port) { @@ -4631,7 +4632,7 @@ Firebird::IService* RProvider::attachSvc(CheckStatusWrapper* status, const char* if (newSpb.find(isc_spb_expected_db)) newSpb.getPath(refDbName); - rem_port* port = analyze(cBlock, expanded_name, flags, newSpb, spbParam, node_name, &refDbName); + rem_port* port = analyze(cBlock, expanded_name, flags, newSpb, spbParam, node_name, &refDbName, cryptCallback); RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION); Rdb* rdb = port->port_context; @@ -5400,7 +5401,8 @@ static void secureAuthentication(ClntAuthBlock& cBlock, rem_port* port) static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned flags, - ClumpletWriter& pb, const ParametersSet& parSet, PathName& node_name, PathName* ref_db_name) + ClumpletWriter& pb, const ParametersSet& parSet, PathName& node_name, PathName* ref_db_name, + Firebird::ICryptKeyCallback* cryptCb) { /************************************** * @@ -5464,7 +5466,7 @@ static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned } port = INET_analyze(&cBlock, attach_name, node_name.c_str(), flags & ANALYZE_UV, pb, - cBlock.getConfig(), ref_db_name, inet_af); + cBlock.getConfig(), ref_db_name, cryptCb, inet_af); } // We have a local connection string. If it's a file on a network share, @@ -5498,7 +5500,7 @@ static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned ISC_utf8ToSystem(node_name); port = INET_analyze(&cBlock, expanded_name, node_name.c_str(), flags & ANALYZE_UV, pb, - cBlock.getConfig(), ref_db_name); + cBlock.getConfig(), ref_db_name, cryptCb); } } #endif @@ -5527,7 +5529,7 @@ static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned if (!port) { port = INET_analyze(&cBlock, attach_name, INET_LOCALHOST, flags & ANALYZE_UV, pb, - cBlock.getConfig(), ref_db_name); + cBlock.getConfig(), ref_db_name, cryptCb); } } } diff --git a/src/remote/inet.cpp b/src/remote/inet.cpp index 54c0907668..45320c230c 100644 --- a/src/remote/inet.cpp +++ b/src/remote/inet.cpp @@ -457,7 +457,7 @@ static rem_port* inet_try_connect( PACKET*, const PathName&, const TEXT*, ClumpletReader&, - RefPtr*, + RefPtr*, const PathName*, int); static bool inet_write(XDR*); @@ -538,8 +538,9 @@ rem_port* INET_analyze(ClntAuthBlock* cBlock, const TEXT* node_name, bool uv_flag, ClumpletReader &dpb, - RefPtr* config, + RefPtr* config, const PathName* ref_db_name, + Firebird::ICryptKeyCallback* cryptCb, int af) { /************************************** @@ -619,7 +620,8 @@ rem_port* INET_analyze(ClntAuthBlock* cBlock, REMOTE_PROTOCOL(PROTOCOL_VERSION11, ptype_lazy_send, 2), REMOTE_PROTOCOL(PROTOCOL_VERSION12, ptype_lazy_send, 3), REMOTE_PROTOCOL(PROTOCOL_VERSION13, ptype_lazy_send, 4), - REMOTE_PROTOCOL(PROTOCOL_VERSION14, ptype_lazy_send, 5) + REMOTE_PROTOCOL(PROTOCOL_VERSION14, ptype_lazy_send, 5), + REMOTE_PROTOCOL(PROTOCOL_VERSION15, ptype_lazy_send, 6) }; fb_assert(FB_NELEM(protocols_to_try) <= FB_NELEM(cnct->p_cnct_versions)); cnct->p_cnct_count = FB_NELEM(protocols_to_try); @@ -634,51 +636,96 @@ rem_port* INET_analyze(ClntAuthBlock* cBlock, } rem_port* port = inet_try_connect(packet, rdb, file_name, node_name, dpb, config, ref_db_name, af); + P_ACPT* accept; - P_ACPT* accept = NULL; - switch (packet->p_operation) + for(;;) { - case op_accept_data: - case op_cond_accept: - accept = &packet->p_acpd; - if (cBlock) + accept = NULL; + switch (packet->p_operation) { - cBlock->storeDataForPlugin(packet->p_acpd.p_acpt_data.cstr_length, - packet->p_acpd.p_acpt_data.cstr_address); - cBlock->authComplete = packet->p_acpd.p_acpt_authenticated; - port->addServerKeys(&packet->p_acpd.p_acpt_keys); - cBlock->resetClnt(&file_name, &packet->p_acpd.p_acpt_keys); - } - break; + case op_accept_data: + case op_cond_accept: + accept = &packet->p_acpd; + if (cBlock) + { + cBlock->storeDataForPlugin(packet->p_acpd.p_acpt_data.cstr_length, + packet->p_acpd.p_acpt_data.cstr_address); + cBlock->authComplete = packet->p_acpd.p_acpt_authenticated; + port->addServerKeys(&packet->p_acpd.p_acpt_keys); + cBlock->resetClnt(&file_name, &packet->p_acpd.p_acpt_keys); + } + break; - case op_accept: - if (cBlock) - { - cBlock->resetClnt(&file_name); - } - accept = &packet->p_acpt; - break; + case op_accept: + if (cBlock) + { + cBlock->resetClnt(&file_name); + } + accept = &packet->p_acpt; + break; - case op_response: - try - { - LocalStatus warning; // Ignore connect warnings for a while - CheckStatusWrapper statusWrapper(&warning); - REMOTE_check_response(&statusWrapper, rdb, packet, false); - } - catch (const Exception&) - { + case op_crypt_key_callback: + try + { + UCharBuffer buf; + P_CRYPT_CALLBACK* cc = &packet->p_cc; + + if (cryptCb) + { + if (cc->p_cc_reply <= 0) + { + cc->p_cc_reply = 1; + } + UCHAR* reply = buf.getBuffer(cc->p_cc_reply); + unsigned l = cryptCb->callback(cc->p_cc_data.cstr_length, + cc->p_cc_data.cstr_address, cc->p_cc_reply, reply); + + REMOTE_free_packet(port, packet, true); + cc->p_cc_data.cstr_length = l; + cc->p_cc_data.cstr_address = reply; + } + else + { + REMOTE_free_packet(port, packet, true); + cc->p_cc_data.cstr_length = 0; + } + + packet->p_operation = op_crypt_key_callback; + cc->p_cc_reply = 0; + port->send(packet); + port->receive(packet); + continue; + } + catch (const Exception&) + { + disconnect(port); + delete rdb; + throw; + } + + case op_response: + try + { + LocalStatus warning; // Ignore connect warnings for a while + CheckStatusWrapper statusWrapper(&warning); + REMOTE_check_response(&statusWrapper, rdb, packet, false); + } + catch (const Exception&) + { + disconnect(port); + delete rdb; + throw; + } + // fall through - response is not a required accept + + default: disconnect(port); delete rdb; - throw; + Arg::Gds(isc_connect_reject).raise(); + break; } - // fall through - response is not a required accept - default: - disconnect(port); - delete rdb; - Arg::Gds(isc_connect_reject).raise(); - break; + break; // Always leave for() loop here } fb_assert(accept); @@ -720,7 +767,7 @@ rem_port* INET_connect(const TEXT* name, PACKET* packet, USHORT flag, ClumpletReader* dpb, - RefPtr* config, + RefPtr* config, int af) { /************************************** @@ -2662,7 +2709,7 @@ static rem_port* inet_try_connect(PACKET* packet, const PathName& file_name, const TEXT* node_name, ClumpletReader& dpb, - RefPtr* config, + RefPtr* config, const PathName* ref_db_name, int af) { diff --git a/src/remote/inet_proto.h b/src/remote/inet_proto.h index 6aeebace22..85db279bf5 100644 --- a/src/remote/inet_proto.h +++ b/src/remote/inet_proto.h @@ -34,10 +34,10 @@ namespace Firebird } rem_port* INET_analyze(ClntAuthBlock*, const Firebird::PathName&, const TEXT*, - bool, Firebird::ClumpletReader&, Firebird::RefPtr*, - const Firebird::PathName*, int af = AF_UNSPEC); + bool, Firebird::ClumpletReader&, Firebird::RefPtr*, + const Firebird::PathName*, Firebird::ICryptKeyCallback*, int af = AF_UNSPEC); rem_port* INET_connect(const TEXT*, struct packet*, USHORT, Firebird::ClumpletReader*, - Firebird::RefPtr*, int af = AF_UNSPEC); + Firebird::RefPtr*, int af = AF_UNSPEC); rem_port* INET_reconnect(SOCKET); rem_port* INET_server(SOCKET); void setStopMainThread(FPTR_INT func); diff --git a/src/remote/os/win32/wnet.cpp b/src/remote/os/win32/wnet.cpp index 21682a4541..6af1d51371 100644 --- a/src/remote/os/win32/wnet.cpp +++ b/src/remote/os/win32/wnet.cpp @@ -72,7 +72,7 @@ static void disconnect(rem_port*); static void exit_handler(void*); #endif static void force_close(rem_port*); -static rem_str* make_pipe_name(const RefPtr&, const TEXT*, const TEXT*, const TEXT*); +static rem_str* make_pipe_name(const RefPtr&, const TEXT*, const TEXT*, const TEXT*); static rem_port* receive(rem_port*, PACKET*); static int send_full(rem_port*, PACKET*); static int send_partial(rem_port*, PACKET*); @@ -104,7 +104,7 @@ rem_port* WNET_analyze(ClntAuthBlock* cBlock, const PathName& file_name, const TEXT* node_name, bool uv_flag, - RefPtr* config, + RefPtr* config, const Firebird::PathName* ref_db_name) { /************************************** @@ -269,7 +269,7 @@ rem_port* WNET_analyze(ClntAuthBlock* cBlock, } -rem_port* WNET_connect(const TEXT* name, PACKET* packet, USHORT flag, Firebird::RefPtr* config) +rem_port* WNET_connect(const TEXT* name, PACKET* packet, USHORT flag, Firebird::RefPtr* config) { /************************************** * @@ -800,7 +800,7 @@ static void exit_handler(void* main_port) #endif -static rem_str* make_pipe_name(const RefPtr& config, const TEXT* connect_name, +static rem_str* make_pipe_name(const RefPtr& config, const TEXT* connect_name, const TEXT* suffix_name, const TEXT* str_pid) { /************************************** diff --git a/src/remote/os/win32/wnet_proto.h b/src/remote/os/win32/wnet_proto.h index c2fc22288a..ab1723d1f8 100644 --- a/src/remote/os/win32/wnet_proto.h +++ b/src/remote/os/win32/wnet_proto.h @@ -32,8 +32,8 @@ extern "C" { rem_port* WNET_analyze(ClntAuthBlock*, const Firebird::PathName&, const TEXT*, bool, - Firebird::RefPtr*, const Firebird::PathName*); -rem_port* WNET_connect(const TEXT*, struct packet*, USHORT, Firebird::RefPtr*); + Firebird::RefPtr*, const Firebird::PathName*); +rem_port* WNET_connect(const TEXT*, struct packet*, USHORT, Firebird::RefPtr*); rem_port* WNET_reconnect(HANDLE); diff --git a/src/remote/os/win32/xnet.cpp b/src/remote/os/win32/xnet.cpp index cba8163b0e..f45862e91b 100644 --- a/src/remote/os/win32/xnet.cpp +++ b/src/remote/os/win32/xnet.cpp @@ -148,7 +148,7 @@ namespace Remote { } - rem_port* connect_client(PACKET*, const RefPtr*); + rem_port* connect_client(PACKET*, const RefPtr*); void server_shutdown(rem_port* port); private: @@ -238,7 +238,7 @@ static void xnet_log_error(const char* err_msg) rem_port* XNET_analyze(ClntAuthBlock* cBlock, const PathName& file_name, bool uv_flag, - RefPtr* config, + RefPtr* config, const Firebird::PathName* ref_db_name) { /************************************** @@ -403,7 +403,7 @@ rem_port* XNET_analyze(ClntAuthBlock* cBlock, rem_port* XNET_connect(PACKET* packet, USHORT flag, - Firebird::RefPtr* config) + Firebird::RefPtr* config) { /************************************** * @@ -1087,7 +1087,7 @@ static void raise_lostconn_or_syserror(const char* msg) } -rem_port* XnetClientEndPoint::connect_client(PACKET* packet, const RefPtr* config) +rem_port* XnetClientEndPoint::connect_client(PACKET* packet, const RefPtr* config) { /************************************** * @@ -1100,7 +1100,7 @@ rem_port* XnetClientEndPoint::connect_client(PACKET* packet, const RefPtr& conf(config ? *config : Config::getDefaultConfig()); + const Firebird::RefPtr& conf(config ? *config : Config::getDefaultConfig()); if (!xnet_initialized) { diff --git a/src/remote/os/win32/xnet_proto.h b/src/remote/os/win32/xnet_proto.h index cfd617ca18..4133827d56 100644 --- a/src/remote/os/win32/xnet_proto.h +++ b/src/remote/os/win32/xnet_proto.h @@ -32,9 +32,9 @@ #define rem_port void #endif -rem_port* XNET_analyze(ClntAuthBlock*, const Firebird::PathName&, bool, Firebird::RefPtr*, +rem_port* XNET_analyze(ClntAuthBlock*, const Firebird::PathName&, bool, Firebird::RefPtr*, const Firebird::PathName*); -rem_port* XNET_connect(struct packet*, USHORT, Firebird::RefPtr*); +rem_port* XNET_connect(struct packet*, USHORT, Firebird::RefPtr*); rem_port* XNET_reconnect(ULONG); #endif // REMOTE_XNET_PROTO_H diff --git a/src/remote/protocol.cpp b/src/remote/protocol.cpp index 8c4a4ab3da..ea31ff484f 100644 --- a/src/remote/protocol.cpp +++ b/src/remote/protocol.cpp @@ -803,7 +803,9 @@ bool_t xdr_protocol(XDR* xdrs, PACKET* p) MAP(xdr_cstring, cc->p_cc_data); rem_port* port = (rem_port*) xdrs->x_public; - if (port->port_protocol >= PROTOCOL_VERSION14) + // If the protocol is 0 we are in the process of establishing a connection. + // crypt_key_callback at this phaze means server protocol is at least P15 + if (port->port_protocol >= PROTOCOL_VERSION14 || port->port_protocol == 0) MAP(xdr_short, reinterpret_cast(cc->p_cc_reply)); DEBUG_PRINTSIZE(xdrs, p->p_operation); diff --git a/src/remote/protocol.h b/src/remote/protocol.h index 9ba758e932..35dc0e9d89 100644 --- a/src/remote/protocol.h +++ b/src/remote/protocol.h @@ -82,6 +82,11 @@ const USHORT PROTOCOL_VERSION13 = (FB_PROTOCOL_FLAG | 13); const USHORT PROTOCOL_VERSION14 = (FB_PROTOCOL_FLAG | 14); +// Protocol 15: +// - supports crypt key callback at connect phaze + +const USHORT PROTOCOL_VERSION15 = (FB_PROTOCOL_FLAG | 15); + // Architecture types enum P_ARCH diff --git a/src/remote/remot_proto.h b/src/remote/remot_proto.h index 7e2cdb6d25..09d99640b3 100644 --- a/src/remote/remot_proto.h +++ b/src/remote/remot_proto.h @@ -61,7 +61,7 @@ void REMOTE_reset_request (struct Rrq *, struct RMessage*); void REMOTE_reset_statement (struct Rsr *); bool_t REMOTE_getbytes (XDR*, SCHAR*, u_int); LegacyPlugin REMOTE_legacy_auth(const char* nm, int protocol); -Firebird::RefPtr REMOTE_get_config(const Firebird::PathName* dbName, +Firebird::RefPtr REMOTE_get_config(const Firebird::PathName* dbName, const Firebird::string* dpb_config = NULL); void REMOTE_check_response(Firebird::IStatus* warning, Rdb* rdb, PACKET* packet, bool checkKeys = false); bool REMOTE_inflate(rem_port*, PacketReceive*, UCHAR*, SSHORT, SSHORT*); diff --git a/src/remote/remote.cpp b/src/remote/remote.cpp index 574005e0ba..2862401732 100644 --- a/src/remote/remote.cpp +++ b/src/remote/remote.cpp @@ -604,7 +604,12 @@ void rem_port::linkParent(rem_port* const parent) parent->port_clients = parent->port_next = this; } -const Firebird::RefPtr& rem_port::getPortConfig() const +const Firebird::RefPtr& rem_port::getPortConfig() const +{ + return port_config.hasData() ? port_config : Config::getDefaultConfig(); +} + +Firebird::RefPtr rem_port::getPortConfig() { return port_config.hasData() ? port_config : Config::getDefaultConfig(); } @@ -1006,7 +1011,7 @@ void ClntAuthBlock::resetClnt(const Firebird::PathName* fileName, const CSTRING* plugins.set(final.c_str()); } -Firebird::RefPtr* ClntAuthBlock::getConfig() +Firebird::RefPtr* ClntAuthBlock::getConfig() { return clntConfig.hasData() ? &clntConfig : NULL; } @@ -1017,10 +1022,10 @@ void ClntAuthBlock::storeDataForPlugin(unsigned int length, const unsigned char* HANDSHAKE_DEBUG(fprintf(stderr, "Cli: accepted data for plugin length=%d\n", length)); } -Firebird::RefPtr REMOTE_get_config(const Firebird::PathName* dbName, +Firebird::RefPtr REMOTE_get_config(const Firebird::PathName* dbName, const Firebird::string* dpb_config) { - Firebird::RefPtr config; + Firebird::RefPtr config; if (dbName && dbName->hasData()) { diff --git a/src/remote/remote.h b/src/remote/remote.h index cee570f609..4f73c3cdbe 100644 --- a/src/remote/remote.h +++ b/src/remote/remote.h @@ -703,7 +703,7 @@ private: Firebird::UCharBuffer dataForPlugin, dataFromPlugin; Firebird::HalfStaticArray cryptKeys; // Wire crypt keys that came from plugin(s) last time Firebird::string dpbConfig; // Used to recreate config with new filename - Firebird::RefPtr clntConfig; // Used to get plugins list and pass to port + Firebird::RefPtr clntConfig; // Used to get plugins list and pass to port unsigned nextKey; // First key to be analyzed bool hasCryptKey; // DPB contains disk crypt key, may be passed only over encrypted wire @@ -733,7 +733,7 @@ public: Firebird::PathName getPluginName(); void tryNewKeys(rem_port*); void releaseKeys(unsigned from); - Firebird::RefPtr* getConfig(); + Firebird::RefPtr* getConfig(); // Firebird::IClientBlock implementation int release(); @@ -947,7 +947,7 @@ struct rem_port : public Firebird::GlobalStorage, public Firebird::RefCounted OBJCT port_last_object_id; // cached last id Firebird::ObjectsArray< Firebird::Array > port_queue; FB_SIZE_T port_qoffset; // current packet in the queue - Firebird::RefPtr port_config; // connection-specific configuration info + Firebird::RefPtr port_config; // connection-specific configuration info // Authentication and crypt stuff ServerAuthBase* port_srv_auth; @@ -1023,7 +1023,8 @@ public: static bool checkCompression(); void linkParent(rem_port* const parent); void unlinkParent(); - const Firebird::RefPtr& getPortConfig() const; + Firebird::RefPtr getPortConfig(); + const Firebird::RefPtr& getPortConfig() const; void versionInfo(Firebird::string& version) const; bool extractNewKeys(CSTRING* to, bool flagPlugList = false) diff --git a/src/remote/server/os/posix/inet_server.cpp b/src/remote/server/os/posix/inet_server.cpp index f22f898cf2..56f6a1ff9b 100644 --- a/src/remote/server/os/posix/inet_server.cpp +++ b/src/remote/server/os/posix/inet_server.cpp @@ -399,7 +399,7 @@ int CLIB_ROUTINE main( int argc, char** argv) ISC_STATUS_ARRAY status; isc_db_handle db_handle = 0L; - const Firebird::RefPtr defConf(Config::getDefaultConfig()); + const Firebird::RefPtr defConf(Config::getDefaultConfig()); const char* path = defConf->getSecurityDatabase(); const char dpb[] = {isc_dpb_version1, isc_dpb_sec_attach, 1, 1, isc_dpb_address_path, 0}; diff --git a/src/remote/server/server.cpp b/src/remote/server/server.cpp index 0031b38097..bd6c50bde9 100644 --- a/src/remote/server/server.cpp +++ b/src/remote/server/server.cpp @@ -105,7 +105,175 @@ public: namespace { +// DB crypt key passthrough + +class NetworkCallback : public VersionedIface > +{ +public: + explicit NetworkCallback(rem_port* prt) + : port(prt), l(0), d(NULL), stopped(false), wake(false) + { } + + unsigned int callback(unsigned int dataLength, const void* data, + unsigned int bufferLength, void* buffer) + { + if (stopped) + return 0; + + if (port->port_protocol < PROTOCOL_VERSION13) + return 0; + + Reference r(*port); + + d = buffer; + l = bufferLength; + + PACKET p; + p.p_operation = op_crypt_key_callback; + p.p_cc.p_cc_data.cstr_length = dataLength; + p.p_cc.p_cc_data.cstr_address = (UCHAR*) data; + p.p_cc.p_cc_reply = bufferLength; + port->send(&p); + + if (!sem.tryEnter(60)) + return 0; + + return l; + } + + void wakeup(unsigned int length, const void* data) + { + if (l > length) + l = length; + memcpy(d, data, l); + + wake = true; + sem.release(); + } + + void stop() + { + stopped = true; + } + + bool isStopped() const + { + return stopped; + } + +private: + rem_port* port; + Semaphore sem; + unsigned int l; + void* d; + bool stopped; + +public: + bool wake; +}; + +class CryptKeyCallback : public VersionedIface > +{ +public: + explicit CryptKeyCallback(rem_port* prt) + : port(prt), networkCallback(prt), keyHolder(NULL), keyCallback(NULL) + { } + + ~CryptKeyCallback() + { + if (keyHolder) + PluginManagerInterfacePtr()->releasePlugin(keyHolder); + } + + unsigned int callback(unsigned int dataLength, const void* data, + unsigned int bufferLength, void* buffer) + { + if (keyCallback) + return keyCallback->callback(dataLength, data, bufferLength, buffer); + + if (networkCallback.isStopped()) + return 0; + + Reference r(*port); + + for (GetPlugins kh(IPluginManager::TYPE_KEY_HOLDER, port->getPortConfig()); + kh.hasData(); kh.next()) + { + IKeyHolderPlugin* keyPlugin = kh.plugin(); + LocalStatus ls; + CheckStatusWrapper st(&ls); + + networkCallback.wake = false; + if (keyPlugin->keyCallback(&st, &networkCallback) && networkCallback.wake) + { + // current holder has a key and it seems to be from the client + keyHolder = keyPlugin; + keyHolder->addRef(); + keyCallback = keyHolder->chainHandle(&st); + + if (st.isEmpty() && keyCallback) + break; + } + } + + unsigned rc = keyCallback ? + keyCallback->callback(dataLength, data, bufferLength, buffer) : + // use legacy behavior if holders to do wish to accept keys from client + networkCallback.callback(dataLength, data, bufferLength, buffer); + + //stop(); + return rc; + } + + void wakeup(unsigned int length, const void* data) + { + networkCallback.wakeup(length, data); + } + + void stop() + { + networkCallback.stop(); + } + +private: + rem_port* port; + NetworkCallback networkCallback; + IKeyHolderPlugin* keyHolder; + ICryptKeyCallback* keyCallback; +}; + +class ServerCallback : public ServerCallbackBase, public GlobalStorage +{ +public: + explicit ServerCallback(rem_port* prt) + : cryptCallback(prt) + { } + + ~ServerCallback() + { } + + void wakeup(unsigned int length, const void* data) + { + cryptCallback.wakeup(length, data); + } + + ICryptKeyCallback* getInterface() + { + return &cryptCallback; + } + + void stop() + { + cryptCallback.stop(); + } + +private: + CryptKeyCallback cryptCallback; +}; + + // Disable attempts to brute-force logins/passwords + class FailedLogin { public: @@ -287,6 +455,7 @@ static void getMultiPartConnectParameter(T& putTo, Firebird::ClumpletReader& id, // delayed authentication block for auth callback + class ServerAuth : public GlobalStorage, public ServerAuthBase { public: @@ -379,6 +548,11 @@ public: authPort->port_srv_auth_block->setDataForPlugin(u); } #endif + + if (!authPort->port_server_crypt_callback) + { + authPort->port_server_crypt_callback = FB_NEW ServerCallback(authPort); + } } ~ServerAuth() @@ -425,6 +599,13 @@ public: { authServer = authItr->plugin(); authPort->port_srv_auth_block->authBlockWriter.setPlugin(authItr->name()); + + if (authPort->getPortConfig()->getCryptSecurityDatabase() && + authPort->port_protocol >= PROTOCOL_VERSION15 && + authPort->port_server_crypt_callback) + { + authServer->setDbCryptCallback(&st, authPort->port_server_crypt_callback->getInterface()); + } } // if we asked for more data but received nothing switch to next plugin @@ -774,6 +955,9 @@ private: Rvnt* event; }; + +// Stores types of known wire crypt keys + class CryptKeyTypeManager : public PermanentStorage { class CryptKeyType : public PermanentStorage @@ -868,91 +1052,6 @@ private: InitInstance knownCryptKeyTypes; -class CryptKeyCallback : public VersionedIface > -{ -public: - explicit CryptKeyCallback(rem_port* prt) - : port(prt), l(0), d(NULL), stopped(false) - { } - - unsigned int callback(unsigned int dataLength, const void* data, - unsigned int bufferLength, void* buffer) - { - if (stopped) - return 0; - - Reference r(*port); - - PACKET p; - p.p_operation = op_crypt_key_callback; - p.p_cc.p_cc_data.cstr_length = dataLength; - p.p_cc.p_cc_data.cstr_address = (UCHAR*) data; - p.p_cc.p_cc_reply = bufferLength; - port->send(&p); - - if (!sem.tryEnter(10)) - return 0; - - if (bufferLength > l) - bufferLength = l; - memcpy(buffer, d, bufferLength); - if (l) - sem2.release(); - - return l; - } - - void wakeup(unsigned int length, const void* data) - { - l = length; - d = data; - sem.release(); - if (l) - sem2.enter(); - } - - void stop() - { - stopped = true; - } - -private: - rem_port* port; - Semaphore sem, sem2; - unsigned int l; - const void* d; - bool stopped; -}; - -class ServerCallback : public ServerCallbackBase, public GlobalStorage -{ -public: - explicit ServerCallback(rem_port* prt) - : cryptCallback(prt) - { } - - ~ServerCallback() - { } - - void wakeup(unsigned int length, const void* data) - { - cryptCallback.wakeup(length, data); - } - - ICryptKeyCallback* getInterface() - { - return &cryptCallback; - } - - void stop() - { - cryptCallback.stop(); - } - -private: - CryptKeyCallback cryptCallback; -}; - } // anonymous static void free_request(server_req_t*); @@ -1715,11 +1814,8 @@ static bool accept_connection(rem_port* port, P_CNCT* connect, PACKET* send) for (const p_cnct::p_cnct_repeat* const end = protocol + connect->p_cnct_count; protocol < end; protocol++) { - if ((protocol->p_cnct_version == PROTOCOL_VERSION10 || - protocol->p_cnct_version == PROTOCOL_VERSION11 || - protocol->p_cnct_version == PROTOCOL_VERSION12 || - protocol->p_cnct_version == PROTOCOL_VERSION13 || - protocol->p_cnct_version == PROTOCOL_VERSION14) && + if ((protocol->p_cnct_version >= PROTOCOL_VERSION10 && + protocol->p_cnct_version <= PROTOCOL_VERSION15) && (protocol->p_cnct_architecture == arch_generic || protocol->p_cnct_architecture == ARCHITECTURE) && protocol->p_cnct_weight >= weight) @@ -2239,13 +2335,10 @@ void DatabaseAuth::accept(PACKET* send, Auth::WriterImplementation* authBlock) const UCHAR* dpb = pb->getBuffer(); unsigned int dl = (ULONG) pb->getBufferLength(); - if (!authPort->port_server_crypt_callback) - { - authPort->port_server_crypt_callback = FB_NEW ServerCallback(authPort); - } - LocalStatus ls; CheckStatusWrapper status_vector(&ls); + + fb_assert(authPort->port_server_crypt_callback); provider->setDbCryptCallback(&status_vector, authPort->port_server_crypt_callback->getInterface()); if (!(status_vector.getState() & Firebird::IStatus::STATE_ERRORS)) @@ -5378,16 +5471,13 @@ ISC_STATUS rem_port::service_attach(const char* service_name, // they will be stuffed in the SPB if so. REMOTE_get_timeout_params(this, spb); - if (!port_server_crypt_callback) - { - port_server_crypt_callback = FB_NEW ServerCallback(this); - } - DispatcherPtr provider; LocalStatus ls; CheckStatusWrapper status_vector(&ls); + fb_assert(port_server_crypt_callback); provider->setDbCryptCallback(&status_vector, port_server_crypt_callback->getInterface()); + if (!(status_vector.getState() & Firebird::IStatus::STATE_ERRORS)) { dumpAuthBlock("rem_port::service_attach()", spb, isc_spb_auth_block); @@ -6500,7 +6590,7 @@ void SrvAuthBlock::createPluginsItr() REMOTE_makeList(pluginList, final); - RefPtr portConf(port->getPortConfig()); + RefPtr portConf(port->getPortConfig()); plugins = FB_NEW AuthServerPlugins(IPluginManager::TYPE_AUTH_SERVER, portConf, pluginList.c_str()); } diff --git a/src/utilities/gsec/gsec.cpp b/src/utilities/gsec/gsec.cpp index 8f2f0ac6b7..687c33a7e5 100644 --- a/src/utilities/gsec/gsec.cpp +++ b/src/utilities/gsec/gsec.cpp @@ -379,7 +379,7 @@ int gsec(Firebird::UtilSvc* uSvc) } else { - const Firebird::RefPtr defConf(Config::getDefaultConfig()); + const Firebird::RefPtr defConf(Config::getDefaultConfig()); databaseName = defConf->getSecurityDatabase(); } @@ -460,8 +460,8 @@ int gsec(Firebird::UtilSvc* uSvc) Firebird::string databaseText; databaseText.printf("SecurityDatabase = %s\n", databaseName.c_str()); ConfigFile gsecDatabase(ConfigFile::USE_TEXT, databaseText.c_str()); - Firebird::RefPtr defaultConfig(Config::getDefaultConfig()); - Firebird::RefPtr pseudoConfig(FB_NEW Config(gsecDatabase, *defaultConfig)); + Firebird::RefPtr defaultConfig(Config::getDefaultConfig()); + Firebird::RefPtr pseudoConfig(FB_NEW Config(gsecDatabase, *defaultConfig)); uSvc->checkService(); diff --git a/src/yvalve/PluginManager.cpp b/src/yvalve/PluginManager.cpp index 7cf73764ca..9bb607e945 100644 --- a/src/yvalve/PluginManager.cpp +++ b/src/yvalve/PluginManager.cpp @@ -563,7 +563,7 @@ namespace { if (!firebirdConf.hasData()) { - RefPtr specificConf(Config::getDefaultConfig()); + RefPtr specificConf(Config::getDefaultConfig()); firebirdConf = FB_NEW FirebirdConf(specificConf); } @@ -1257,7 +1257,7 @@ public: try { PathName dummy; - Firebird::RefPtr config; + Firebird::RefPtr config; expandDatabaseName(dbName, dummy, &config); IFirebirdConf* firebirdConf = FB_NEW FirebirdConf(config); diff --git a/src/yvalve/why.cpp b/src/yvalve/why.cpp index 8a703e35c8..4b27496e75 100644 --- a/src/yvalve/why.cpp +++ b/src/yvalve/why.cpp @@ -5663,7 +5663,7 @@ YAttachment* Dispatcher::attachOrCreateDatabase(Firebird::CheckStatusWrapper* st orgFilename.rtrim(); PathName expandedFilename; - RefPtr config; + RefPtr config; if (expandDatabaseName(orgFilename, expandedFilename, &config)) { expandedFilename = orgFilename; @@ -5790,7 +5790,7 @@ YService* Dispatcher::attachServiceManager(CheckStatusWrapper* status, const cha } // Build correct config - RefPtr config(Config::getDefaultConfig()); + RefPtr config(Config::getDefaultConfig()); if (spbWriter.find(isc_spb_config)) { string spb_config;