8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 20:43:02 +01:00

Implemented #7951: Make it possible to handle attach errors in key holder plugin.

This commit is contained in:
AlexPeshkoff 2024-01-11 13:45:04 +03:00
parent 6dce8db8ca
commit c2238907a7
6 changed files with 382 additions and 69 deletions

View File

@ -941,6 +941,21 @@ interface CryptKeyCallback : Versioned
// any further details.
uint callback(uint dataLength, const void* data,
uint bufferLength, void* buffer);
version: // 6.0
// Result returned by afterAttach()
const uint NO_RETRY = 0; // Returned by old plugins & stub
const uint DO_RETRY = 1;
// NULL in attStatus means attach was successful.
// DO_RETRY return will be ignored in this case, but plugin has a chance
// to reflect internally success.
[stub defaultAction]
uint afterAttach(Status status, const string dbName, const Status attStatus);
// interface not needed any more
[stub defaultAction]
void dispose();
}

View File

@ -3891,7 +3891,7 @@ namespace Firebird
}
};
#define FIREBIRD_ICRYPT_KEY_CALLBACK_VERSION 2u
#define FIREBIRD_ICRYPT_KEY_CALLBACK_VERSION 3u
class ICryptKeyCallback : public IVersioned
{
@ -3899,6 +3899,8 @@ namespace Firebird
struct VTable : public IVersioned::VTable
{
unsigned (CLOOP_CARG *callback)(ICryptKeyCallback* self, unsigned dataLength, const void* data, unsigned bufferLength, void* buffer) CLOOP_NOEXCEPT;
unsigned (CLOOP_CARG *afterAttach)(ICryptKeyCallback* self, IStatus* status, const char* dbName, const IStatus* attStatus) CLOOP_NOEXCEPT;
void (CLOOP_CARG *dispose)(ICryptKeyCallback* self) CLOOP_NOEXCEPT;
};
protected:
@ -3914,11 +3916,37 @@ namespace Firebird
public:
static CLOOP_CONSTEXPR unsigned VERSION = FIREBIRD_ICRYPT_KEY_CALLBACK_VERSION;
static CLOOP_CONSTEXPR unsigned NO_RETRY = 0;
static CLOOP_CONSTEXPR unsigned DO_RETRY = 1;
unsigned callback(unsigned dataLength, const void* data, unsigned bufferLength, void* buffer)
{
unsigned ret = static_cast<VTable*>(this->cloopVTable)->callback(this, dataLength, data, bufferLength, buffer);
return ret;
}
template <typename StatusType> unsigned afterAttach(StatusType* status, const char* dbName, const IStatus* attStatus)
{
if (cloopVTable->version < 3)
{
StatusType::setVersionError(status, "ICryptKeyCallback", cloopVTable->version, 3);
StatusType::checkException(status);
return 0;
}
StatusType::clearException(status);
unsigned ret = static_cast<VTable*>(this->cloopVTable)->afterAttach(this, status, dbName, attStatus);
StatusType::checkException(status);
return ret;
}
void dispose()
{
if (cloopVTable->version < 3)
{
return;
}
static_cast<VTable*>(this->cloopVTable)->dispose(this);
}
};
#define FIREBIRD_IKEY_HOLDER_PLUGIN_VERSION 5u
@ -14395,6 +14423,8 @@ namespace Firebird
{
this->version = Base::VERSION;
this->callback = &Name::cloopcallbackDispatcher;
this->afterAttach = &Name::cloopafterAttachDispatcher;
this->dispose = &Name::cloopdisposeDispatcher;
}
} vTable;
@ -14413,6 +14443,33 @@ namespace Firebird
return static_cast<unsigned>(0);
}
}
static unsigned CLOOP_CARG cloopafterAttachDispatcher(ICryptKeyCallback* self, IStatus* status, const char* dbName, const IStatus* attStatus) CLOOP_NOEXCEPT
{
StatusType status2(status);
try
{
return static_cast<Name*>(self)->Name::afterAttach(&status2, dbName, attStatus);
}
catch (...)
{
StatusType::catchException(&status2);
return static_cast<unsigned>(0);
}
}
static void CLOOP_CARG cloopdisposeDispatcher(ICryptKeyCallback* self) CLOOP_NOEXCEPT
{
try
{
static_cast<Name*>(self)->Name::dispose();
}
catch (...)
{
StatusType::catchException(0);
}
}
};
template <typename Name, typename StatusType, typename Base = IVersionedImpl<Name, StatusType, Inherit<ICryptKeyCallback> > >
@ -14429,6 +14486,13 @@ namespace Firebird
}
virtual unsigned callback(unsigned dataLength, const void* data, unsigned bufferLength, void* buffer) = 0;
virtual unsigned afterAttach(StatusType* status, const char* dbName, const IStatus* attStatus)
{
return 0;
}
virtual void dispose()
{
}
};
template <typename Name, typename StatusType, typename Base>

View File

@ -490,6 +490,8 @@ type
IWireCryptPlugin_getSpecificDataPtr = function(this: IWireCryptPlugin; status: IStatus; keyType: PAnsiChar; length: CardinalPtr): BytePtr; cdecl;
IWireCryptPlugin_setSpecificDataPtr = procedure(this: IWireCryptPlugin; status: IStatus; keyType: PAnsiChar; length: Cardinal; data: BytePtr); cdecl;
ICryptKeyCallback_callbackPtr = function(this: ICryptKeyCallback; dataLength: Cardinal; data: Pointer; bufferLength: Cardinal; buffer: Pointer): Cardinal; cdecl;
ICryptKeyCallback_afterAttachPtr = function(this: ICryptKeyCallback; status: IStatus; dbName: PAnsiChar; attStatus: IStatus): Cardinal; cdecl;
ICryptKeyCallback_disposePtr = procedure(this: ICryptKeyCallback); cdecl;
IKeyHolderPlugin_keyCallbackPtr = function(this: IKeyHolderPlugin; status: IStatus; callback: ICryptKeyCallback): Integer; cdecl;
IKeyHolderPlugin_keyHandlePtr = function(this: IKeyHolderPlugin; status: IStatus; keyName: PAnsiChar): ICryptKeyCallback; cdecl;
IKeyHolderPlugin_useOnlyOwnKeysPtr = function(this: IKeyHolderPlugin; status: IStatus): Boolean; cdecl;
@ -2375,18 +2377,26 @@ type
CryptKeyCallbackVTable = class(VersionedVTable)
callback: ICryptKeyCallback_callbackPtr;
afterAttach: ICryptKeyCallback_afterAttachPtr;
dispose: ICryptKeyCallback_disposePtr;
end;
ICryptKeyCallback = class(IVersioned)
const VERSION = 2;
const VERSION = 3;
const NO_RETRY = Cardinal(0);
const DO_RETRY = Cardinal(1);
function callback(dataLength: Cardinal; data: Pointer; bufferLength: Cardinal; buffer: Pointer): Cardinal;
function afterAttach(status: IStatus; dbName: PAnsiChar; attStatus: IStatus): Cardinal;
procedure dispose();
end;
ICryptKeyCallbackImpl = class(ICryptKeyCallback)
constructor create;
function callback(dataLength: Cardinal; data: Pointer; bufferLength: Cardinal; buffer: Pointer): Cardinal; virtual; abstract;
function afterAttach(status: IStatus; dbName: PAnsiChar; attStatus: IStatus): Cardinal; virtual;
procedure dispose(); virtual;
end;
KeyHolderPluginVTable = class(PluginBaseVTable)
@ -8104,6 +8114,27 @@ begin
Result := CryptKeyCallbackVTable(vTable).callback(Self, dataLength, data, bufferLength, buffer);
end;
function ICryptKeyCallback.afterAttach(status: IStatus; dbName: PAnsiChar; attStatus: IStatus): Cardinal;
begin
if (vTable.version < 3) then begin
FbException.setVersionError(status, 'ICryptKeyCallback', vTable.version, 3);
Result := 0;
end
else begin
Result := CryptKeyCallbackVTable(vTable).afterAttach(Self, status, dbName, attStatus);
end;
FbException.checkException(status);
end;
procedure ICryptKeyCallback.dispose();
begin
if (vTable.version < 3) then begin
end
else begin
CryptKeyCallbackVTable(vTable).dispose(Self);
end;
end;
function IKeyHolderPlugin.keyCallback(status: IStatus; callback: ICryptKeyCallback): Integer;
begin
Result := KeyHolderPluginVTable(vTable).keyCallback(Self, status, callback);
@ -13432,6 +13463,34 @@ begin
end
end;
function ICryptKeyCallbackImpl_afterAttachDispatcher(this: ICryptKeyCallback; status: IStatus; dbName: PAnsiChar; attStatus: IStatus): Cardinal; cdecl;
begin
Result := 0;
try
Result := ICryptKeyCallbackImpl(this).afterAttach(status, dbName, attStatus);
except
on e: Exception do FbException.catchException(status, e);
end
end;
function ICryptKeyCallbackImpl.afterAttach(status: IStatus; dbName: PAnsiChar; attStatus: IStatus): Cardinal;
begin
Result := 0;
end;
procedure ICryptKeyCallbackImpl_disposeDispatcher(this: ICryptKeyCallback); cdecl;
begin
try
ICryptKeyCallbackImpl(this).dispose();
except
on e: Exception do FbException.catchException(nil, e);
end
end;
procedure ICryptKeyCallbackImpl.dispose();
begin
end;
var
ICryptKeyCallbackImpl_vTable: CryptKeyCallbackVTable;
@ -17517,8 +17576,10 @@ initialization
IWireCryptPluginImpl_vTable.setSpecificData := @IWireCryptPluginImpl_setSpecificDataDispatcher;
ICryptKeyCallbackImpl_vTable := CryptKeyCallbackVTable.create;
ICryptKeyCallbackImpl_vTable.version := 2;
ICryptKeyCallbackImpl_vTable.version := 3;
ICryptKeyCallbackImpl_vTable.callback := @ICryptKeyCallbackImpl_callbackDispatcher;
ICryptKeyCallbackImpl_vTable.afterAttach := @ICryptKeyCallbackImpl_afterAttachDispatcher;
ICryptKeyCallbackImpl_vTable.dispose := @ICryptKeyCallbackImpl_disposeDispatcher;
IKeyHolderPluginImpl_vTable := KeyHolderPluginVTable.create;
IKeyHolderPluginImpl_vTable.version := 5;

View File

@ -8393,7 +8393,10 @@ static bool init(CheckStatusWrapper* status, ClntAuthBlock& cBlock, rem_port* po
port->port_client_crypt_callback = cryptCallback;
cBlock.createCryptCallback(&port->port_client_crypt_callback);
auto cb = port->port_client_crypt_callback;
for(;;)
{
// Make attach packet
P_ATCH* attach = &packet->p_atch;
packet->p_operation = op;
@ -8403,10 +8406,38 @@ static bool init(CheckStatusWrapper* status, ClntAuthBlock& cBlock, rem_port* po
attach->p_atch_dpb.cstr_address = dpb.getBuffer();
send_packet(port, packet);
try
{
authReceiveResponse(false, cBlock, port, rdb, status, packet, true);
}
catch (const Exception& ex)
{
FbLocalStatus stAttach;
ex.stuffException(&stAttach);
const ISC_STATUS* v = stAttach->getErrors();
if (cb && (fb_utils::containsErrorCode(v, isc_bad_crypt_key) ||
fb_utils::containsErrorCode(v, isc_db_crypt_key)) &&
(cb->afterAttach(status, file_name.c_str(), &stAttach) == ICryptKeyCallback::DO_RETRY))
{
continue;
}
status->init();
throw;
}
// response is success
if (cb)
{
cb->afterAttach(status, file_name.c_str(), nullptr);
status->init();
}
return true;
}
}
catch (const Exception& ex)
{
// report primary init error
@ -9829,6 +9860,14 @@ Firebird::ICryptKeyCallback* ClntAuthBlock::ClientCrypt::create(const Config* co
unsigned ClntAuthBlock::ClientCrypt::callback(unsigned dlen, const void* data, unsigned blen, void* buffer)
{
// if we have a retry iface - use it
if (afterIface)
{
unsigned retlen = afterIface->callback(dlen, data, blen, buffer);
HANDSHAKE_DEBUG(fprintf(stderr, "Iface %p returned %d\n", currentIface, retlen));
return retlen;
}
HANDSHAKE_DEBUG(fprintf(stderr, "dlen=%d blen=%d\n", dlen, blen));
int loop = 0;
@ -9854,10 +9893,14 @@ unsigned ClntAuthBlock::ClientCrypt::callback(unsigned dlen, const void* data, u
unsigned retlen = currentIface->callback(dlen, data, blen, buffer);
HANDSHAKE_DEBUG(fprintf(stderr, "Iface %p returned %d\n", currentIface, retlen));
if (retlen)
{
triedPlugins.add(pluginItr);
return retlen;
}
}
// no success with iface - clear it
currentIface->dispose();
// appropriate data structures to be released by plugin cleanup code
currentIface = nullptr;
}
@ -9870,3 +9913,48 @@ unsigned ClntAuthBlock::ClientCrypt::callback(unsigned dlen, const void* data, u
// no luck with suggested data
return 0;
}
unsigned ClntAuthBlock::ClientCrypt::afterAttach(CheckStatusWrapper* st, const char* dbName, const IStatus* attStatus)
{
while (triedPlugins.hasData())
{
if (afterIface)
{
auto rc = afterIface->afterAttach(st, dbName, attStatus);
if (attStatus && (rc == NO_RETRY))
{
afterIface->dispose();
afterIface = nullptr;
triedPlugins.remove();
continue;
}
return rc;
}
else
{
FbLocalStatus st;
afterIface = triedPlugins.get()->chainHandle(&st);
check(&st, isc_interface_version_too_old);
fb_assert(afterIface);
if (!afterIface)
triedPlugins.remove();
}
}
return NO_RETRY;
}
void ClntAuthBlock::ClientCrypt::destroy()
{
if (currentIface)
{
currentIface->dispose();
currentIface = nullptr;
}
if (afterIface)
{
afterIface->dispose();
afterIface = nullptr;
}
}

View File

@ -712,6 +712,7 @@ public:
virtual void wakeup(unsigned int length, const void* data) = 0;
virtual Firebird::ICryptKeyCallback* getInterface() = 0;
virtual void stop() = 0;
virtual void destroy() = 0;
};
// CryptKey implementation
@ -859,21 +860,76 @@ private:
unsigned nextKey; // First key to be analyzed
class ClientCrypt final :
public Firebird::VersionedIface<Firebird::ICryptKeyCallbackImpl<ClientCrypt, Firebird::CheckStatusWrapper> >
public Firebird::VersionedIface<Firebird::ICryptKeyCallbackImpl<ClientCrypt, Firebird::CheckStatusWrapper> >,
public Firebird::GlobalStorage
{
public:
ClientCrypt()
: pluginItr(Firebird::IPluginManager::TYPE_KEY_HOLDER, "NoDefault"), currentIface(nullptr)
: pluginItr(Firebird::IPluginManager::TYPE_KEY_HOLDER, "NoDefault"),
currentIface(nullptr), afterIface(nullptr),
triedPlugins(getPool())
{ }
~ClientCrypt()
{
destroy();
}
Firebird::ICryptKeyCallback* create(const Firebird::Config* conf);
// Firebird::ICryptKeyCallback implementation
unsigned callback(unsigned dataLength, const void* data, unsigned bufferLength, void* buffer);
unsigned afterAttach(Firebird::CheckStatusWrapper* st, const char* dbName, const Firebird::IStatus* attStatus);
void destroy();
private:
Firebird::GetPlugins<Firebird::IKeyHolderPlugin> pluginItr;
typedef Firebird::GetPlugins<Firebird::IKeyHolderPlugin> KeyHolderItr;
KeyHolderItr pluginItr;
Firebird::ICryptKeyCallback* currentIface;
Firebird::ICryptKeyCallback* afterIface;
class TriedPlugins
{
typedef Firebird::Pair<Firebird::Left<Firebird::PathName, Firebird::IKeyHolderPlugin*> > TriedPlugin;
Firebird::ObjectsArray<TriedPlugin> data;
public:
TriedPlugins(MemoryPool& p)
: data(p)
{ }
void add(KeyHolderItr& itr)
{
for (auto& p : data)
{
if (p.first == itr.name())
return;
}
TriedPlugin tp(itr.name(), itr.plugin());
data.add(tp);
tp.second->addRef();
}
void remove()
{
fb_assert(data.hasData());
data[0].second->release();
data.remove(0);
}
bool hasData() const
{
return data.hasData();
}
Firebird::IKeyHolderPlugin* get()
{
return data[0].second;
}
};
TriedPlugins triedPlugins;
};
ClientCrypt clientCrypt;
Firebird::ICryptKeyCallback** createdInterface;

View File

@ -114,33 +114,6 @@ public:
: port(prt), replyLength(0), replyData(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 || port->port_type != rem_port::INET)
return 0;
Reference r(*port);
replyData = buffer;
replyLength = 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 replyLength;
}
void wakeup(unsigned int wakeLength, const void* wakeData)
{
if (replyLength > wakeLength)
@ -167,6 +140,34 @@ public:
return stopped;
}
// ICryptKeyCallback implementation
unsigned int callback(unsigned int dataLength, const void* data,
unsigned int bufferLength, void* buffer) override
{
if (stopped)
return 0;
if (port->port_protocol < PROTOCOL_VERSION13 || port->port_type != rem_port::INET)
return 0;
Reference r(*port);
replyData = buffer;
replyLength = 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 replyLength;
}
private:
rem_port* port;
Semaphore sem;
@ -187,29 +188,11 @@ public:
~CryptKeyCallback()
{
dispose();
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);
loadClientKey();
unsigned rc = keyCallback ?
keyCallback->callback(dataLength, data, bufferLength, buffer) :
// use legacy behavior if holders do wish to accept keys from client
networkCallback.callback(dataLength, data, bufferLength, buffer);
return rc;
}
void loadClientKey()
{
if (keyCallback)
@ -256,6 +239,43 @@ public:
networkCallback.stop();
}
// ICryptKeyCallback implementation
unsigned int callback(unsigned int dataLength, const void* data,
unsigned int bufferLength, void* buffer) override
{
if (keyCallback)
return keyCallback->callback(dataLength, data, bufferLength, buffer);
if (networkCallback.isStopped())
return 0;
Reference r(*port);
loadClientKey();
unsigned rc = keyCallback ?
keyCallback->callback(dataLength, data, bufferLength, buffer) :
// use legacy behavior if holders do wish to accept keys from client
networkCallback.callback(dataLength, data, bufferLength, buffer);
return rc;
}
unsigned afterAttach(Firebird::CheckStatusWrapper* st, const char* dbName,
const Firebird::IStatus* attStatus) override
{
return NO_RETRY;
}
void dispose() override
{
if (keyCallback)
{
LocalStatus ls;
CheckStatusWrapper st(&ls);
keyCallback->dispose();
keyCallback = nullptr;
}
}
private:
rem_port* port;
NetworkCallback networkCallback;
@ -273,22 +293,27 @@ public:
~ServerCallback()
{ }
void wakeup(unsigned int length, const void* data)
void wakeup(unsigned int length, const void* data) override
{
cryptCallback.wakeup(length, data);
}
ICryptKeyCallback* getInterface()
ICryptKeyCallback* getInterface() override
{
cryptCallback.loadClientKey();
return &cryptCallback;
}
void stop()
void stop() override
{
cryptCallback.stop();
}
void destroy() override
{
cryptCallback.dispose();
}
private:
CryptKeyCallback cryptCallback;
};
@ -2494,6 +2519,7 @@ void DatabaseAuth::accept(PACKET* send, Auth::WriterImplementation* authBlock)
CheckStatusWrapper status_vector(&ls);
fb_assert(authPort->port_server_crypt_callback);
authPort->port_server_crypt_callback->destroy();
provider->setDbCryptCallback(&status_vector, authPort->port_server_crypt_callback->getInterface());
if (!(status_vector.getState() & Firebird::IStatus::STATE_ERRORS))
@ -2512,12 +2538,13 @@ void DatabaseAuth::accept(PACKET* send, Auth::WriterImplementation* authBlock)
#endif
rdb->rdb_port = authPort;
rdb->rdb_iface = iface;
authPort->port_server_crypt_callback->stop();
}
}
CSTRING* const s = &send->p_resp.p_resp_data;
authPort->extractNewKeys(s);
authPort->port_server_crypt_callback->stop();
authPort->send_response(send, 0, s->cstr_length, &status_vector, false);
}
@ -6214,6 +6241,7 @@ ISC_STATUS rem_port::service_attach(const char* service_name,
CheckStatusWrapper status_vector(&ls);
fb_assert(port_server_crypt_callback);
port_server_crypt_callback->destroy();
provider->setDbCryptCallback(&status_vector, port_server_crypt_callback->getInterface());
if (!(status_vector.getState() & Firebird::IStatus::STATE_ERRORS))
@ -6233,9 +6261,10 @@ ISC_STATUS rem_port::service_attach(const char* service_name,
rdb->rdb_port = this;
Svc* svc = rdb->rdb_svc = FB_NEW Svc;
svc->svc_iface = iface;
}
}
port_server_crypt_callback->stop();
}
}
return this->send_response(sendL, 0, sendL->p_resp.p_resp_data.cstr_length, &status_vector,
false);