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

Implemented CORE-5442: Enhance control upon ability to share database crypt key between attachments in SS

This commit is contained in:
AlexPeshkoff 2017-01-09 20:28:11 +03:00
parent d5146be51e
commit e722a4095c
8 changed files with 278 additions and 122 deletions

View File

@ -7,7 +7,8 @@
<META NAME="AUTHOR" CONTENT="alex ">
<META NAME="CREATED" CONTENT="20130531;10003100">
<META NAME="CHANGEDBY" CONTENT="Alex Peshkoff">
<META NAME="CHANGED" CONTENT="20160905;13142600">
<META NAME="CHANGED" CONTENT="20170109;20251600">
<META NAME="CHANGEDBY" CONTENT="Alex Peshkoff">
<META NAME="CHANGEDBY" CONTENT="Alex Peshkoff">
<META NAME="CHANGEDBY" CONTENT="Alex Peshkoff">
<STYLE TYPE="text/css">
@ -587,10 +588,10 @@ namespace Firebird), timestamp with class FbTimestamp, containing
two public data members date and time of appropriate class, char -
with struct <A HREF="#FbChar">FbChar</A> and varchar with struct
<A HREF="#FbVarChar">FbVarChar</A>. For each field preprocessor
creates two data members in the message </FONT><FONT SIZE=4><I>name</I></FONT><FONT SIZE=4>
for field/parameter value and </FONT><FONT SIZE=4><I>nameNull</I></FONT><FONT SIZE=4>
for NULL indicator. Message constructor has 2 parameters pointer
to status wrapper and master interface:</FONT></P>
creates two data members in the message </FONT><FONT SIZE=4><I>name</I></FONT>
<FONT SIZE=4>for field/parameter value and </FONT><FONT SIZE=4><I>nameNull</I></FONT>
<FONT SIZE=4>for NULL indicator. Message constructor has 2 parameters
pointer to status wrapper and master interface:</FONT></P>
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>FB_MESSAGE(Output,
ThrowStatusWrapper,</I></FONT></P>
<P STYLE="margin-bottom: 0in"><FONT SIZE=4><I>(FB_SMALLINT,
@ -748,16 +749,15 @@ completed in FB3, we expect to have something more interesting in
next version. The minimum existing support is as follows: <A HREF="#Attachment">IAttachment</A>
contains call queEvents() which performs almost same functions as
isc_que_events() call. Instead the pair of parameters
</FONT><FONT SIZE=4><I>FPTR_EVENT_CALLBACK ast</I></FONT><FONT SIZE=4>
and </FONT><FONT SIZE=4><I>void* arg</I></FONT><FONT SIZE=4>,
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 <A HREF="#Events">IEvents</A> having method
cancel() used when waiting for event should be stopped. Unlike
identifier which is automatically destroyed when event arrives
</FONT><FONT SIZE=4><I>FPTR_EVENT_CALLBACK ast</I></FONT> <FONT SIZE=4>and
</FONT><FONT SIZE=4><I>void* arg</I></FONT><FONT SIZE=4>, 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 <A HREF="#Events">IEvents</A>
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
@ -890,7 +890,7 @@ services tasks do not forget to close an interface:</FONT></P>
<P STYLE="margin-bottom: 0in"><FONT SIZE=4>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 </FONT><FONT SIZE=4><I>plugin
module</I></FONT><FONT SIZE=4> or just </FONT><FONT SIZE=4><I>module</I></FONT><FONT SIZE=4>.
module</I></FONT> <FONT SIZE=4>or just </FONT><FONT SIZE=4><I>module</I></FONT><FONT SIZE=4>.
In most cases single plugin is place in dynamic library but in common
case. One of that interfaces <A HREF="#PluginModule">IPluginModule</A>
is module-wide (as more or less clear from it's name), others are
@ -920,9 +920,9 @@ 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 </FONT><FONT SIZE=4><I><SPAN STYLE="font-weight: normal">fbclient</SPAN></I></FONT><FONT SIZE=4>
is unloaded). Primary task of IPluginModule interface is that
notification. First of all one must decide - how to detect that
firebird library </FONT><FONT SIZE=4><I><SPAN STYLE="font-weight: normal">fbclient</SPAN></I></FONT>
<FONT SIZE=4>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
@ -2073,8 +2073,8 @@ moment when given timer should alarm.</FONT></P>
<LI><P STYLE="margin-bottom: 0in"><FONT SIZE=4>void
start(StatusType* status, ITimer* timer, ISC_UINT64 microSeconds)
start <A HREF="#Timer">ITimer</A> to alarm after given delay (in
microseconds, 10</FONT><SUP><FONT SIZE=4>-6</FONT></SUP><FONT SIZE=4>
seconds). Timer will be waked up only once after this call.</FONT></P>
microseconds, 10</FONT><SUP><FONT SIZE=4>-6</FONT></SUP> <FONT SIZE=4>seconds).
Timer will be waked up only once after this call.</FONT></P>
<LI><P STYLE="margin-bottom: 0in"><FONT SIZE=4>void stop(StatusType*
status, ITimer* timer) stop <A HREF="#Timer">ITimer</A>. It's
not an error to stop not started timer thus avoiding problems with
@ -2706,6 +2706,14 @@ interface is main interface of database crypt key holder plugin.</FONT></P>
(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.</FONT></P>
<LI><P STYLE="margin-bottom: 0in"><FONT SIZE=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
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.</FONT></P>
</OL>
<P STYLE="margin-bottom: 0in"><BR>
</P>

View File

@ -108,6 +108,17 @@ public:
return key;
}
FB_BOOLEAN useOnlyOwnKeys(CheckStatusWrapper* status)
{
IConfigEntry* e = getEntry(status, "OnlyOwnKey");
if (!e)
return FB_TRUE; // safe default
FB_BOOLEAN rc = e->getBoolValue();
e->release();
return rc;
}
private:
class CallbackInterface : public ICryptKeyCallbackImpl<CallbackInterface, CheckStatusWrapper>
{

View File

@ -1,6 +1,6 @@
/*
* PROGRAM: Firebird interface.
* MODULE: ImplementHelper.h
* MODULE: GetPlugins.h
* DESCRIPTION: Tools to help access plugins.
*
* The contents of this file are subject to the Initial
@ -82,6 +82,16 @@ public:
return currentPlugin;
}
P* makeInstance()
{
if (!hasData())
return NULL;
P* p = (P*) pluginSet->getPlugin(&status);
check(&status);
return p;
}
void next()
{
if (hasData())

View File

@ -732,6 +732,11 @@ interface KeyHolderPlugin : PluginBase
// Missing key with given name is not an error condition for keyHandle().
// It should just return NULL in this case
CryptKeyCallback keyHandle(Status status, const string keyName);
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);
}

View File

@ -2905,6 +2905,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();
};
protected:
@ -2918,7 +2919,7 @@ namespace Firebird
}
public:
static const unsigned VERSION = 4;
static const unsigned VERSION = 5;
template <typename StatusType> int keyCallback(StatusType* status, ICryptKeyCallback* callback)
{
@ -2935,6 +2936,20 @@ namespace Firebird
StatusType::checkException(status);
return ret;
}
template <typename StatusType> FB_BOOLEAN useOnlyOwnKeys(StatusType* status)
{
if (cloopVTable->version < 5)
{
StatusType::setVersionError(status, "IKeyHolderPlugin", cloopVTable->version, 5);
StatusType::checkException(status);
return 0;
}
StatusType::clearException(status);
FB_BOOLEAN ret = static_cast<VTable*>(this->cloopVTable)->useOnlyOwnKeys(this, status);
StatusType::checkException(status);
return ret;
}
};
class IDbCryptInfo : public IReferenceCounted
@ -11230,6 +11245,7 @@ namespace Firebird
this->getOwner = &Name::cloopgetOwnerDispatcher;
this->keyCallback = &Name::cloopkeyCallbackDispatcher;
this->keyHandle = &Name::cloopkeyHandleDispatcher;
this->useOnlyOwnKeys = &Name::cloopuseOnlyOwnKeysDispatcher;
}
} vTable;
@ -11266,6 +11282,21 @@ namespace Firebird
}
}
static FB_BOOLEAN CLOOP_CARG cloopuseOnlyOwnKeysDispatcher(IKeyHolderPlugin* self, IStatus* status) throw()
{
StatusType status2(status);
try
{
return static_cast<Name*>(self)->Name::useOnlyOwnKeys(&status2);
}
catch (...)
{
StatusType::catchException(&status2);
return static_cast<FB_BOOLEAN>(0);
}
}
static void CLOOP_CARG cloopsetOwnerDispatcher(IPluginBase* self, IReferenceCounted* r) throw()
{
try
@ -11332,6 +11363,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;
};
template <typename Name, typename StatusType, typename Base>

View File

@ -265,10 +265,12 @@ namespace Jrd {
: PermanentStorage(*tdbb->getDatabase()->dbb_permanent),
sync(this),
keyName(getPool()),
keyHolderPlugins(getPool()),
keyHolderPlugins(getPool(), this),
hash(getPool()),
dbInfo(FB_NEW DbInfo(this)),
cryptThreadId(0),
cryptPlugin(NULL),
checkPlugin(NULL),
dbb(*tdbb->getDatabase()),
cryptAtt(NULL),
slowIO(0),
@ -364,14 +366,15 @@ namespace Jrd {
loadPlugin(hdr->hdr_crypt_plugin);
string valid;
calcValidation(valid);
calcValidation(valid, cryptPlugin);
if (hc.find(Ods::HDR_crypt_hash))
{
string hash;
hc.getString(hash);
if (hash != valid)
(Arg::Gds(isc_bad_crypt_key) << keyName).raise();
}
else
hash = valid;
}
if (flags & CRYPT_HDR_INIT)
@ -409,9 +412,21 @@ namespace Jrd {
status_exception::raise(&status);
}
keyHolderPlugins.init(p, keyName.c_str());
keyHolderPlugins.init(p, keyName);
cryptPlugin = p;
cryptPlugin->addRef();
// May be load second instance to validate keys
if (checkPlugin)
{
PluginManagerInterfacePtr()->releasePlugin(checkPlugin);
checkPlugin = NULL;
}
if (dbb.dbb_config->getServerMode() == MODE_SUPER)
{
checkPlugin = cryptControl.makeInstance();
keyHolderPlugins.validate(checkPlugin, NULL, keyName);
}
}
void CryptoManager::prepareChangeCryptState(thread_db* tdbb, const MetaName& plugName,
@ -472,13 +487,13 @@ namespace Jrd {
}
}
void CryptoManager::calcValidation(string& valid)
void CryptoManager::calcValidation(string& valid, IDbCryptPlugin* plugin)
{
// crypt verifier
const char* sample = "0123456789ABCDEF";
char result[16];
FbLocalStatus sv;
cryptPlugin->encrypt(&sv, sizeof(result), sample, result);
plugin->encrypt(&sv, sizeof(result), sample, result);
if (sv->getState() & IStatus::STATE_ERRORS)
Arg::StatusVector(&sv).raise();
@ -487,6 +502,13 @@ namespace Jrd {
Sha1::hashBased64(valid, verifier);
}
bool CryptoManager::checkValidation(IDbCryptPlugin* plugin)
{
string valid;
calcValidation(valid, plugin);
return valid == hash;
}
void CryptoManager::changeCryptState(thread_db* tdbb, const string& plugName)
{
if (plugName.length() > 31)
@ -550,8 +572,7 @@ namespace Jrd {
{
header->hdr_flags |= Ods::hdr_encrypted;
plugName.copyTo(header->hdr_crypt_plugin, sizeof(header->hdr_crypt_plugin));
string hash;
calcValidation(hash);
calcValidation(hash, cryptPlugin);
hc.deleteWithTag(Ods::HDR_crypt_hash);
hc.insertString(Ods::HDR_crypt_hash, hash);
@ -619,7 +640,15 @@ namespace Jrd {
{
keyHolderPlugins.attach(att, dbb.dbb_config);
IDbCryptPlugin* p = checkPlugin;
lockAndReadHeader(tdbb, CRYPT_HDR_INIT);
if (p && p == checkPlugin)
{
if (!keyHolderPlugins.validate(checkPlugin, att, keyName))
(Arg::Gds(isc_bad_crypt_key) << keyName).raise();
}
}
void CryptoManager::terminateCryptThread(thread_db*, bool wait)
@ -1132,49 +1161,6 @@ namespace Jrd {
return (crypt ? fb_info_crypt_encrypted : 0) | (process ? fb_info_crypt_process : 0);
}
CryptoManager::HolderAttachments::HolderAttachments(MemoryPool& p)
: keyHolder(NULL), attachments(p)
{ }
void CryptoManager::HolderAttachments::setPlugin(IKeyHolderPlugin* kh)
{
keyHolder = kh;
keyHolder->addRef();
}
CryptoManager::HolderAttachments::~HolderAttachments()
{
if (keyHolder)
{
PluginManagerInterfacePtr()->releasePlugin(keyHolder);
}
}
void CryptoManager::HolderAttachments::registerAttachment(Attachment* att)
{
attachments.add(att);
}
bool CryptoManager::HolderAttachments::unregisterAttachment(Attachment* att)
{
unsigned i = attachments.getCount();
while (i--)
{
if (attachments[i] == att)
{
attachments.remove(i);
break;
}
}
return attachments.getCount() == 0;
}
bool CryptoManager::HolderAttachments::operator==(IKeyHolderPlugin* kh) const
{
// ASF: I think there should be a better way to do this.
return keyHolder->cloopVTable == kh->cloopVTable;
}
void CryptoManager::KeyHolderPlugins::attach(Attachment* att, Config* config)
{
MutexLockGuard g(holdersMutex, FB_FUNCTION);
@ -1184,31 +1170,42 @@ namespace Jrd {
{
IKeyHolderPlugin* keyPlugin = keyControl.plugin();
FbLocalStatus st;
if (keyPlugin->keyCallback(&st, att->att_crypt_callback) > 0)
bool flProvide = false;
if (keyPlugin->keyCallback(&st, att->att_crypt_callback))
flProvide = true;
else
{
// holder accepted attachment's key
HolderAttachments* ha = NULL;
st.check();
flProvide = !keyPlugin->useOnlyOwnKeys(&st);
// Ignore error condition to support old plugins
}
if (flProvide)
{
// holder is going to provide a key
PerAttHolders* pa = NULL;
for (unsigned i = 0; i < knownHolders.getCount(); ++i)
{
if (knownHolders[i] == keyPlugin)
if (knownHolders[i].first == att)
{
ha = &knownHolders[i];
pa = &knownHolders[i];
break;
}
}
if (!ha)
if ((!pa) && config->getServerMode() == MODE_SUPER)
{
ha = &(knownHolders.add());
ha->setPlugin(keyPlugin);
pa = &(knownHolders.add());
pa->first = att;
}
ha->registerAttachment(att);
break; // Do not need >1 key from attachment to single DB
if (pa)
{
pa->second.add(keyPlugin);
keyPlugin->addRef();
}
}
else
st.check();
}
}
@ -1216,33 +1213,130 @@ namespace Jrd {
{
MutexLockGuard g(holdersMutex, FB_FUNCTION);
unsigned i = knownHolders.getCount();
while (i--)
for (unsigned i = 0; i < knownHolders.getCount(); ++i)
{
if (knownHolders[i].unregisterAttachment(att))
if (knownHolders[i].first == att)
{
releaseHolders(knownHolders[i]);
knownHolders.remove(i);
break;
}
}
}
void CryptoManager::KeyHolderPlugins::init(IDbCryptPlugin* crypt, const char* keyName)
void CryptoManager::KeyHolderPlugins::releaseHolders(PerAttHolders& pa)
{
unsigned j = 0;
for (; j < pa.second.getCount(); ++j)
{
PluginManagerInterfacePtr()->releasePlugin(pa.second[j]);
}
pa.second.removeCount(0, j);
}
void CryptoManager::KeyHolderPlugins::init(IDbCryptPlugin* crypt, const MetaName& keyName)
{
MutexLockGuard g(holdersMutex, FB_FUNCTION);
Firebird::HalfStaticArray<Firebird::IKeyHolderPlugin*, 64> holdersVector;
unsigned int length = knownHolders.getCount();
IKeyHolderPlugin** vector = holdersVector.getBuffer(length);
for (unsigned i = 0; i < length; ++i)
for (unsigned i = 0; i < knownHolders.getCount(); ++i)
{
vector[i] = knownHolders[i].getPlugin();
PerAttHolders pa = knownHolders[i];
for (unsigned j = 0; j < pa.second.getCount(); ++j)
holdersVector.push(pa.second[j]);
}
FbLocalStatus st;
crypt->setKey(&st, length, vector, keyName);
crypt->setKey(&st, holdersVector.getCount(), holdersVector.begin(), keyName.c_str());
st.check();
}
bool CryptoManager::KeyHolderPlugins::validateHoldersGroup(PerAttHolders& pa, IDbCryptPlugin* crypt, const MetaName& keyName)
{
FbLocalStatus st;
fb_assert(holdersMutex.locked());
for (unsigned j = 0; j < pa.second.getCount(); ++j)
{
IKeyHolderPlugin* keyHolder = pa.second[j];
if (!keyHolder->useOnlyOwnKeys(&st))
return true;
crypt->setKey(&st, 1, &keyHolder, keyName.c_str());
if (st.isSuccess() && mgr->checkValidation(crypt))
return true;
}
return true;
}
bool CryptoManager::KeyHolderPlugins::validate(IDbCryptPlugin* crypt, Attachment* att, const MetaName& keyName)
{
FbLocalStatus st;
MutexLockGuard g(holdersMutex, FB_FUNCTION);
// Check for current attachment
for (unsigned i = 0; i < knownHolders.getCount(); ++i)
{
PerAttHolders& pa = knownHolders[i];
if (pa.first == att)
{
bool empty = (pa.second.getCount() == 0);
bool result = empty ? false : validateHoldersGroup(pa, crypt, keyName);
releaseHolders(pa);
knownHolders.remove(i);
if (empty)
break;
return result;
}
}
// Special case - holders not needed at all
crypt->setKey(&st, 0, NULL, keyName.c_str());
if (st.isSuccess() && mgr->checkValidation(crypt))
return true;
return false;
}
void CryptoManager::KeyHolderPlugins::validate(IDbCryptPlugin* crypt, 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))
return;
// Loop through whole attathment list of DBB, shutdown attachments missing any holders
for (Attachment* att = mgr->dbb.dbb_attachments; att; att = att->att_next)
{
for (unsigned i = 0; i < knownHolders.getCount(); ++i)
{
if (knownHolders[i].first == att)
goto found;
}
att->signalCancel();
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]);
}
knownHolders.clear();
}
void CryptoManager::addClumplet(string& signature, ClumpletReader& block, UCHAR tag)
{
if (block.find(tag))

View File

@ -294,6 +294,8 @@ public:
void cryptThread();
bool checkValidation(Firebird::IDbCryptPlugin* crypt);
ULONG getCurrentPage() const;
UCHAR getCurrentState() const;
@ -319,42 +321,28 @@ private:
char buf[MAX_PAGE_SIZE + PAGE_ALIGNMENT - 1];
};
class HolderAttachments
{
public:
explicit HolderAttachments(Firebird::MemoryPool& p);
~HolderAttachments();
void registerAttachment(Attachment* att);
bool unregisterAttachment(Attachment* att);
void setPlugin(Firebird::IKeyHolderPlugin* kh);
Firebird::IKeyHolderPlugin* getPlugin() const
{
return keyHolder;
}
bool operator==(Firebird::IKeyHolderPlugin* kh) const;
private:
Firebird::IKeyHolderPlugin* keyHolder;
Firebird::HalfStaticArray<Attachment*, 32> attachments;
};
class KeyHolderPlugins
{
public:
explicit KeyHolderPlugins(Firebird::MemoryPool& p)
: knownHolders(p)
explicit KeyHolderPlugins(Firebird::MemoryPool& p, CryptoManager* m)
: knownHolders(p), mgr(m)
{ }
void attach(Attachment* att, 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);
void detach(Attachment* att);
void init(Firebird::IDbCryptPlugin* crypt, const char* keyName);
private:
Firebird::Mutex holdersMutex;
Firebird::ObjectsArray<HolderAttachments> knownHolders;
typedef Firebird::Pair<Firebird::Right<Attachment*,
Firebird::HalfStaticArray<Firebird::IKeyHolderPlugin*, 4>>> PerAttHolders;
Firebird::ObjectsArray<PerAttHolders> knownHolders;
CryptoManager* mgr;
bool validateHoldersGroup(PerAttHolders& pa, Firebird::IDbCryptPlugin* crypt, const Firebird::MetaName& keyName);
void releaseHolders(PerAttHolders& pa);
};
class DbInfo;
@ -398,7 +386,8 @@ private:
void loadPlugin(const char* pluginName);
ULONG getLastPage(thread_db* tdbb);
void writeDbHeader(thread_db* tdbb, ULONG runpage);
void calcValidation(Firebird::string& valid);
void calcValidation(Firebird::string& valid, Firebird::IDbCryptPlugin* plugin);
void checkValidation();
void lockAndReadHeader(thread_db* tdbb, unsigned flags = 0);
static const unsigned CRYPT_HDR_INIT = 0x01;
@ -414,9 +403,11 @@ private:
ULONG currentPage;
Firebird::Mutex pluginLoadMtx, cryptThreadMtx;
KeyHolderPlugins keyHolderPlugins;
Firebird::string hash;
Firebird::RefPtr<DbInfo> dbInfo;
Thread::Handle cryptThreadId;
Firebird::IDbCryptPlugin* cryptPlugin;
Firebird::IDbCryptPlugin* checkPlugin;
Database& dbb;
Lock* stateLock;
Lock* threadLock;

View File

@ -101,6 +101,11 @@ namespace Jrd
return localStatusVector.isEmpty();
}
bool isSuccess() const
{
return localStatusVector.isEmpty();
}
private:
Firebird::LocalStatus localStatus;
SW localStatusVector;