Firebird plugins.

Since version 3, Firebird supports plugins architecture. This means that for a number of predefined points in Firebird code, user can write his own fragment of code which will be executed when needed. Plugin is not necessarily always written by user - Firebird already has a number of plugins which are its native part. Moreover, as you will see later, some core parts of Firebird are implemented as plugins.

First of all a few words about the term “plugin”. Unfortunately, it's often used to define related but different things. Plugin is used to name:

- dynamic library, containing code to be loaded as plugin (often called plugin module) and stored in $FIREBIRD/plugins directory;

- code implementing plugin – slightly different from the library cause single dynamic library may contain code for more than one plugin;

- plugin's factory - an object created by that code (pure virtual C++ class), creating plugin instances on Firebird request;

- instance of plugin, created by factory.

In most cases it's clear from context what “plugin” do we talk about. If not it will be clarified explicitly.

Firebird plugin architecture supports creation of any plugin type for any purpose – but first of all this requires changes in Firebird code. Plugin can't be added at any desired point “magically”. To be able to have plugin (for example) encrypting database on the disk, Firebird code should be prepared for it – must have a point from which plugin is called. I.e. each version has a fixed set of plugins which are supported. To add one more type, first of all Firebird code should be modified. What DOES our plugin architecture – it helps to make both adding new types of plugins and writing plugin code simple and as universal between plugins as possible.

Firebird 3 has a following set of plugin types:

- user authentication related: AuthServer (validates user's credentials on server when use logins), AuthClient (prepares credentials to be passed over the wire) and AuthUserManagement (maintains a list of users on a server in a form, known to AuthServer);

- ExternalEngine controls use of various engines, see README.external_routines;

- Trace plugin is known from Firebird 2.5, but a way how it interacts with engine was changed to match new generic rules;

- encrypting plugins are for network (WireCrypt) and disk (DbCrypt), there is also helper plugin KeyHolder, which is used to help maintaining secret key(s) for DbCrypt;

- and probably the most important kind – Provider. Firebird 3 supports providers as a kind of plugins, which has nothing outstanding compared with others. See README.Providers for more information about providers.

Plugins are using a set of special Firebird interfaces (see Using_OO_API for details). All plugin-specific interfaces are reference counted, i.e. have explicitly controlled lifetime. Interfaces are declared in Interfaces.h include file. There is an example of writing plugin module – DbCrypt_example. It does not perform any actual encryption – just a sample of how to write plugin. Complete instruction of how to write plugins is out of this document's scope. Here is provided a short list of plugin features:

- plugin may be written using any language, supporting function pointers in a structure/record (or at least arrays of function pointers);

- currently interfaces description is available only for C++ and Pascal(Delphi), you will need to write your interfaces declarations generator for your language if you need something else;

- like with UDFs you are free to add any reasonable code to your plugin, but pay attention to word “reasonable” - asking a question from plugin at server's console is hardly good idea;

- it's OK to use Firebird API calls in your plugin if needed (for example, default authentication server and user manager are using Firebird database to store accounts);

- additionally Firebird provides a set of interfaces, helping you to configure your plugins (certainly, you are not forced to use them – plugin is generic code, which may use any way of providing configuration information, but with standard tools you get common for the rest of Firebird configuration style and sooner of all save you efforts).

Configuring plugins has 2 parts – first, engine should be instructed what plugins it should load, and next plugins themselves sometimes need some configuration. What plugins to be loaded is defined in main configuration file – firebird.conf for each type of plugin. Like any other value in firebird.conf the have defaults:

AuthServer = Srp

AuthClient = Srp, Win_Sspi, Legacy_Auth

UserManager = Srp

TracePlugin = fbtrace

Providers = Remote,Engine12,Loopback

WireCryptPlugin = Arc4

This provides normal operation in server, client and embedded cases. If you want to add other plugins, you must mention them in firebird.conf – except other this is security measure to avoid loading unknown code. But what does for example fbtrace mean here? Is it a name of dynamic library to load? In trivial case yes, but exact answer is more complicated.

As it was already mentioned, single plugin module may implement more than single plugin. Moreover, single plugin may have at the same time more than one configuration – and for each configuration separate plugin's factory is created. Each of this 3 objects (module – implementation – factory) has it's own name. Name of a module is a file name of dynamic library. Plugin implementation's name is one given to it by plugin developer and hard-coded inside module. Factory's name by default equals to plugin implementation's name (and it's factory name which is actually used in firebird.conf). Certainly in typical case, module contains one plugin, and that plugin works with only one configuration, and all 3 names are equal, and no more configuration is needed – for example libEngine12.so or Engine12.dll contains implementation of provider Engine12, and nothing else except record

Providers = Engine12

in firebird.conf is needed to load it. But if you have something complex – configuration file plugins.conf will help you to have such plugin factories which you really want.

File plugins.conf has 2 types of records – config and plugin. Plugin record is a set of rules for plugin's loading and activating. Plugin record has the following format:

Plugin = PlugName ## this is name to be referenced in firebird.conf

{

Module = LibName ## name of dynamic library

RegisterName = RegName ## name given to plugin by it's developer

Config = ConfName ## name of config record to be used

ConfigFile = ConfFile ## name of a file, containing plugin's configuration

}

i.e. when plugin PlugName is needed Firebird loads library LibName, finds in it plugin registered with name RegName and passes it configuration from config record ConfName or config file ConfFile (config record is used if both are given). Each parameter in this record may be missing, in that case the default PlugName is used. The only exception is ConfigFile – by default, file with same name as module's dynamic library but .conf extension is used. ConfigFile is expected to have format Key=Value (like other Firebird configuration files), same format is used for plugin record:

Config = ConfName

{

Key1 = Value1

Key2 = Value2

...

}

Let's have a sample. Suppose some clients of your server trust wire encryption from one vendor and others – from another one (and have different licenses for appropriate client parts), but each vendor calls his plugin BestCrypt. Certainly, first of all you have to rename libraries to something like Crypt1 and Crypt2 – one can't have 2 files with same name in one directory. But after it, modules stop to load automatically – they are not named BestCrypt any more. To fix it, plugins.conf should contain something like this:

Plugin = Crypt1

{

RegisterName = BestCrypt

}

Plugin = Crypt2

{

RegisterName = BestCrypt

}

Module names will be automatically set to Crypt1 and Crypt2 and found. Certainly you may add some configuration info for plugins if needed. Also don't forget to modify firebird.conf:

WireCryptPlugin = Crypt1, Crypt2

After it server will automatically select appropriate plugin to talk to client.

Another sample may be found in distributed with Firebird plugins.conf. One of standard plugins, UDR, is written to use non-default configuration. Therefore module name and one configuration parameter are given explicitly.

Questions and answers.

Q. There are plugins named Remote, Loopback, Arc4 in default configuration, but no libraries with such names. How do they work?

A. That are so-called 'built-in' plugins. They are built into fbclient library, and therefore are always present. Arrival of such plugins is due to old ability to distribute windows Firebird client as single dll. It was decided to keep such feature at least for a case when standard set of plugins is used.

Q. What do names of Srp and Arc4 plugins mean?

A. Srp implements Secure Remote Passwords protocol which is default way of authenticating users in Firebird 3. It has efficient password’s length equal to 20 bytes, resistant to most of attacks (including man in the middle) and does not require exchanging any keys between client and server to work. Arc4 means Alleged RC4 - an implementation of RC4 cypher. The advantage of SRP is that it can generate unique cryptographically strong key on both client and server and it's impossible to guess it capturing data transferred over the wire during password validation by SRP. That key is used after SRP handshake by Arc4, which makes wire encryption secure without need to exchange any keys between client and server explicitly.

Q. And what do Win_Sspi and Legacy_Auth mean?

A. Windows SSPI was used since FB 2.1 for windows trusted authentication. Legacy_Auth is compatibility plugin. It's enabled by default on client to let it connect to pre-FB3 servers. (Yes – it still transfers almost plain passwords over the wire. Compatibility...) On server it works with security database from FB 2.5, and should be avoided except cases when you understand well what are you doing. To use Legacy_Auth on server you should set lower level of network traffic encryption in firebird.conf:

WireCrypt = Enabled

or in the worst case:

WireCrypt = Disabled