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

Implemented CORE-5100: Improve control over database crypt keys

This commit is contained in:
alexpeshkoff 2016-02-03 13:20:41 +00:00
parent 91f7b5ec56
commit bebf1d5a70
25 changed files with 272 additions and 80 deletions

View File

@ -85,7 +85,7 @@ class CryptKeyHolder : public IKeyHolderPluginImpl<CryptKeyHolder, CheckStatusWr
{
public:
explicit CryptKeyHolder(IPluginConfig* cnf) throw()
: callbackInterface(this), config(cnf), key(0), owner(NULL)
: callbackInterface(this), named(NULL), config(cnf), key(0), owner(NULL)
{
config->addRef();
}
@ -139,12 +139,12 @@ private:
{
public:
explicit CallbackInterface(CryptKeyHolder* p)
: parent(p)
: holder(p)
{ }
unsigned int callback(unsigned int, const void*, unsigned int length, void* buffer)
{
UCHAR k = parent->getKey();
UCHAR k = holder->getKey();
if (!k)
{
return 0;
@ -157,16 +157,38 @@ private:
return 1;
}
IPluginModule* getModule()
private:
CryptKeyHolder* holder;
};
class NamedCallback : public ICryptKeyCallbackImpl<NamedCallback, CheckStatusWrapper>
{
public:
NamedCallback(NamedCallback *n, const char* nm, UCHAR k)
: next(n), key(k)
{
return &module;
strncpy(name, nm, sizeof(name));
name[sizeof(name) - 1] = 0;
}
private:
CryptKeyHolder* parent;
unsigned int callback(unsigned int, const void*, unsigned int length, void* buffer)
{
memcpy(buffer, &key, 1);
return 1;
}
~NamedCallback()
{
delete next;
}
char name[32];
NamedCallback* next;
UCHAR key;
};
CallbackInterface callbackInterface;
NamedCallback *named;
IPluginConfig* config;
UCHAR key;
@ -174,18 +196,21 @@ private:
AtomicCounter refCounter;
IReferenceCounted* owner;
void noKeyError(CheckStatusWrapper* status);
IConfigEntry* getEntry(CheckStatusWrapper* status, const char* entryName);
};
void CryptKeyHolder::noKeyError(CheckStatusWrapper* status)
IConfigEntry* CryptKeyHolder::getEntry(CheckStatusWrapper* status, const char* entryName)
{
ISC_STATUS_ARRAY vector;
vector[0] = isc_arg_gds;
vector[1] = isc_random;
vector[2] = isc_arg_string;
vector[3] = (ISC_STATUS) "Key not set";
vector[4] = isc_arg_end;
status->setErrors(vector);
IConfig* def = config->getDefaultConfig(status);
if (status->getState() & Firebird::IStatus::STATE_ERRORS)
return NULL;
IConfigEntry* confEntry = def->find(status, entryName);
def->release();
if (status->getState() & Firebird::IStatus::STATE_ERRORS)
return NULL;
return confEntry;
}
int CryptKeyHolder::keyCallback(CheckStatusWrapper* status, ICryptKeyCallback* callback)
@ -195,14 +220,7 @@ int CryptKeyHolder::keyCallback(CheckStatusWrapper* status, ICryptKeyCallback* c
if (key != 0)
return 1;
IConfig* def = config->getDefaultConfig(status);
if (status->getState() & Firebird::IStatus::STATE_ERRORS)
return 1;
IConfigEntry* confEntry = def->find(status, "Auto");
def->release();
if (status->getState() & Firebird::IStatus::STATE_ERRORS)
return 1;
IConfigEntry* confEntry = getEntry(status, "Auto");
if (confEntry)
{
@ -226,12 +244,33 @@ int CryptKeyHolder::keyCallback(CheckStatusWrapper* status, ICryptKeyCallback* c
ICryptKeyCallback* CryptKeyHolder::keyHandle(CheckStatusWrapper* status, const char* keyName)
{
if (strcmp(keyName, "sample") != 0)
if (keyName[0] == 0)
return &callbackInterface;
for (NamedCallback* n = named; n; n = n->next)
{
return NULL;
if (strcmp(keyName, n->name) == 0)
return n;
}
return &callbackInterface;
char kn[40];
strcpy(kn, "Key");
strncat(kn, keyName, sizeof(kn));
kn[sizeof(kn) - 1] = 0;
IConfigEntry* confEntry = getEntry(status, kn);
if (confEntry)
{
UCHAR k = confEntry->getIntValue();
confEntry->release();
if (k > 0 && k < 256)
{
named = new NamedCallback(named, keyName, static_cast<UCHAR>(k));
return named;
}
}
return NULL;
}
class Factory : public IPluginFactoryImpl<Factory, CheckStatusWrapper>

View File

@ -95,7 +95,8 @@ public:
// ICryptPlugin implementation
void encrypt(CheckStatusWrapper* status, unsigned int length, const void* from, void* to);
void decrypt(CheckStatusWrapper* status, unsigned int length, const void* from, void* to);
void setKey(CheckStatusWrapper* status, unsigned int length, IKeyHolderPlugin** sources);
void setKey(CheckStatusWrapper* status, const char* keyName,
unsigned int length, IKeyHolderPlugin** sources);
int release()
{
@ -129,6 +130,7 @@ public:
private:
IPluginConfig* config;
char savedKeyName[32];
UCHAR key;
AtomicCounter refCounter;
@ -139,11 +141,20 @@ private:
void DbCrypt::noKeyError(CheckStatusWrapper* status)
{
char msg[100];
strcpy(msg, "Crypt key ");
if (savedKeyName[0])
{
strcat(msg, savedKeyName);
strcat(msg, " ");
}
strcat(msg, "not set");
ISC_STATUS_ARRAY vector;
vector[0] = isc_arg_gds;
vector[1] = isc_random;
vector[2] = isc_arg_string;
vector[3] = (ISC_STATUS)"Key not set";
vector[3] = (ISC_STATUS)msg;
vector[4] = isc_arg_end;
status->setErrors(vector);
}
@ -186,13 +197,17 @@ void DbCrypt::decrypt(CheckStatusWrapper* status, unsigned int length, const voi
}
}
void DbCrypt::setKey(CheckStatusWrapper* status, unsigned int length, IKeyHolderPlugin** sources)
void DbCrypt::setKey(CheckStatusWrapper* status, const char* keyName,
unsigned int length, IKeyHolderPlugin** sources)
{
status->init();
if (key != 0)
return;
strncpy(savedKeyName, keyName ? keyName : "", sizeof(savedKeyName));
savedKeyName[sizeof(savedKeyName) - 1] = 0;
IConfig* def = config->getDefaultConfig(status);
if (status->getState() & Firebird::IStatus::STATE_ERRORS)
return;
@ -230,7 +245,7 @@ void DbCrypt::setKey(CheckStatusWrapper* status, unsigned int length, IKeyHolder
for (unsigned n = 0; n < length; ++n)
{
ICryptKeyCallback* callback = sources[n]->keyHandle(status, "sample");
ICryptKeyCallback* callback = sources[n]->keyHandle(status, savedKeyName);
if (status->getState() & Firebird::IStatus::STATE_ERRORS)
return;

View File

@ -1626,6 +1626,10 @@ C --
PARAMETER (GDS__login_error = 335545106)
INTEGER*4 GDS__already_opened
PARAMETER (GDS__already_opened = 335545107)
INTEGER*4 GDS__bad_crypt_key
PARAMETER (GDS__bad_crypt_key = 335545108)
INTEGER*4 GDS__encrypt_error
PARAMETER (GDS__encrypt_error = 335545109)
INTEGER*4 GDS__gfix_db_name
PARAMETER (GDS__gfix_db_name = 335740929)
INTEGER*4 GDS__gfix_invalid_sw

View File

@ -1621,6 +1621,10 @@ const
gds_login_error = 335545106;
isc_already_opened = 335545107;
gds_already_opened = 335545107;
isc_bad_crypt_key = 335545108;
gds_bad_crypt_key = 335545108;
isc_encrypt_error = 335545109;
gds_encrypt_error = 335545109;
isc_gfix_db_name = 335740929;
gds_gfix_db_name = 335740929;
isc_gfix_invalid_sw = 335740930;

View File

@ -29,6 +29,7 @@
#include "firebird.h"
#include "../common/classes/ClumpletReader.h"
#include "../common/classes/MetaName.h"
#include "fb_exception.h"
#include "../jrd/ibase.h"
@ -784,6 +785,14 @@ string& ClumpletReader::getString(string& str) const
return str;
}
MetaName& ClumpletReader::getString(MetaName& str) const
{
const UCHAR* ptr = getBytes();
const FB_SIZE_T length = getClumpLength();
str.assign(reinterpret_cast<const char*>(ptr), length);
return str;
}
PathName& ClumpletReader::getPath(PathName& str) const
{
const UCHAR* ptr = getBytes();

View File

@ -40,6 +40,8 @@
namespace Firebird {
class MetaName;
// This class provides read access for clumplet structure
// Note: it doesn't make a copy of buffer it reads
class ClumpletReader : protected AutoStorage
@ -100,6 +102,7 @@ public:
bool getBoolean() const;
SINT64 getBigInt() const;
string& getString(string& str) const;
MetaName& getString(MetaName& str) const;
PathName& getPath(PathName& str) const;
void getData(UCharBuffer& data) const;
const UCHAR* getBytes() const;

View File

@ -30,6 +30,7 @@
#include "firebird.h"
#include "../common/classes/ClumpletWriter.h"
#include "../common/classes/MetaName.h"
#include "fb_exception.h"
#include "../jrd/ibase.h"
@ -183,7 +184,7 @@ void ClumpletWriter::reset(const UCHAR* buffer, const FB_SIZE_T buffLen)
}
else
{
UCHAR tag = (kind == SpbStart || kind == UnTagged || kind == WideUnTagged) ? getBufferTag() : 0;
UCHAR tag = (kind == SpbStart || kind == UnTagged || kind == WideUnTagged) ? 0 : getBufferTag();
initNewBuffer(tag);
}
rewind();
@ -250,16 +251,26 @@ void ClumpletWriter::insertString(UCHAR tag, const string& str)
insertString(tag, str.c_str(), str.length());
}
void ClumpletWriter::insertPath(UCHAR tag, const PathName& str)
void ClumpletWriter::insertString(UCHAR tag, const MetaName& str)
{
insertString(tag, str.c_str(), str.length());
}
void ClumpletWriter::insertString(UCHAR tag, const char* str)
{
insertString(tag, str, strlen(str));
}
void ClumpletWriter::insertString(UCHAR tag, const char* str, FB_SIZE_T length)
{
insertBytesLengthCheck(tag, str, length);
}
void ClumpletWriter::insertPath(UCHAR tag, const PathName& str)
{
insertString(tag, str.c_str(), str.length());
}
void ClumpletWriter::insertBytes(UCHAR tag, const void* bytes, FB_SIZE_T length)
{
insertBytesLengthCheck(tag, bytes, length);

View File

@ -73,7 +73,9 @@ public:
void insertBigInt(UCHAR tag, const SINT64 value);
void insertBytes(UCHAR tag, const void* bytes, FB_SIZE_T length);
void insertString(UCHAR tag, const string& str);
void insertString(UCHAR tag, const MetaName& str);
void insertPath(UCHAR tag, const PathName& str);
void insertString(UCHAR tag, const char* str);
void insertString(UCHAR tag, const char* str, FB_SIZE_T length);
void insertByte(UCHAR tag, const UCHAR byte);
void insertTag(UCHAR tag);

View File

@ -11577,6 +11577,7 @@ string AlterDatabaseNode::internalPrint(NodePrinter& printer) const
NODE_PRINT(printer, setDefaultCollation);
NODE_PRINT(printer, files);
NODE_PRINT(printer, cryptPlugin);
NODE_PRINT(printer, keyName);
return "AlterDatabaseNode";
}
@ -11660,7 +11661,7 @@ void AlterDatabaseNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc
if (clauses & CLAUSE_CRYPT)
{
Database* const db = tdbb->getDatabase();
db->dbb_crypto_manager->prepareChangeCryptState(tdbb, cryptPlugin);
db->dbb_crypto_manager->prepareChangeCryptState(tdbb, cryptPlugin, keyName);
DFW_post_work(transaction, dfw_db_crypt, cryptPlugin.c_str(), 0);
}

View File

@ -2228,7 +2228,8 @@ public:
setDefaultCharSet(p),
setDefaultCollation(p),
files(p),
cryptPlugin(p)
cryptPlugin(p),
keyName(p)
{
}
@ -2263,6 +2264,7 @@ public:
Firebird::MetaName setDefaultCollation;
Firebird::Array<NestConst<DbFileClause> > files;
Firebird::MetaName cryptPlugin;
Firebird::MetaName keyName;
};

View File

@ -3961,7 +3961,7 @@ db_alter_clause($alterDatabaseNode)
{ $alterDatabaseNode->clauses |= AlterDatabaseNode::CLAUSE_END_BACKUP; }
| SET DEFAULT CHARACTER SET symbol_character_set_name
{ $alterDatabaseNode->setDefaultCharSet = *$5; }
| ENCRYPT WITH valid_symbol_name
| ENCRYPT WITH valid_symbol_name crypt_key_clause($alterDatabaseNode)
{
setClauseFlag($alterDatabaseNode->clauses, AlterDatabaseNode::CLAUSE_CRYPT, "CRYPT");
$alterDatabaseNode->cryptPlugin = *$3;
@ -3974,6 +3974,12 @@ db_alter_clause($alterDatabaseNode)
{ $alterDatabaseNode->linger = 0; }
;
%type crypt_key_clause(<alterDatabaseNode>)
crypt_key_clause($alterDatabaseNode)
: /* nothing */
| KEY valid_symbol_name
{ $alterDatabaseNode->keyName = *$2; }
;
// ALTER TRIGGER

View File

@ -720,11 +720,11 @@ interface KeyHolderPlugin : PluginBase
interface DbCryptPlugin : PluginBase
{
// When database crypt plugin is loaded, setKey() is called to provide information
// about key holders, available for a given database.
// about key holders, available for a given database and key name for database.
// It's supposed that crypt plugin will invoke keyHandle() function from them
// to access callback interface for getting actual crypt key.
// If crypt plugin fails to find appropriate key in sources, it should raise error.
void setKey(Status status, uint length, KeyHolderPlugin* sources);
void setKey(Status status, const string keyName, uint length, KeyHolderPlugin* sources);
void encrypt(Status status, uint length, const void* from, void* to);
void decrypt(Status status, uint length, const void* from, void* to);
}

View File

@ -2871,7 +2871,7 @@ namespace Firebird
public:
struct VTable : public IPluginBase::VTable
{
void (CLOOP_CARG *setKey)(IDbCryptPlugin* self, IStatus* status, unsigned length, IKeyHolderPlugin** sources) throw();
void (CLOOP_CARG *setKey)(IDbCryptPlugin* self, IStatus* status, const char* keyName, unsigned length, IKeyHolderPlugin** sources) throw();
void (CLOOP_CARG *encrypt)(IDbCryptPlugin* self, IStatus* status, unsigned length, const void* from, void* to) throw();
void (CLOOP_CARG *decrypt)(IDbCryptPlugin* self, IStatus* status, unsigned length, const void* from, void* to) throw();
};
@ -2889,10 +2889,10 @@ namespace Firebird
public:
static const unsigned VERSION = 4;
template <typename StatusType> void setKey(StatusType* status, unsigned length, IKeyHolderPlugin** sources)
template <typename StatusType> void setKey(StatusType* status, const char* keyName, unsigned length, IKeyHolderPlugin** sources)
{
StatusType::clearException(status);
static_cast<VTable*>(this->cloopVTable)->setKey(this, status, length, sources);
static_cast<VTable*>(this->cloopVTable)->setKey(this, status, keyName, length, sources);
StatusType::checkException(status);
}
@ -11093,13 +11093,13 @@ namespace Firebird
this->cloopVTable = &vTable;
}
static void CLOOP_CARG cloopsetKeyDispatcher(IDbCryptPlugin* self, IStatus* status, unsigned length, IKeyHolderPlugin** sources) throw()
static void CLOOP_CARG cloopsetKeyDispatcher(IDbCryptPlugin* self, IStatus* status, const char* keyName, unsigned length, IKeyHolderPlugin** sources) throw()
{
StatusType status2(status);
try
{
static_cast<Name*>(self)->Name::setKey(&status2, length, sources);
static_cast<Name*>(self)->Name::setKey(&status2, keyName, length, sources);
}
catch (...)
{
@ -11199,7 +11199,7 @@ namespace Firebird
{
}
virtual void setKey(StatusType* status, unsigned length, IKeyHolderPlugin** sources) = 0;
virtual void setKey(StatusType* status, const char* keyName, unsigned length, IKeyHolderPlugin** sources) = 0;
virtual void encrypt(StatusType* status, unsigned length, const void* from, void* to) = 0;
virtual void decrypt(StatusType* status, unsigned length, const void* from, void* to) = 0;
};

View File

@ -809,6 +809,8 @@ static const struct {
{"map_down", 335545105},
{"login_error", 335545106},
{"already_opened", 335545107},
{"bad_crypt_key", 335545108},
{"encrypt_error", 335545109},
{"gfix_db_name", 335740929},
{"gfix_invalid_sw", 335740930},
{"gfix_incmp_sw", 335740932},

View File

@ -843,6 +843,8 @@ const ISC_STATUS isc_invalid_attachment_charset = 335545104L;
const ISC_STATUS isc_map_down = 335545105L;
const ISC_STATUS isc_login_error = 335545106L;
const ISC_STATUS isc_already_opened = 335545107L;
const ISC_STATUS isc_bad_crypt_key = 335545108L;
const ISC_STATUS isc_encrypt_error = 335545109L;
const ISC_STATUS isc_gfix_db_name = 335740929L;
const ISC_STATUS isc_gfix_invalid_sw = 335740930L;
const ISC_STATUS isc_gfix_incmp_sw = 335740932L;
@ -1306,7 +1308,7 @@ const ISC_STATUS isc_trace_switch_user_only = 337182757L;
const ISC_STATUS isc_trace_switch_param_miss = 337182758L;
const ISC_STATUS isc_trace_param_act_notcompat = 337182759L;
const ISC_STATUS isc_trace_mandatory_switch_miss = 337182760L;
const ISC_STATUS isc_err_max = 1250;
const ISC_STATUS isc_err_max = 1252;
#else /* c definitions */
@ -2119,6 +2121,8 @@ const ISC_STATUS isc_err_max = 1250;
#define isc_map_down 335545105L
#define isc_login_error 335545106L
#define isc_already_opened 335545107L
#define isc_bad_crypt_key 335545108L
#define isc_encrypt_error 335545109L
#define isc_gfix_db_name 335740929L
#define isc_gfix_invalid_sw 335740930L
#define isc_gfix_incmp_sw 335740932L
@ -2582,7 +2586,7 @@ const ISC_STATUS isc_err_max = 1250;
#define isc_trace_switch_param_miss 337182758L
#define isc_trace_param_act_notcompat 337182759L
#define isc_trace_mandatory_switch_miss 337182760L
#define isc_err_max 1250
#define isc_err_max 1252
#endif

View File

@ -812,6 +812,8 @@ Data source : @4"}, /* eds_statement */
{335545105, "Some database(s) were shutdown when trying to read mapping data"}, /* map_down */
{335545106, "Error occurred during login, please check server firebird.log for details"}, /* login_error */
{335545107, "Database already opened with engine instance, incompatible with current"}, /* already_opened */
{335545108, "Invalid crypt key @1"}, /* bad_crypt_key */
{335545109, "Page requires encyption but crypt plugin is missing"}, /* encrypt_error */
{335740929, "data base file name (@1) already given"}, /* gfix_db_name */
{335740930, "invalid switch @1"}, /* gfix_invalid_sw */
{335740932, "incompatible switch combination"}, /* gfix_incmp_sw */

View File

@ -808,6 +808,8 @@ static const struct {
{335545105, -901}, /* 785 map_down */
{335545106, -902}, /* 786 login_error */
{335545107, -902}, /* 787 already_opened */
{335545108, -902}, /* 788 bad_crypt_key */
{335545109, -901}, /* 789 encrypt_error */
{335740929, -901}, /* 1 gfix_db_name */
{335740930, -901}, /* 2 gfix_invalid_sw */
{335740932, -901}, /* 4 gfix_incmp_sw */

View File

@ -743,7 +743,7 @@ static const struct {
{335545040, "22001"}, // 720 cp_name_too_long
{335545041, "42818"}, // 721 cp_process_active
{335545042, "42818"}, // 722 cp_already_crypted
{335545043, "39000"}, // 723 decrypt_error
{335545043, "XX000"}, // 723 decrypt_error
{335545044, "39000"}, // 724 no_providers
{335545045, "42818"}, // 725 null_spb
{335545046, "42000"}, // 726 max_args_exceeded
@ -808,6 +808,8 @@ static const struct {
{335545105, "08004"}, // 785 map_down
{335545106, "08006"}, // 786 login_error
{335545107, "08006"}, // 787 already_opened
{335545108, "08006"}, // 788 bad_crypt_key
{335545109, "XX000"}, // 789 encrypt_error
{335740929, "00000"}, // 1 gfix_db_name
{335740930, "00000"}, // 2 gfix_invalid_sw
{335740932, "00000"}, // 4 gfix_incmp_sw

View File

@ -48,6 +48,8 @@
#include "../common/isc_proto.h"
#include "../common/classes/GetPlugins.h"
#include "../common/classes/RefMutex.h"
#include "../common/classes/ClumpletWriter.h"
#include "../common/sha.h"
using namespace Firebird;
@ -83,23 +85,16 @@ namespace {
return header;
}
// This routine is looking for a clumplet on header page but is not ready to handle continuation
// Fortunately, modern pages of size 4k and bigger can contain everything on one page.
bool searchEntry(UCHAR type, UCHAR& out_len, const UCHAR* &entry)
// This routine is getting clumplets from header page but is not ready to handle continuation
// Fortunately, modern pages of size 4k and bigger can fit everything on one page.
void getClumplets(ClumpletWriter& writer)
{
const UCHAR* end = ((const UCHAR*) header) + header->hdr_page_size;
for (const UCHAR* p = header->hdr_data; (p < end - 2) && (*p != Ods::HDR_end); p += 2u + p[1])
{
if (*p == type)
{
out_len = p[1];
entry = p + 2;
if (entry + out_len > end)
out_len = end - entry;
return true;
}
}
return false;
const UCHAR* p = header->hdr_data;
const UCHAR* const end = reinterpret_cast<const UCHAR*>(header) + header->hdr_page_size;
while ((p < end - 2) && (*p != Ods::HDR_end))
p += 2u + p[1];
writer.reset(header->hdr_data, p - header->hdr_data);
}
private:
@ -112,7 +107,8 @@ namespace {
public:
CchHdr(Jrd::thread_db* p_tdbb, USHORT lockType)
: window(Jrd::HEADER_PAGE_NUMBER),
tdbb(p_tdbb)
tdbb(p_tdbb),
wrtFlag(false)
{
void* h = CCH_FETCH(tdbb, &window, lockType, pag_header);
if (!h)
@ -124,10 +120,30 @@ namespace {
Ods::header_page* write()
{
CCH_MARK_MUST_WRITE(tdbb, &window);
if (!wrtFlag)
{
CCH_MARK_MUST_WRITE(tdbb, &window);
wrtFlag = true;
}
return const_cast<Ods::header_page*>(operator->());
}
void setClumplets(const ClumpletWriter& writer)
{
Ods::header_page* hdr = write();
UCHAR* const to = hdr->hdr_data;
UCHAR* const end = reinterpret_cast<UCHAR*>(hdr) + hdr->hdr_page_size;
const unsigned limit = (end - to) - 1;
const unsigned length = writer.getBufferLength();
fb_assert(length <= limit);
if (length > limit)
(Arg::Gds(isc_random) << "HDR page clumplets overflow").raise();
memcpy(to, writer.getBuffer(), length);
to[length] = Ods::HDR_end;
}
~CchHdr()
{
CCH_RELEASE(tdbb, &window);
@ -136,6 +152,7 @@ namespace {
private:
Jrd::WIN window;
Jrd::thread_db* tdbb;
bool wrtFlag;
};
class PhysHdr : public Header
@ -222,6 +239,7 @@ namespace Jrd {
CryptoManager::CryptoManager(thread_db* tdbb)
: PermanentStorage(*tdbb->getDatabase()->dbb_permanent),
sync(this),
keyName(getPool()),
keyHolderPlugins(getPool()),
cryptThreadId(0),
cryptPlugin(NULL),
@ -310,9 +328,27 @@ namespace Jrd {
PhysHdr hdr(tdbb);
crypt = hdr->hdr_flags & Ods::hdr_encrypted;
process = hdr->hdr_flags & Ods::hdr_crypt_process;
if (crypt || process)
if ((crypt || process) && !cryptPlugin)
{
ClumpletWriter hc(ClumpletWriter::UnTagged, hdr->hdr_page_size);
hdr.getClumplets(hc);
if (hc.find(Ods::HDR_crypt_key))
hc.getString(keyName);
else
keyName = "";
loadPlugin(hdr->hdr_crypt_plugin);
string valid;
calcValidation(valid);
if (hc.find(Ods::HDR_crypt_hash))
{
string hash;
hc.getString(hash);
if (hash != valid)
(Arg::Gds(isc_bad_crypt_key) << keyName).raise();
}
}
}
@ -337,12 +373,13 @@ namespace Jrd {
// do not assign cryptPlugin directly before key init complete
IDbCryptPlugin* p = cryptControl.plugin();
keyHolderPlugins.init(p);
keyHolderPlugins.init(p, keyName.c_str());
cryptPlugin = p;
cryptPlugin->addRef();
}
void CryptoManager::prepareChangeCryptState(thread_db* tdbb, const Firebird::MetaName& plugName)
void CryptoManager::prepareChangeCryptState(thread_db* tdbb, const MetaName& plugName,
const MetaName& key)
{
if (plugName.length() > MAX_PLUGIN_NAME_LEN)
{
@ -371,12 +408,28 @@ namespace Jrd {
if (cryptPlugin)
(Arg::Gds(isc_cp_already_crypted)).raise();
keyName = key;
loadPlugin(plugName.c_str());
}
}
}
void CryptoManager::changeCryptState(thread_db* tdbb, const Firebird::string& plugName)
void CryptoManager::calcValidation(string& valid)
{
// crypt verifier
const char* sample = "0123456789ABCDEF";
char result[16];
FbLocalStatus sv;
cryptPlugin->encrypt(&sv, sizeof(result), sample, result);
if (sv->getState() & IStatus::STATE_ERRORS)
Arg::StatusVector(&sv).raise();
// calculate it's hash
const string verifier(result, sizeof(result));
Sha1::hashBased64(valid, verifier);
}
void CryptoManager::changeCryptState(thread_db* tdbb, const string& plugName)
{
if (plugName.length() > 31)
{
@ -423,17 +476,31 @@ namespace Jrd {
// Write modified header page
Ods::header_page* header = hdr.write();
ClumpletWriter hc(ClumpletWriter::UnTagged, header->hdr_page_size);
hdr.getClumplets(hc);
if (crypt)
{
header->hdr_flags |= Ods::hdr_encrypted;
plugName.copyTo(header->hdr_crypt_plugin, sizeof(header->hdr_crypt_plugin));
string hash;
calcValidation(hash);
hc.insertString(Ods::HDR_crypt_hash, hash);
}
else
{
header->hdr_flags &= ~Ods::hdr_encrypted;
hc.deleteWithTag(Ods::HDR_crypt_hash);
}
// Set hdr_crypt_page for crypt thread
if (crypt && keyName.hasData())
hc.insertString(Ods::HDR_crypt_key, keyName);
else
hc.deleteWithTag(Ods::HDR_crypt_key);
hdr.setClumplets(hc);
// Setup hdr_crypt_page for crypt thread
header->hdr_crypt_page = 1;
header->hdr_flags |= Ods::hdr_crypt_process;
process = true;
@ -848,8 +915,7 @@ namespace Jrd {
fb_assert(cryptPlugin);
if (!cryptPlugin)
{
(Arg::Gds(isc_decrypt_error) <<
Arg::Gds(isc_random) << "Missing crypt plugin").copyTo(sv);
Arg::Gds(isc_encrypt_error).copyTo(sv);
return FAILED_CRYPT;
}
@ -950,7 +1016,7 @@ namespace Jrd {
{
IKeyHolderPlugin* keyPlugin = keyControl.plugin();
FbLocalStatus st;
if (keyPlugin->keyCallback(&st, att->att_crypt_callback) == 1) //// FIXME: 1 ???
if (keyPlugin->keyCallback(&st, att->att_crypt_callback) > 0)
{
// holder accepted attachment's key
HolderAttachments* ha = NULL;
@ -992,7 +1058,7 @@ namespace Jrd {
}
}
void CryptoManager::KeyHolderPlugins::init(IDbCryptPlugin* crypt)
void CryptoManager::KeyHolderPlugins::init(IDbCryptPlugin* crypt, const char* keyName)
{
MutexLockGuard g(holdersMutex, FB_FUNCTION);
@ -1005,7 +1071,7 @@ namespace Jrd {
}
FbLocalStatus st;
crypt->setKey(&st, length, vector);
crypt->setKey(&st, keyName, length, vector);
st.check();
}

View File

@ -35,6 +35,7 @@
#include "../common/classes/fb_string.h"
#include "../common/classes/objects_array.h"
#include "../common/classes/condition.h"
#include "../common/classes/MetaName.h"
#include "../common/ThreadStart.h"
#include "../jrd/ods.h"
#include "../jrd/status.h"
@ -259,7 +260,8 @@ public:
void shutdown(thread_db* tdbb);
void prepareChangeCryptState(thread_db* tdbb, const Firebird::MetaName& plugName);
void prepareChangeCryptState(thread_db* tdbb, const Firebird::MetaName& plugName,
const Firebird::MetaName& key);
void changeCryptState(thread_db* tdbb, const Firebird::string& plugName);
void attach(thread_db* tdbb, Attachment* att);
void detach(thread_db* tdbb, Attachment* att);
@ -334,7 +336,7 @@ private:
void attach(Attachment* att, Config* config);
void detach(Attachment* att);
void init(Firebird::IDbCryptPlugin* crypt);
void init(Firebird::IDbCryptPlugin* crypt, const char* keyName);
private:
Firebird::Mutex holdersMutex;
@ -351,12 +353,14 @@ private:
void loadPlugin(const char* pluginName);
ULONG getLastPage(thread_db* tdbb);
void writeDbHeader(thread_db* tdbb, ULONG runpage);
void calcValidation(Firebird::string& valid);
void lockAndReadHeader(thread_db* tdbb, unsigned flags = 0);
static const unsigned CRYPT_HDR_INIT = 0x01;
static const unsigned CRYPT_HDR_NOWAIT = 0x02;
BarSync sync;
Firebird::MetaName keyName;
ULONG currentPage;
Firebird::Mutex pluginLoadMtx, cryptThreadMtx;
KeyHolderPlugins keyHolderPlugins;

View File

@ -429,7 +429,9 @@ const UCHAR HDR_sweep_interval = 4; // Transactions between sweeps
const UCHAR HDR_password_file_key = 5; // Key to compare to password db
const UCHAR HDR_difference_file = 6; // Delta file that is used during backup lock
const UCHAR HDR_backup_guid = 7; // UID generated on each switch into backup mode
const UCHAR HDR_max = 8; // Maximum HDR_clump value
const UCHAR HDR_crypt_key = 8; // Name of a key used to crypt database
const UCHAR HDR_crypt_hash = 9; // Validator of key correctness
const UCHAR HDR_max = 10; // Maximum HDR_clump value
// Header page flags

View File

@ -1,7 +1,7 @@
/* MAX_NUMBER is the next number to be used, always one more than the highest message number. */
set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?);
--
('2015-12-30 17:52:25', 'JRD', 0, 788)
('2016-02-03 14:51:49', 'JRD', 0, 790)
('2015-03-17 18:33:00', 'QLI', 1, 533)
('2015-01-07 18:01:51', 'GFIX', 3, 134)
('1996-11-07 13:39:40', 'GPRE', 4, 1)

View File

@ -895,6 +895,8 @@ Data source : @4', NULL, NULL)
('map_down', NULL, 'Mapping.cpp', NULL, 0, 785, NULL, 'Some database(s) were shutdown when trying to read mapping data', NULL, NULL);
('login_error', NULL, 'server.cpp', NULL, 0, 786, NULL, 'Error occurred during login, please check server firebird.log for details', NULL, NULL);
('already_opened', 'lockDatabaseFile', 'unix.cpp', NULL, 0, 787, NULL, 'Database already opened with engine instance, incompatible with current', NULL, NULL);
('bad_crypt_key', NULL, 'CryptoManager.cpp', NULL, 0, 788, NULL, 'Invalid crypt key @1', NULL, NULL);
('encrypt_error', NULL, 'CryptoManager.cpp', NULL, 0, 789, NULL, 'Page requires encyption but crypt plugin is missing', NULL, NULL);
-- QLI
(NULL, NULL, NULL, NULL, 1, 0, NULL, 'expected type', NULL, NULL);
(NULL, NULL, NULL, NULL, 1, 1, NULL, 'bad block type', NULL, NULL);

View File

@ -729,7 +729,7 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA
(-104, '22', '001', 0, 720, 'cp_name_too_long', NULL, NULL)
(-901, '42', '818', 0, 721, 'cp_process_active', NULL, NULL)
(-901, '42', '818', 0, 722, 'cp_already_crypted', NULL, NULL)
(-902, '39', '000', 0, 723, 'decrypt_error', NULL, NULL)
(-902, 'XX', '000', 0, 723, 'decrypt_error', NULL, NULL)
(-902, '39', '000', 0, 724, 'no_providers', NULL, NULL)
(-104, '42', '818', 0, 725, 'null_spb', NULL, NULL)
(-833, '42', '000', 0, 726, 'max_args_exceeded', NULL, NULL)
@ -794,6 +794,8 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA
(-901, '08', '004', 0, 785, 'map_down', NULL, NULL)
(-902, '08', '006', 0, 786, 'login_error', NULL, NULL)
(-902, '08', '006', 0, 787, 'already_opened', NULL, NULL)
(-902, '08', '006', 0, 788, 'bad_crypt_key', NULL, NULL)
(-901, 'XX', '000', 0, 789, 'encrypt_error', NULL, NULL)
-- GFIX
(-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL)
(-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL)

View File

@ -281,6 +281,14 @@ void PPG_print_header(const header_page* header, ULONG page,
break;
}
case HDR_crypt_key:
uSvc->printf(false, "\tEncryption key name:\t%*.*s\n", p[1], p[1], p + 2);
break;
case HDR_crypt_hash:
uSvc->printf(false, "\tKey hash:\t%*.*s\n", p[1], p[1], p + 2);
break;
default:
if (*p > HDR_max)
uSvc->printf(false, "\tUnrecognized option %d, length %d\n", p[0], p[1]);